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