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