1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.wifi;
18 
19 import static com.android.server.wifi.ConnectToNetworkNotificationBuilder.ACTION_CONNECT_TO_NETWORK;
20 import static com.android.server.wifi.ConnectToNetworkNotificationBuilder.ACTION_PICK_WIFI_NETWORK;
21 import static com.android.server.wifi.ConnectToNetworkNotificationBuilder.ACTION_PICK_WIFI_NETWORK_AFTER_CONNECT_FAILURE;
22 import static com.android.server.wifi.ConnectToNetworkNotificationBuilder.ACTION_USER_DISMISSED_NOTIFICATION;
23 import static com.android.server.wifi.ConnectToNetworkNotificationBuilder.AVAILABLE_NETWORK_NOTIFIER_TAG;
24 
25 import android.annotation.IntDef;
26 import android.annotation.NonNull;
27 import android.annotation.Nullable;
28 import android.app.Notification;
29 import android.content.BroadcastReceiver;
30 import android.content.Context;
31 import android.content.Intent;
32 import android.content.IntentFilter;
33 import android.database.ContentObserver;
34 import android.net.wifi.IActionListener;
35 import android.net.wifi.ScanResult;
36 import android.net.wifi.WifiConfiguration;
37 import android.net.wifi.WifiContext;
38 import android.net.wifi.util.ScanResultUtil;
39 import android.os.Handler;
40 import android.os.Looper;
41 import android.os.Process;
42 import android.os.UserHandle;
43 import android.os.UserManager;
44 import android.provider.Settings;
45 import android.text.TextUtils;
46 import android.util.ArraySet;
47 import android.util.Log;
48 
49 import com.android.internal.annotations.VisibleForTesting;
50 import com.android.modules.utils.build.SdkLevel;
51 import com.android.server.wifi.proto.nano.WifiMetricsProto.ConnectToNetworkNotificationAndActionCount;
52 import com.android.server.wifi.util.ActionListenerWrapper;
53 import com.android.server.wifi.util.WifiPermissionsUtil;
54 
55 import java.io.FileDescriptor;
56 import java.io.PrintWriter;
57 import java.lang.annotation.Retention;
58 import java.lang.annotation.RetentionPolicy;
59 import java.util.List;
60 import java.util.Set;
61 
62 /**
63  * Base class for all network notifiers (e.g. OpenNetworkNotifier).
64  *
65  * NOTE: These API's are not thread safe and should only be used from WifiCoreThread.
66  */
67 public class AvailableNetworkNotifier {
68 
69     /** Time in milliseconds to display the Connecting notification. */
70     private static final int TIME_TO_SHOW_CONNECTING_MILLIS = 10000;
71 
72     /** Time in milliseconds to display the Connected notification. */
73     private static final int TIME_TO_SHOW_CONNECTED_MILLIS = 5000;
74 
75     /** Time in milliseconds to display the Failed To Connect notification. */
76     private static final int TIME_TO_SHOW_FAILED_MILLIS = 5000;
77 
78     /** The state of the notification */
79     @IntDef({
80             STATE_NO_NOTIFICATION,
81             STATE_SHOWING_RECOMMENDATION_NOTIFICATION,
82             STATE_CONNECTING_IN_NOTIFICATION,
83             STATE_CONNECTED_NOTIFICATION,
84             STATE_CONNECT_FAILED_NOTIFICATION
85     })
86     @Retention(RetentionPolicy.SOURCE)
87     private @interface State {}
88 
89     /** No recommendation is made and no notifications are shown. */
90     private static final int STATE_NO_NOTIFICATION = 0;
91     /** The initial notification recommending a network to connect to is shown. */
92     @VisibleForTesting
93     static final int STATE_SHOWING_RECOMMENDATION_NOTIFICATION = 1;
94     /** The notification of status of connecting to the recommended network is shown. */
95     private static final int STATE_CONNECTING_IN_NOTIFICATION = 2;
96     /** The notification that the connection to the recommended network was successful is shown. */
97     private static final int STATE_CONNECTED_NOTIFICATION = 3;
98     /** The notification to show that connection to the recommended network failed is shown. */
99     private static final int STATE_CONNECT_FAILED_NOTIFICATION = 4;
100 
101     /** Current state of the notification. */
102     @VisibleForTesting
103     @State int mState = STATE_NO_NOTIFICATION;
104 
105     /**
106      * The {@link Clock#getWallClockMillis()} must be at least this value for us
107      * to show the notification again.
108      */
109     private long mNotificationRepeatTime;
110     /**
111      * When a notification is shown, we wait this amount before possibly showing it again.
112      */
113     private final long mNotificationRepeatDelay;
114     /** Default repeat delay in seconds. */
115     @VisibleForTesting
116     static final int DEFAULT_REPEAT_DELAY_SEC = 900;
117 
118     /** Whether the user has set the setting to show the 'available networks' notification. */
119     private boolean mSettingEnabled;
120     /** Whether the screen is on or not. */
121     private boolean mScreenOn;
122 
123     /** List of SSIDs blocklisted from recommendation. */
124     private final Set<String> mBlocklistedSsids = new ArraySet<>();
125 
126     private final WifiContext mContext;
127     private final Handler mHandler;
128     private final FrameworkFacade mFrameworkFacade;
129     private final WifiMetrics mWifiMetrics;
130     private final Clock mClock;
131     private final WifiConfigManager mConfigManager;
132     private final ConnectHelper mConnectHelper;
133     private final ConnectToNetworkNotificationBuilder mNotificationBuilder;
134     private final MakeBeforeBreakManager mMakeBeforeBreakManager;
135     private final WifiNotificationManager mWifiNotificationManager;
136     private final WifiPermissionsUtil mWifiPermissionsUtil;
137 
138     @VisibleForTesting
139     ScanResult mRecommendedNetwork;
140 
141     /** Tag used for logs and metrics */
142     private final String mTag;
143     /** Identifier of the {@link SsidSetStoreData}. */
144     private final String mStoreDataIdentifier;
145     /** Identifier for the settings toggle, used for registering ContentObserver */
146     private final String mToggleSettingsName;
147 
148     /** System wide identifier for notification in Notification Manager */
149     private final int mSystemMessageNotificationId;
150 
151     /**
152      * The nominator id for this class, from
153      * {@link com.android.server.wifi.proto.nano.WifiMetricsProto.ConnectionEvent.
154      * ConnectionNominator}
155      */
156     private final int mNominatorId;
157 
AvailableNetworkNotifier( String tag, String storeDataIdentifier, String toggleSettingsName, int notificationIdentifier, int nominatorId, WifiContext context, Looper looper, FrameworkFacade framework, Clock clock, WifiMetrics wifiMetrics, WifiConfigManager wifiConfigManager, WifiConfigStore wifiConfigStore, ConnectHelper connectHelper, ConnectToNetworkNotificationBuilder connectToNetworkNotificationBuilder, MakeBeforeBreakManager makeBeforeBreakManager, WifiNotificationManager wifiNotificationManager, WifiPermissionsUtil wifiPermissionsUtil)158     public AvailableNetworkNotifier(
159             String tag,
160             String storeDataIdentifier,
161             String toggleSettingsName,
162             int notificationIdentifier,
163             int nominatorId,
164             WifiContext context,
165             Looper looper,
166             FrameworkFacade framework,
167             Clock clock,
168             WifiMetrics wifiMetrics,
169             WifiConfigManager wifiConfigManager,
170             WifiConfigStore wifiConfigStore,
171             ConnectHelper connectHelper,
172             ConnectToNetworkNotificationBuilder connectToNetworkNotificationBuilder,
173             MakeBeforeBreakManager makeBeforeBreakManager,
174             WifiNotificationManager wifiNotificationManager,
175             WifiPermissionsUtil wifiPermissionsUtil) {
176         mTag = tag;
177         mStoreDataIdentifier = storeDataIdentifier;
178         mToggleSettingsName = toggleSettingsName;
179         mSystemMessageNotificationId = notificationIdentifier;
180         mNominatorId = nominatorId;
181         mContext = context;
182         mHandler = new Handler(looper);
183         mFrameworkFacade = framework;
184         mWifiMetrics = wifiMetrics;
185         mClock = clock;
186         mConfigManager = wifiConfigManager;
187         mConnectHelper = connectHelper;
188         mNotificationBuilder = connectToNetworkNotificationBuilder;
189         mMakeBeforeBreakManager = makeBeforeBreakManager;
190         mWifiNotificationManager = wifiNotificationManager;
191         mWifiPermissionsUtil = wifiPermissionsUtil;
192         mScreenOn = false;
193         wifiConfigStore.registerStoreData(new SsidSetStoreData(mStoreDataIdentifier,
194                 new AvailableNetworkNotifierStoreData()));
195 
196         // Setting is in seconds
197         mNotificationRepeatDelay = mFrameworkFacade.getIntegerSetting(context,
198                 Settings.Global.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY,
199                 DEFAULT_REPEAT_DELAY_SEC) * 1000L;
200         NotificationEnabledSettingObserver settingObserver = new NotificationEnabledSettingObserver(
201                 mHandler);
202         settingObserver.register();
203 
204         IntentFilter filter = new IntentFilter();
205         filter.addAction(ACTION_USER_DISMISSED_NOTIFICATION);
206         filter.addAction(ACTION_CONNECT_TO_NETWORK);
207         filter.addAction(ACTION_PICK_WIFI_NETWORK);
208         filter.addAction(ACTION_PICK_WIFI_NETWORK_AFTER_CONNECT_FAILURE);
209         mContext.registerReceiver(
210                 mBroadcastReceiver, filter, null /* broadcastPermission */, mHandler);
211     }
212 
213     private final BroadcastReceiver mBroadcastReceiver =
214             new BroadcastReceiver() {
215                 @Override
216                 public void onReceive(Context context, Intent intent) {
217                     if (!TextUtils.equals(mTag,
218                             intent.getStringExtra(AVAILABLE_NETWORK_NOTIFIER_TAG))) {
219                         return;
220                     }
221                     switch (intent.getAction()) {
222                         case ACTION_USER_DISMISSED_NOTIFICATION:
223                             handleUserDismissedAction();
224                             break;
225                         case ACTION_CONNECT_TO_NETWORK:
226                             handleConnectToNetworkAction();
227                             break;
228                         case ACTION_PICK_WIFI_NETWORK:
229                             handleSeeAllNetworksAction();
230                             break;
231                         case ACTION_PICK_WIFI_NETWORK_AFTER_CONNECT_FAILURE:
232                             handlePickWifiNetworkAfterConnectFailure();
233                             break;
234                         default:
235                             Log.e(mTag, "Unknown action " + intent.getAction());
236                     }
237                 }
238             };
239 
240     private final class ConnectActionListener extends IActionListener.Stub {
241         @Override
onSuccess()242         public void onSuccess() {
243             // Success here means that an attempt to connect to the network has been initiated.
244             // Successful connection updates are received via the
245             // WifiConnectivityManager#handleConnectionStateChanged() callback.
246         }
247 
248         @Override
onFailure(int reason)249         public void onFailure(int reason) {
250             handleConnectionAttemptFailedToSend();
251         }
252     }
253 
254     /**
255      * Clears the pending notification. This is called by {@link WifiConnectivityManager} on stop.
256      *
257      * @param resetRepeatTime resets the time delay for repeated notification if true.
258      */
clearPendingNotification(boolean resetRepeatTime)259     public void clearPendingNotification(boolean resetRepeatTime) {
260         if (resetRepeatTime) {
261             mNotificationRepeatTime = 0;
262         }
263 
264         if (mState != STATE_NO_NOTIFICATION) {
265             mWifiNotificationManager.cancel(mSystemMessageNotificationId);
266             Log.i(mTag, "notification canceled");
267 
268             if (mRecommendedNetwork != null) {
269                 Log.d(mTag, "Notification with state="
270                         + mState
271                         + " was cleared for recommended network: "
272                         + "\"" + mRecommendedNetwork.SSID + "\"");
273             }
274             mState = STATE_NO_NOTIFICATION;
275             mRecommendedNetwork = null;
276         }
277     }
278 
isSettingEnabled()279     public boolean isSettingEnabled() {
280         return mSettingEnabled;
281     }
282 
isControllerEnabled()283     private boolean isControllerEnabled() {
284         UserManager userManager = mContext.getSystemService(UserManager.class);
285         UserHandle currentUser = UserHandle.of(mWifiPermissionsUtil.getCurrentUser());
286         return mSettingEnabled
287                 && !userManager.hasUserRestrictionForUser(
288                         UserManager.DISALLOW_CONFIG_WIFI, currentUser)
289                 && !(SdkLevel.isAtLeastT() && userManager.hasUserRestrictionForUser(
290                         UserManager.DISALLOW_ADD_WIFI_CONFIG, currentUser));
291     }
292 
293     /**
294      * If there are available networks, attempt to post a network notification.
295      *
296      * @param availableNetworks Available networks to choose from and possibly show notification
297      */
handleScanResults(@onNull List<ScanDetail> availableNetworks)298     public void handleScanResults(@NonNull List<ScanDetail> availableNetworks) {
299         if (!isControllerEnabled()) {
300             clearPendingNotification(true /* resetRepeatTime */);
301             return;
302         }
303         if (availableNetworks.isEmpty() && mState == STATE_SHOWING_RECOMMENDATION_NOTIFICATION) {
304             clearPendingNotification(false /* resetRepeatTime */);
305             return;
306         }
307 
308         // Not enough time has passed to show a recommendation notification again
309         if (mState == STATE_NO_NOTIFICATION
310                 && mClock.getWallClockMillis() < mNotificationRepeatTime) {
311             return;
312         }
313 
314         // Do nothing when the screen is off and no notification is showing.
315         if (mState == STATE_NO_NOTIFICATION && !mScreenOn) {
316             return;
317         }
318 
319         // Only show a new or update an existing recommendation notification.
320         if (mState == STATE_NO_NOTIFICATION
321                 || mState == STATE_SHOWING_RECOMMENDATION_NOTIFICATION) {
322             ScanResult recommendation =
323                     recommendNetwork(availableNetworks);
324 
325             if (recommendation != null) {
326                 postInitialNotification(recommendation);
327             } else {
328                 clearPendingNotification(false /* resetRepeatTime */);
329             }
330         }
331     }
332 
333     /**
334      * Recommends a network to connect to from a list of available networks, while ignoring the
335      * SSIDs in the blocklist.
336      *
337      * @param networks List of networks to select from
338      */
recommendNetwork(@onNull List<ScanDetail> networks)339     public ScanResult recommendNetwork(@NonNull List<ScanDetail> networks) {
340         ScanResult result = null;
341         int highestRssi = Integer.MIN_VALUE;
342         for (ScanDetail scanDetail : networks) {
343             ScanResult scanResult = scanDetail.getScanResult();
344 
345             if (scanResult.level > highestRssi) {
346                 result = scanResult;
347                 highestRssi = scanResult.level;
348             }
349         }
350 
351         if (result != null && mBlocklistedSsids.contains(result.SSID)) {
352             result = null;
353         }
354         return result;
355     }
356 
357     /** Handles screen state changes. */
handleScreenStateChanged(boolean screenOn)358     public void handleScreenStateChanged(boolean screenOn) {
359         mScreenOn = screenOn;
360     }
361 
362     /**
363      * Called by {@link WifiConnectivityManager} when Wi-Fi is connected. If the notification
364      * was in the connecting state, update the notification to show that it has connected to the
365      * recommended network.
366      *
367      * @param ssid The connected network's ssid
368      */
handleWifiConnected(String ssid)369     public void handleWifiConnected(String ssid) {
370         removeNetworkFromBlocklist(ssid);
371         if (mState != STATE_CONNECTING_IN_NOTIFICATION) {
372             clearPendingNotification(true /* resetRepeatTime */);
373             return;
374         }
375 
376         postNotification(mNotificationBuilder.createNetworkConnectedNotification(mTag,
377                 mRecommendedNetwork));
378 
379         Log.d(mTag, "User connected to recommended network: "
380                 + "\"" + mRecommendedNetwork.SSID + "\"");
381         mWifiMetrics.incrementConnectToNetworkNotification(mTag,
382                 ConnectToNetworkNotificationAndActionCount.NOTIFICATION_CONNECTED_TO_NETWORK);
383         mState = STATE_CONNECTED_NOTIFICATION;
384         mHandler.postDelayed(
385                 () -> {
386                     if (mState == STATE_CONNECTED_NOTIFICATION) {
387                         clearPendingNotification(true /* resetRepeatTime */);
388                     }
389                 },
390                 TIME_TO_SHOW_CONNECTED_MILLIS);
391     }
392 
393     /**
394      * Handles when a Wi-Fi connection attempt failed.
395      */
handleConnectionFailure()396     public void handleConnectionFailure() {
397         if (mState != STATE_CONNECTING_IN_NOTIFICATION) {
398             return;
399         }
400         postNotification(mNotificationBuilder.createNetworkFailedNotification(mTag));
401 
402         Log.d(mTag, "User failed to connect to recommended network: "
403                 + "\"" + mRecommendedNetwork.SSID + "\"");
404         mWifiMetrics.incrementConnectToNetworkNotification(mTag,
405                 ConnectToNetworkNotificationAndActionCount.NOTIFICATION_FAILED_TO_CONNECT);
406         mState = STATE_CONNECT_FAILED_NOTIFICATION;
407         mHandler.postDelayed(
408                 () -> {
409                     if (mState == STATE_CONNECT_FAILED_NOTIFICATION) {
410                         clearPendingNotification(false /* resetRepeatTime */);
411                     }
412                 },
413                 TIME_TO_SHOW_FAILED_MILLIS);
414     }
415 
postInitialNotification(ScanResult recommendedNetwork)416     private void postInitialNotification(ScanResult recommendedNetwork) {
417         if (mRecommendedNetwork != null
418                 && TextUtils.equals(mRecommendedNetwork.SSID, recommendedNetwork.SSID)) {
419             return;
420         }
421 
422         postNotification(mNotificationBuilder.createConnectToAvailableNetworkNotification(mTag,
423                 recommendedNetwork));
424 
425         if (mState == STATE_NO_NOTIFICATION) {
426             mWifiMetrics.incrementConnectToNetworkNotification(mTag,
427                     ConnectToNetworkNotificationAndActionCount.NOTIFICATION_RECOMMEND_NETWORK);
428         } else {
429             mWifiMetrics.incrementNumNetworkRecommendationUpdates(mTag);
430         }
431         mState = STATE_SHOWING_RECOMMENDATION_NOTIFICATION;
432         mRecommendedNetwork = recommendedNetwork;
433         mNotificationRepeatTime = mClock.getWallClockMillis() + mNotificationRepeatDelay;
434     }
435 
postNotification(Notification notification)436     private void postNotification(Notification notification) {
437         mWifiNotificationManager.notify(mSystemMessageNotificationId, notification);
438         Log.i(mTag, "notification send");
439     }
440 
handleConnectToNetworkAction()441     private void handleConnectToNetworkAction() {
442         mWifiMetrics.incrementConnectToNetworkNotificationAction(mTag, mState,
443                 ConnectToNetworkNotificationAndActionCount.ACTION_CONNECT_TO_NETWORK);
444         if (mState != STATE_SHOWING_RECOMMENDATION_NOTIFICATION) {
445             return;
446         }
447         postNotification(mNotificationBuilder.createNetworkConnectingNotification(mTag,
448                 mRecommendedNetwork));
449         mWifiMetrics.incrementConnectToNetworkNotification(mTag,
450                 ConnectToNetworkNotificationAndActionCount.NOTIFICATION_CONNECTING_TO_NETWORK);
451 
452         Log.d(mTag,
453                 "User initiated connection to recommended network: "
454                         + "\"" + mRecommendedNetwork.SSID + "\"");
455         WifiConfiguration network = createRecommendedNetworkConfig(mRecommendedNetwork);
456         if (null == network) {
457             Log.e(mTag, "Cannot create the network from the scan result.");
458             return;
459         }
460 
461         NetworkUpdateResult result = mConfigManager.addOrUpdateNetwork(network, Process.WIFI_UID);
462         if (result.isSuccess()) {
463             mWifiMetrics.setNominatorForNetwork(result.getNetworkId(), mNominatorId);
464             ConnectActionListener listener = new ConnectActionListener();
465             mMakeBeforeBreakManager.stopAllSecondaryTransientClientModeManagers(() ->
466                     mConnectHelper.connectToNetwork(
467                             // only keep netId, discard other fields
468                             new NetworkUpdateResult(result.getNetworkId()),
469                             new ActionListenerWrapper(listener),
470                             Process.SYSTEM_UID, mContext.getOpPackageName(), null));
471             addNetworkToBlocklist(mRecommendedNetwork.SSID);
472         }
473 
474         mState = STATE_CONNECTING_IN_NOTIFICATION;
475         mHandler.postDelayed(
476                 () -> {
477                     if (mState == STATE_CONNECTING_IN_NOTIFICATION) {
478                         handleConnectionFailure();
479                     }
480                 },
481                 TIME_TO_SHOW_CONNECTING_MILLIS);
482     }
483 
addNetworkToBlocklist(String ssid)484     private void addNetworkToBlocklist(String ssid) {
485         mBlocklistedSsids.add(ssid);
486         mWifiMetrics.setNetworkRecommenderBlocklistSize(mTag, mBlocklistedSsids.size());
487         mConfigManager.saveToStore();
488         Log.d(mTag, "Network is added to the network notification blocklist: "
489                 + "\"" + ssid + "\"");
490     }
491 
removeNetworkFromBlocklist(String ssid)492     private void removeNetworkFromBlocklist(String ssid) {
493         if (ssid == null) {
494             return;
495         }
496         if (!mBlocklistedSsids.remove(ssid)) {
497             return;
498         }
499         mWifiMetrics.setNetworkRecommenderBlocklistSize(mTag, mBlocklistedSsids.size());
500         mConfigManager.saveToStore();
501         Log.d(mTag, "Network is removed from the network notification blocklist: "
502                 + "\"" + ssid + "\"");
503     }
504 
createRecommendedNetworkConfig(ScanResult recommendedNetwork)505     @Nullable WifiConfiguration createRecommendedNetworkConfig(ScanResult recommendedNetwork) {
506         return ScanResultUtil.createNetworkFromScanResult(recommendedNetwork);
507     }
508 
handleSeeAllNetworksAction()509     private void handleSeeAllNetworksAction() {
510         mWifiMetrics.incrementConnectToNetworkNotificationAction(mTag, mState,
511                 ConnectToNetworkNotificationAndActionCount.ACTION_PICK_WIFI_NETWORK);
512         startWifiSettings();
513     }
514 
startWifiSettings()515     private void startWifiSettings() {
516         // Close notification drawer before opening the picker.
517         mContext.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
518         mContext.startActivityAsUser(
519                 new Intent(Settings.ACTION_WIFI_SETTINGS)
520                         .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK),
521                 UserHandle.CURRENT);
522         clearPendingNotification(false /* resetRepeatTime */);
523     }
524 
handleConnectionAttemptFailedToSend()525     private void handleConnectionAttemptFailedToSend() {
526         handleConnectionFailure();
527         mWifiMetrics.incrementNumNetworkConnectMessageFailedToSend(mTag);
528     }
529 
handlePickWifiNetworkAfterConnectFailure()530     private void handlePickWifiNetworkAfterConnectFailure() {
531         mWifiMetrics.incrementConnectToNetworkNotificationAction(mTag, mState,
532                 ConnectToNetworkNotificationAndActionCount
533                         .ACTION_PICK_WIFI_NETWORK_AFTER_CONNECT_FAILURE);
534         startWifiSettings();
535     }
536 
handleUserDismissedAction()537     private void handleUserDismissedAction() {
538         Log.d(mTag, "User dismissed notification with state=" + mState);
539         mWifiMetrics.incrementConnectToNetworkNotificationAction(mTag, mState,
540                 ConnectToNetworkNotificationAndActionCount.ACTION_USER_DISMISSED_NOTIFICATION);
541         if (mState == STATE_SHOWING_RECOMMENDATION_NOTIFICATION) {
542             // blocklist dismissed network
543             addNetworkToBlocklist(mRecommendedNetwork.SSID);
544         }
545         resetStateAndDelayNotification();
546     }
547 
resetStateAndDelayNotification()548     private void resetStateAndDelayNotification() {
549         mState = STATE_NO_NOTIFICATION;
550         mNotificationRepeatTime = System.currentTimeMillis() + mNotificationRepeatDelay;
551         mRecommendedNetwork = null;
552     }
553 
554     /** Dump this network notifier's state. */
dump(FileDescriptor fd, PrintWriter pw, String[] args)555     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
556         pw.println(mTag + ": ");
557         pw.println("mSettingEnabled " + mSettingEnabled);
558         pw.println("currentTime: " + mClock.getWallClockMillis());
559         pw.println("mNotificationRepeatTime: " + mNotificationRepeatTime);
560         pw.println("mState: " + mState);
561         pw.println("mBlocklistedSsids: " + mBlocklistedSsids.toString());
562     }
563 
564     private class AvailableNetworkNotifierStoreData implements SsidSetStoreData.DataSource {
565         @Override
getSsids()566         public Set<String> getSsids() {
567             return new ArraySet<>(mBlocklistedSsids);
568         }
569 
570         @Override
setSsids(Set<String> ssidList)571         public void setSsids(Set<String> ssidList) {
572             mBlocklistedSsids.addAll(ssidList);
573             mWifiMetrics.setNetworkRecommenderBlocklistSize(mTag, mBlocklistedSsids.size());
574         }
575     }
576 
577     private class NotificationEnabledSettingObserver extends ContentObserver {
NotificationEnabledSettingObserver(Handler handler)578         NotificationEnabledSettingObserver(Handler handler) {
579             super(handler);
580         }
581 
register()582         public void register() {
583             mFrameworkFacade.registerContentObserver(mContext,
584                     Settings.Global.getUriFor(mToggleSettingsName), true, this);
585             mSettingEnabled = getValue();
586         }
587 
588         @Override
onChange(boolean selfChange)589         public void onChange(boolean selfChange) {
590             super.onChange(selfChange);
591             mSettingEnabled = getValue();
592             clearPendingNotification(true /* resetRepeatTime */);
593         }
594 
getValue()595         private boolean getValue() {
596             boolean enabled =
597                     mFrameworkFacade.getIntegerSetting(mContext, mToggleSettingsName, 1) == 1;
598             mWifiMetrics.setIsWifiNetworksAvailableNotificationEnabled(mTag, enabled);
599             Log.d(mTag, "Settings toggle enabled=" + enabled);
600             return enabled;
601         }
602     }
603 }
604