• 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.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 
267             if (mRecommendedNetwork != null) {
268                 Log.d(mTag, "Notification with state="
269                         + mState
270                         + " was cleared for recommended network: "
271                         + "\"" + mRecommendedNetwork.SSID + "\"");
272             }
273             mState = STATE_NO_NOTIFICATION;
274             mRecommendedNetwork = null;
275         }
276     }
277 
isControllerEnabled()278     private boolean isControllerEnabled() {
279         UserManager userManager = mContext.getSystemService(UserManager.class);
280         UserHandle currentUser = UserHandle.of(mWifiPermissionsUtil.getCurrentUser());
281         return mSettingEnabled
282                 && !userManager.hasUserRestrictionForUser(
283                         UserManager.DISALLOW_CONFIG_WIFI, currentUser)
284                 && !(SdkLevel.isAtLeastT() && userManager.hasUserRestrictionForUser(
285                         UserManager.DISALLOW_ADD_WIFI_CONFIG, currentUser));
286     }
287 
288     /**
289      * If there are available networks, attempt to post a network notification.
290      *
291      * @param availableNetworks Available networks to choose from and possibly show notification
292      */
handleScanResults(@onNull List<ScanDetail> availableNetworks)293     public void handleScanResults(@NonNull List<ScanDetail> availableNetworks) {
294         if (!isControllerEnabled()) {
295             clearPendingNotification(true /* resetRepeatTime */);
296             return;
297         }
298         if (availableNetworks.isEmpty() && mState == STATE_SHOWING_RECOMMENDATION_NOTIFICATION) {
299             clearPendingNotification(false /* resetRepeatTime */);
300             return;
301         }
302 
303         // Not enough time has passed to show a recommendation notification again
304         if (mState == STATE_NO_NOTIFICATION
305                 && mClock.getWallClockMillis() < mNotificationRepeatTime) {
306             return;
307         }
308 
309         // Do nothing when the screen is off and no notification is showing.
310         if (mState == STATE_NO_NOTIFICATION && !mScreenOn) {
311             return;
312         }
313 
314         // Only show a new or update an existing recommendation notification.
315         if (mState == STATE_NO_NOTIFICATION
316                 || mState == STATE_SHOWING_RECOMMENDATION_NOTIFICATION) {
317             ScanResult recommendation =
318                     recommendNetwork(availableNetworks);
319 
320             if (recommendation != null) {
321                 postInitialNotification(recommendation);
322             } else {
323                 clearPendingNotification(false /* resetRepeatTime */);
324             }
325         }
326     }
327 
328     /**
329      * Recommends a network to connect to from a list of available networks, while ignoring the
330      * SSIDs in the blocklist.
331      *
332      * @param networks List of networks to select from
333      */
recommendNetwork(@onNull List<ScanDetail> networks)334     public ScanResult recommendNetwork(@NonNull List<ScanDetail> networks) {
335         ScanResult result = null;
336         int highestRssi = Integer.MIN_VALUE;
337         for (ScanDetail scanDetail : networks) {
338             ScanResult scanResult = scanDetail.getScanResult();
339 
340             if (scanResult.level > highestRssi) {
341                 result = scanResult;
342                 highestRssi = scanResult.level;
343             }
344         }
345 
346         if (result != null && mBlocklistedSsids.contains(result.SSID)) {
347             result = null;
348         }
349         return result;
350     }
351 
352     /** Handles screen state changes. */
handleScreenStateChanged(boolean screenOn)353     public void handleScreenStateChanged(boolean screenOn) {
354         mScreenOn = screenOn;
355     }
356 
357     /**
358      * Called by {@link WifiConnectivityManager} when Wi-Fi is connected. If the notification
359      * was in the connecting state, update the notification to show that it has connected to the
360      * recommended network.
361      *
362      * @param ssid The connected network's ssid
363      */
handleWifiConnected(String ssid)364     public void handleWifiConnected(String ssid) {
365         removeNetworkFromBlocklist(ssid);
366         if (mState != STATE_CONNECTING_IN_NOTIFICATION) {
367             clearPendingNotification(true /* resetRepeatTime */);
368             return;
369         }
370 
371         postNotification(mNotificationBuilder.createNetworkConnectedNotification(mTag,
372                 mRecommendedNetwork));
373 
374         Log.d(mTag, "User connected to recommended network: "
375                 + "\"" + mRecommendedNetwork.SSID + "\"");
376         mWifiMetrics.incrementConnectToNetworkNotification(mTag,
377                 ConnectToNetworkNotificationAndActionCount.NOTIFICATION_CONNECTED_TO_NETWORK);
378         mState = STATE_CONNECTED_NOTIFICATION;
379         mHandler.postDelayed(
380                 () -> {
381                     if (mState == STATE_CONNECTED_NOTIFICATION) {
382                         clearPendingNotification(true /* resetRepeatTime */);
383                     }
384                 },
385                 TIME_TO_SHOW_CONNECTED_MILLIS);
386     }
387 
388     /**
389      * Handles when a Wi-Fi connection attempt failed.
390      */
handleConnectionFailure()391     public void handleConnectionFailure() {
392         if (mState != STATE_CONNECTING_IN_NOTIFICATION) {
393             return;
394         }
395         postNotification(mNotificationBuilder.createNetworkFailedNotification(mTag));
396 
397         Log.d(mTag, "User failed to connect to recommended network: "
398                 + "\"" + mRecommendedNetwork.SSID + "\"");
399         mWifiMetrics.incrementConnectToNetworkNotification(mTag,
400                 ConnectToNetworkNotificationAndActionCount.NOTIFICATION_FAILED_TO_CONNECT);
401         mState = STATE_CONNECT_FAILED_NOTIFICATION;
402         mHandler.postDelayed(
403                 () -> {
404                     if (mState == STATE_CONNECT_FAILED_NOTIFICATION) {
405                         clearPendingNotification(false /* resetRepeatTime */);
406                     }
407                 },
408                 TIME_TO_SHOW_FAILED_MILLIS);
409     }
410 
postInitialNotification(ScanResult recommendedNetwork)411     private void postInitialNotification(ScanResult recommendedNetwork) {
412         if (mRecommendedNetwork != null
413                 && TextUtils.equals(mRecommendedNetwork.SSID, recommendedNetwork.SSID)) {
414             return;
415         }
416 
417         postNotification(mNotificationBuilder.createConnectToAvailableNetworkNotification(mTag,
418                 recommendedNetwork));
419 
420         if (mState == STATE_NO_NOTIFICATION) {
421             mWifiMetrics.incrementConnectToNetworkNotification(mTag,
422                     ConnectToNetworkNotificationAndActionCount.NOTIFICATION_RECOMMEND_NETWORK);
423         } else {
424             mWifiMetrics.incrementNumNetworkRecommendationUpdates(mTag);
425         }
426         mState = STATE_SHOWING_RECOMMENDATION_NOTIFICATION;
427         mRecommendedNetwork = recommendedNetwork;
428         mNotificationRepeatTime = mClock.getWallClockMillis() + mNotificationRepeatDelay;
429     }
430 
postNotification(Notification notification)431     private void postNotification(Notification notification) {
432         mWifiNotificationManager.notify(mSystemMessageNotificationId, notification);
433     }
434 
handleConnectToNetworkAction()435     private void handleConnectToNetworkAction() {
436         mWifiMetrics.incrementConnectToNetworkNotificationAction(mTag, mState,
437                 ConnectToNetworkNotificationAndActionCount.ACTION_CONNECT_TO_NETWORK);
438         if (mState != STATE_SHOWING_RECOMMENDATION_NOTIFICATION) {
439             return;
440         }
441         postNotification(mNotificationBuilder.createNetworkConnectingNotification(mTag,
442                 mRecommendedNetwork));
443         mWifiMetrics.incrementConnectToNetworkNotification(mTag,
444                 ConnectToNetworkNotificationAndActionCount.NOTIFICATION_CONNECTING_TO_NETWORK);
445 
446         Log.d(mTag,
447                 "User initiated connection to recommended network: "
448                         + "\"" + mRecommendedNetwork.SSID + "\"");
449         WifiConfiguration network = createRecommendedNetworkConfig(mRecommendedNetwork);
450         if (null == network) {
451             Log.e(mTag, "Cannot create the network from the scan result.");
452             return;
453         }
454 
455         NetworkUpdateResult result = mConfigManager.addOrUpdateNetwork(network, Process.WIFI_UID);
456         if (result.isSuccess()) {
457             mWifiMetrics.setNominatorForNetwork(result.getNetworkId(), mNominatorId);
458             ConnectActionListener listener = new ConnectActionListener();
459             mMakeBeforeBreakManager.stopAllSecondaryTransientClientModeManagers(() ->
460                     mConnectHelper.connectToNetwork(
461                             // only keep netId, discard other fields
462                             new NetworkUpdateResult(result.getNetworkId()),
463                             new ActionListenerWrapper(listener),
464                             Process.SYSTEM_UID, mContext.getOpPackageName()));
465             addNetworkToBlocklist(mRecommendedNetwork.SSID);
466         }
467 
468         mState = STATE_CONNECTING_IN_NOTIFICATION;
469         mHandler.postDelayed(
470                 () -> {
471                     if (mState == STATE_CONNECTING_IN_NOTIFICATION) {
472                         handleConnectionFailure();
473                     }
474                 },
475                 TIME_TO_SHOW_CONNECTING_MILLIS);
476     }
477 
addNetworkToBlocklist(String ssid)478     private void addNetworkToBlocklist(String ssid) {
479         mBlocklistedSsids.add(ssid);
480         mWifiMetrics.setNetworkRecommenderBlocklistSize(mTag, mBlocklistedSsids.size());
481         mConfigManager.saveToStore(false /* forceWrite */);
482         Log.d(mTag, "Network is added to the network notification blocklist: "
483                 + "\"" + ssid + "\"");
484     }
485 
removeNetworkFromBlocklist(String ssid)486     private void removeNetworkFromBlocklist(String ssid) {
487         if (ssid == null) {
488             return;
489         }
490         if (!mBlocklistedSsids.remove(ssid)) {
491             return;
492         }
493         mWifiMetrics.setNetworkRecommenderBlocklistSize(mTag, mBlocklistedSsids.size());
494         mConfigManager.saveToStore(false /* forceWrite */);
495         Log.d(mTag, "Network is removed from the network notification blocklist: "
496                 + "\"" + ssid + "\"");
497     }
498 
createRecommendedNetworkConfig(ScanResult recommendedNetwork)499     @Nullable WifiConfiguration createRecommendedNetworkConfig(ScanResult recommendedNetwork) {
500         return ScanResultUtil.createNetworkFromScanResult(recommendedNetwork);
501     }
502 
handleSeeAllNetworksAction()503     private void handleSeeAllNetworksAction() {
504         mWifiMetrics.incrementConnectToNetworkNotificationAction(mTag, mState,
505                 ConnectToNetworkNotificationAndActionCount.ACTION_PICK_WIFI_NETWORK);
506         startWifiSettings();
507     }
508 
startWifiSettings()509     private void startWifiSettings() {
510         // Close notification drawer before opening the picker.
511         mContext.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
512         mContext.startActivity(
513                 new Intent(Settings.ACTION_WIFI_SETTINGS)
514                         .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
515         clearPendingNotification(false /* resetRepeatTime */);
516     }
517 
handleConnectionAttemptFailedToSend()518     private void handleConnectionAttemptFailedToSend() {
519         handleConnectionFailure();
520         mWifiMetrics.incrementNumNetworkConnectMessageFailedToSend(mTag);
521     }
522 
handlePickWifiNetworkAfterConnectFailure()523     private void handlePickWifiNetworkAfterConnectFailure() {
524         mWifiMetrics.incrementConnectToNetworkNotificationAction(mTag, mState,
525                 ConnectToNetworkNotificationAndActionCount
526                         .ACTION_PICK_WIFI_NETWORK_AFTER_CONNECT_FAILURE);
527         startWifiSettings();
528     }
529 
handleUserDismissedAction()530     private void handleUserDismissedAction() {
531         Log.d(mTag, "User dismissed notification with state=" + mState);
532         mWifiMetrics.incrementConnectToNetworkNotificationAction(mTag, mState,
533                 ConnectToNetworkNotificationAndActionCount.ACTION_USER_DISMISSED_NOTIFICATION);
534         if (mState == STATE_SHOWING_RECOMMENDATION_NOTIFICATION) {
535             // blocklist dismissed network
536             addNetworkToBlocklist(mRecommendedNetwork.SSID);
537         }
538         resetStateAndDelayNotification();
539     }
540 
resetStateAndDelayNotification()541     private void resetStateAndDelayNotification() {
542         mState = STATE_NO_NOTIFICATION;
543         mNotificationRepeatTime = System.currentTimeMillis() + mNotificationRepeatDelay;
544         mRecommendedNetwork = null;
545     }
546 
547     /** Dump this network notifier's state. */
dump(FileDescriptor fd, PrintWriter pw, String[] args)548     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
549         pw.println(mTag + ": ");
550         pw.println("mSettingEnabled " + mSettingEnabled);
551         pw.println("currentTime: " + mClock.getWallClockMillis());
552         pw.println("mNotificationRepeatTime: " + mNotificationRepeatTime);
553         pw.println("mState: " + mState);
554         pw.println("mBlocklistedSsids: " + mBlocklistedSsids.toString());
555     }
556 
557     private class AvailableNetworkNotifierStoreData implements SsidSetStoreData.DataSource {
558         @Override
getSsids()559         public Set<String> getSsids() {
560             return new ArraySet<>(mBlocklistedSsids);
561         }
562 
563         @Override
setSsids(Set<String> ssidList)564         public void setSsids(Set<String> ssidList) {
565             mBlocklistedSsids.addAll(ssidList);
566             mWifiMetrics.setNetworkRecommenderBlocklistSize(mTag, mBlocklistedSsids.size());
567         }
568     }
569 
570     private class NotificationEnabledSettingObserver extends ContentObserver {
NotificationEnabledSettingObserver(Handler handler)571         NotificationEnabledSettingObserver(Handler handler) {
572             super(handler);
573         }
574 
register()575         public void register() {
576             mFrameworkFacade.registerContentObserver(mContext,
577                     Settings.Global.getUriFor(mToggleSettingsName), true, this);
578             mSettingEnabled = getValue();
579         }
580 
581         @Override
onChange(boolean selfChange)582         public void onChange(boolean selfChange) {
583             super.onChange(selfChange);
584             mSettingEnabled = getValue();
585             clearPendingNotification(true /* resetRepeatTime */);
586         }
587 
getValue()588         private boolean getValue() {
589             boolean enabled =
590                     mFrameworkFacade.getIntegerSetting(mContext, mToggleSettingsName, 1) == 1;
591             mWifiMetrics.setIsWifiNetworksAvailableNotificationEnabled(mTag, enabled);
592             Log.d(mTag, "Settings toggle enabled=" + enabled);
593             return enabled;
594         }
595     }
596 }
597