1 /*
2  * Copyright (C) 2017 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 package com.android.networkrecommendation.notify;
17 
18 import static com.android.networkrecommendation.Constants.TAG;
19 
20 import android.app.Notification;
21 import android.app.NotificationManager;
22 import android.content.BroadcastReceiver;
23 import android.content.ContentResolver;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.IntentFilter;
27 import android.database.ContentObserver;
28 import android.net.NetworkInfo;
29 import android.net.NetworkScoreManager;
30 import android.net.RecommendationRequest;
31 import android.net.RecommendationResult;
32 import android.net.wifi.ScanResult;
33 import android.net.wifi.WifiConfiguration;
34 import android.net.wifi.WifiManager;
35 import android.os.Handler;
36 import android.os.UserManager;
37 import android.provider.Settings;
38 import android.support.annotation.IntDef;
39 import android.support.annotation.Nullable;
40 import com.android.networkrecommendation.R;
41 import com.android.networkrecommendation.SynchronousNetworkRecommendationProvider;
42 import com.android.networkrecommendation.util.Blog;
43 import com.android.networkrecommendation.util.RoboCompatUtil;
44 import com.android.networkrecommendation.util.ScanResultUtil;
45 import java.io.FileDescriptor;
46 import java.io.PrintWriter;
47 import java.lang.annotation.Retention;
48 import java.lang.annotation.RetentionPolicy;
49 import java.util.ArrayList;
50 import java.util.List;
51 import java.util.concurrent.TimeUnit;
52 import java.util.concurrent.atomic.AtomicBoolean;
53 
54 /** Takes care of handling the "open wi-fi network available" notification */
55 public class WifiNotificationController {
56     /** The unique ID of the Notification given to the NotificationManager. */
57     private static final int NOTIFICATION_ID = R.string.wifi_available_title;
58 
59     /** When a notification is shown, we wait this amount before possibly showing it again. */
60     private final long mNotificationRepeatDelayMs;
61 
62     /** Whether the user has set the setting to show the 'available networks' notification. */
63     private boolean mNotificationEnabled;
64 
65     /** Whether the user has {@link UserManager#DISALLOW_CONFIG_WIFI} restriction. */
66     private boolean mWifiConfigRestricted;
67 
68     /** Observes the user setting to keep {@link #mNotificationEnabled} in sync. */
69     private final NotificationEnabledSettingObserver mNotificationEnabledSettingObserver;
70 
71     /**
72      * The {@link System#currentTimeMillis()} must be at least this value for us to show the
73      * notification again.
74      */
75     private long mNotificationRepeatTime;
76 
77     /** These are all of the possible states for the open networks available notification. */
78     @IntDef({
79         State.NO_RECOMMENDATION,
80         State.SHOWING_RECOMMENDATION_NOTIFICATION,
81         State.CONNECTING_IN_NOTIFICATION,
82         State.CONNECTING_IN_WIFI_PICKER,
83         State.CONNECTED,
84         State.CONNECT_FAILED
85     })
86     @Retention(RetentionPolicy.SOURCE)
87     public @interface State {
88         int NO_RECOMMENDATION = 0;
89         int SHOWING_RECOMMENDATION_NOTIFICATION = 1;
90         int CONNECTING_IN_NOTIFICATION = 2;
91         int CONNECTING_IN_WIFI_PICKER = 3;
92         int CONNECTED = 4;
93         int CONNECT_FAILED = 5;
94     }
95 
96     /**
97      * The {@link System#currentTimeMillis()} must be at least this value to log that open networks
98      * are available.
99      */
100     private long mOpenNetworksLoggingRepeatTime;
101 
102     /** Current state of the notification. */
103     @State private int mState = State.NO_RECOMMENDATION;
104 
105     /**
106      * The number of continuous scans that must occur before consider the supplicant in a scanning
107      * state. This allows supplicant to associate with remembered networks that are in the scan
108      * results.
109      */
110     private static final int NUM_SCANS_BEFORE_ACTUALLY_SCANNING = 3;
111 
112     /**
113      * The number of scans since the last network state change. When this exceeds {@link
114      * #NUM_SCANS_BEFORE_ACTUALLY_SCANNING}, we consider the supplicant to actually be scanning.
115      * When the network state changes to something other than scanning, we reset this to 0.
116      */
117     private int mNumScansSinceNetworkStateChange;
118 
119     /** Time in milliseconds to display the Connecting notification. */
120     private static final int TIME_TO_SHOW_CONNECTING_MILLIS = 10000;
121 
122     /** Time in milliseconds to display the Connected notification. */
123     private static final int TIME_TO_SHOW_CONNECTED_MILLIS = 5000;
124 
125     /** Time in milliseconds to display the Failed To Connect notification. */
126     private static final int TIME_TO_SHOW_FAILED_MILLIS = 5000;
127 
128     /** Try to connect to the recommended WifiConfiguration and also open the wifi picker. */
129     static final String ACTION_CONNECT_TO_RECOMMENDED_NETWORK_AND_OPEN_SETTINGS =
130             "com.android.networkrecommendation.notify.CONNECT_TO_RECOMMENDED_NETWORK_AND_OPEN_SETTINGS";
131 
132     /** Try to connect to the recommended WifiConfiguration. */
133     static final String ACTION_CONNECT_TO_RECOMMENDED_NETWORK =
134             "com.android.networkrecommendation.notify.CONNECT_TO_RECOMMENDED_NETWORK";
135 
136     /** Open wifi picker to see all available networks. */
137     static final String ACTION_PICK_WIFI_NETWORK =
138             "com.android.networkrecommendation.notify.ACTION_PICK_WIFI_NETWORK";
139 
140     /** Open wifi picker to see all networks after connect to the recommended network failed. */
141     static final String ACTION_PICK_WIFI_NETWORK_AFTER_CONNECT_FAILURE =
142             "com.android.networkrecommendation.notify.ACTION_PICK_WIFI_NETWORK_AFTER_CONNECT_FAILURE";
143 
144     /** Handles behavior when notification is deleted. */
145     static final String ACTION_NOTIFICATION_DELETED =
146             "com.android.networkrecommendation.notify.NOTIFICATION_DELETED";
147 
148     /** Network recommended by {@link NetworkScoreManager#requestRecommendation}. */
149     private WifiConfiguration mRecommendedNetwork;
150 
151     /** Whether {@link WifiNotificationController} has been started. */
152     private final AtomicBoolean mStarted;
153 
154     private static final String NOTIFICATION_TAG = "WifiNotification";
155 
156     private final Context mContext;
157     private final Handler mHandler;
158     private final ContentResolver mContentResolver;
159     private final SynchronousNetworkRecommendationProvider mNetworkRecommendationProvider;
160     private final WifiManager mWifiManager;
161     private final NotificationManager mNotificationManager;
162     private final UserManager mUserManager;
163     private final WifiNotificationHelper mWifiNotificationHelper;
164     private NetworkInfo mNetworkInfo;
165     private NetworkInfo.DetailedState mDetailedState;
166     private volatile int mWifiState;
167 
WifiNotificationController( Context context, ContentResolver contentResolver, Handler handler, SynchronousNetworkRecommendationProvider networkRecommendationProvider, WifiManager wifiManager, NotificationManager notificationManager, UserManager userManager, WifiNotificationHelper helper)168     public WifiNotificationController(
169             Context context,
170             ContentResolver contentResolver,
171             Handler handler,
172             SynchronousNetworkRecommendationProvider networkRecommendationProvider,
173             WifiManager wifiManager,
174             NotificationManager notificationManager,
175             UserManager userManager,
176             WifiNotificationHelper helper) {
177         mContext = context;
178         mContentResolver = contentResolver;
179         mNetworkRecommendationProvider = networkRecommendationProvider;
180         mWifiManager = wifiManager;
181         mNotificationManager = notificationManager;
182         mUserManager = userManager;
183         mHandler = handler;
184         mWifiNotificationHelper = helper;
185         mStarted = new AtomicBoolean(false);
186 
187         // Setting is in seconds
188         mNotificationRepeatDelayMs =
189                 TimeUnit.SECONDS.toMillis(
190                         Settings.Global.getInt(
191                                 contentResolver,
192                                 Settings.Global.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY,
193                                 900));
194         mNotificationEnabledSettingObserver = new NotificationEnabledSettingObserver(mHandler);
195     }
196 
197     /** Starts {@link WifiNotificationController}. */
start()198     public void start() {
199         if (!mStarted.compareAndSet(false, true)) {
200             return;
201         }
202 
203         mWifiState = mWifiManager.getWifiState();
204         mDetailedState = NetworkInfo.DetailedState.IDLE;
205 
206         IntentFilter filter = new IntentFilter();
207         filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
208         filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
209         filter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
210         filter.addAction(RoboCompatUtil.ACTION_USER_RESTRICTIONS_CHANGED);
211         filter.addAction(ACTION_CONNECT_TO_RECOMMENDED_NETWORK_AND_OPEN_SETTINGS);
212         filter.addAction(ACTION_CONNECT_TO_RECOMMENDED_NETWORK);
213         filter.addAction(ACTION_NOTIFICATION_DELETED);
214         filter.addAction(ACTION_PICK_WIFI_NETWORK);
215         filter.addAction(ACTION_PICK_WIFI_NETWORK_AFTER_CONNECT_FAILURE);
216 
217         mContext.registerReceiver(
218                 mBroadcastReceiver, filter, null /* broadcastPermission */, mHandler);
219         mNotificationEnabledSettingObserver.register();
220 
221         handleUserRestrictionsChanged();
222     }
223 
224     /** Stops {@link WifiNotificationController}. */
stop()225     public void stop() {
226         if (!mStarted.compareAndSet(true, false)) {
227             return;
228         }
229         mContext.unregisterReceiver(mBroadcastReceiver);
230         mNotificationEnabledSettingObserver.unregister();
231     }
232 
233     private final BroadcastReceiver mBroadcastReceiver =
234             new BroadcastReceiver() {
235                 @Override
236                 public void onReceive(Context context, Intent intent) {
237                     try {
238                         switch (intent.getAction()) {
239                             case WifiManager.WIFI_STATE_CHANGED_ACTION:
240                                 mWifiState = mWifiManager.getWifiState();
241                                 resetNotification();
242                                 break;
243                             case WifiManager.NETWORK_STATE_CHANGED_ACTION:
244                                 handleNetworkStateChange(intent);
245                                 break;
246                             case WifiManager.SCAN_RESULTS_AVAILABLE_ACTION:
247                                 checkAndSetNotification(mNetworkInfo);
248                                 break;
249                             case RoboCompatUtil.ACTION_USER_RESTRICTIONS_CHANGED:
250                                 handleUserRestrictionsChanged();
251                                 break;
252                             case ACTION_CONNECT_TO_RECOMMENDED_NETWORK_AND_OPEN_SETTINGS:
253                                 connectToRecommendedNetwork();
254                                 openWifiPicker();
255                                 updateOnConnecting(false /* showNotification*/);
256                                 break;
257                             case ACTION_CONNECT_TO_RECOMMENDED_NETWORK:
258                                 connectToRecommendedNetwork();
259                                 updateOnConnecting(true /* showNotification*/);
260                                 break;
261                             case ACTION_NOTIFICATION_DELETED:
262                                 handleNotificationDeleted();
263                                 break;
264                             case ACTION_PICK_WIFI_NETWORK:
265                                 openWifiPicker();
266                                 break;
267                             case ACTION_PICK_WIFI_NETWORK_AFTER_CONNECT_FAILURE:
268                                 openWifiPicker();
269                                 break;
270                             default:
271                                 Blog.e(
272                                         TAG,
273                                         "Unexpected broadcast. [action=%s]",
274                                         intent.getAction());
275                         }
276 
277                     } catch (RuntimeException re) {
278                         // TODO(b/35044022) Remove try/catch after a couple of releases when we are confident
279                         // this is not going to throw.
280                         Blog.e(TAG, re, "RuntimeException in broadcast receiver.");
281                     }
282                 }
283             };
284 
handleNetworkStateChange(Intent intent)285     private void handleNetworkStateChange(Intent intent) {
286         mNetworkInfo = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
287         NetworkInfo.DetailedState detailedState = mNetworkInfo.getDetailedState();
288         if (detailedState != NetworkInfo.DetailedState.SCANNING
289                 && detailedState != mDetailedState) {
290             mDetailedState = detailedState;
291             switch (mDetailedState) {
292                 case CONNECTED:
293                     updateOnConnect();
294                     break;
295                 case DISCONNECTED:
296                 case CAPTIVE_PORTAL_CHECK:
297                     resetNotification();
298                     break;
299 
300                     // TODO: figure out if these are failure cases when connecting
301                 case IDLE:
302                 case SCANNING:
303                 case CONNECTING:
304                 case DISCONNECTING:
305                 case AUTHENTICATING:
306                 case OBTAINING_IPADDR:
307                 case SUSPENDED:
308                 case FAILED:
309                 case BLOCKED:
310                 case VERIFYING_POOR_LINK:
311                     break;
312             }
313         }
314     }
315 
handleUserRestrictionsChanged()316     private void handleUserRestrictionsChanged() {
317         mWifiConfigRestricted = mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_WIFI);
318         Blog.v(TAG, "handleUserRestrictionsChanged: %b", mWifiConfigRestricted);
319     }
320 
checkAndSetNotification(NetworkInfo networkInfo)321     private void checkAndSetNotification(NetworkInfo networkInfo) {
322         // TODO: unregister broadcast so we do not have to check here
323         // If we shouldn't place a notification on available networks, then
324         // don't bother doing any of the following
325         if (!mNotificationEnabled
326                 || mWifiConfigRestricted
327                 || mWifiState != WifiManager.WIFI_STATE_ENABLED
328                 || mState > State.SHOWING_RECOMMENDATION_NOTIFICATION) {
329             return;
330         }
331 
332         NetworkInfo.State state = NetworkInfo.State.DISCONNECTED;
333         if (networkInfo != null) {
334             state = networkInfo.getState();
335         }
336 
337         if (state == NetworkInfo.State.DISCONNECTED || state == NetworkInfo.State.UNKNOWN) {
338             maybeLogOpenNetworksAvailable();
339             RecommendationResult result = getOpenNetworkRecommendation();
340             if (result != null && result.getWifiConfiguration() != null) {
341                 mRecommendedNetwork = result.getWifiConfiguration();
342 
343                 if (++mNumScansSinceNetworkStateChange >= NUM_SCANS_BEFORE_ACTUALLY_SCANNING) {
344                     /*
345                      * We have scanned continuously at least
346                      * NUM_SCANS_BEFORE_NOTIFICATION times. The user
347                      * probably does not have a remembered network in range,
348                      * since otherwise supplicant would have tried to
349                      * associate and thus resetting this counter.
350                      */
351                     displayNotification();
352                 }
353                 return;
354             }
355         }
356 
357         // No open networks in range, remove the notification
358         removeNotification();
359     }
360 
maybeLogOpenNetworksAvailable()361     private void maybeLogOpenNetworksAvailable() {
362         long now = System.currentTimeMillis();
363         if (now < mOpenNetworksLoggingRepeatTime) {
364             return;
365         }
366         mOpenNetworksLoggingRepeatTime = now + mNotificationRepeatDelayMs;
367     }
368 
369     /**
370      * Uses {@link NetworkScoreManager} to choose a qualified network out of the list of {@link
371      * ScanResult}s.
372      *
373      * @return returns the best qualified open networks, if any.
374      */
375     @Nullable
getOpenNetworkRecommendation()376     private RecommendationResult getOpenNetworkRecommendation() {
377         List<ScanResult> scanResults = mWifiManager.getScanResults();
378         if (scanResults == null || scanResults.isEmpty()) {
379             return null;
380         }
381 
382         ArrayList<ScanResult> openNetworks = new ArrayList<>();
383         List<WifiConfiguration> configuredNetworks = mWifiManager.getConfiguredNetworks();
384         for (ScanResult scanResult : scanResults) {
385             //A capability of [ESS] represents an open access point
386             //that is available for an STA to connect
387             //TODO: potentially handle this within NetworkRecommendationProvider instead.
388             if ("[ESS]".equals(scanResult.capabilities)) {
389                 if (isSavedNetwork(scanResult, configuredNetworks)) {
390                     continue;
391                 }
392                 openNetworks.add(scanResult);
393             }
394         }
395 
396         Blog.d(TAG, "Sending RecommendationRequest. [num_open_networks=%d]", openNetworks.size());
397         RecommendationRequest request =
398                 new RecommendationRequest.Builder()
399                         .setScanResults(openNetworks.toArray(new ScanResult[openNetworks.size()]))
400                         .build();
401         return mNetworkRecommendationProvider.requestRecommendation(request);
402     }
403 
404     /** Returns true if scanResult matches the list of saved networks */
isSavedNetwork(ScanResult scanResult, List<WifiConfiguration> savedNetworks)405     private boolean isSavedNetwork(ScanResult scanResult, List<WifiConfiguration> savedNetworks) {
406         if (savedNetworks == null) {
407             return false;
408         }
409         for (int i = 0; i < savedNetworks.size(); i++) {
410             if (ScanResultUtil.doesScanResultMatchWithNetwork(scanResult, savedNetworks.get(i))) {
411                 return true;
412             }
413         }
414         return false;
415     }
416 
417     /** Display's a notification that there are open Wi-Fi networks. */
displayNotification()418     private void displayNotification() {
419         // Since we use auto cancel on the notification, when the
420         // mNetworksAvailableNotificationShown is true, the notification may
421         // have actually been canceled.  However, when it is false we know
422         // for sure that it is not being shown (it will not be shown any other
423         // place than here)
424 
425         // Not enough time has passed to show the notification again
426         if (mState == State.NO_RECOMMENDATION
427                 && System.currentTimeMillis() < mNotificationRepeatTime) {
428             return;
429         }
430         Notification notification =
431                 mWifiNotificationHelper.createMainNotification(mRecommendedNetwork);
432         mNotificationRepeatTime = System.currentTimeMillis() + mNotificationRepeatDelayMs;
433         postNotification(notification);
434         if (mState != State.SHOWING_RECOMMENDATION_NOTIFICATION) {
435             mState = State.SHOWING_RECOMMENDATION_NOTIFICATION;
436         }
437     }
438 
439     /** Opens activity to allow the user to select a wifi network. */
openWifiPicker()440     private void openWifiPicker() {
441         // Close notification drawer before opening the picker.
442         mContext.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
443         mContext.startActivity(
444                 new Intent(WifiManager.ACTION_PICK_WIFI_NETWORK)
445                         .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
446         removeNotification();
447     }
448 
449     /** Attempts to connect to recommended network the recommended network. */
connectToRecommendedNetwork()450     private void connectToRecommendedNetwork() {
451         if (mRecommendedNetwork == null) {
452             return;
453         }
454         mRecommendedNetwork.BSSID = null;
455 
456         // Attempts to connect to recommended network.
457         RoboCompatUtil.getInstance().connectToWifi(mWifiManager, mRecommendedNetwork);
458     }
459 
updateOnConnecting(boolean showNotification)460     private void updateOnConnecting(boolean showNotification) {
461         if (showNotification) {
462             // Update notification to connecting status.
463             Notification notification =
464                     mWifiNotificationHelper.createConnectingNotification(mRecommendedNetwork);
465             postNotification(notification);
466             mState = State.CONNECTING_IN_NOTIFICATION;
467         } else {
468             mState = State.CONNECTING_IN_WIFI_PICKER;
469         }
470         mHandler.postDelayed(
471                 () -> {
472                     updateOnFailedToConnect();
473                 },
474                 TIME_TO_SHOW_CONNECTING_MILLIS);
475     }
476 
477     /**
478      * When detailed state changes to CONNECTED, show connected notification or reset notification.
479      */
updateOnConnect()480     private void updateOnConnect() {
481         if (mState == State.CONNECTING_IN_NOTIFICATION) {
482             Notification notification =
483                     mWifiNotificationHelper.createConnectedNotification(mRecommendedNetwork);
484             postNotification(notification);
485             mState = State.CONNECTED;
486             mHandler.postDelayed(
487                     () -> {
488                         if (mState == State.CONNECTED) {
489                             removeNotification();
490                         }
491                     },
492                     TIME_TO_SHOW_CONNECTED_MILLIS);
493         } else if (mState == State.CONNECTING_IN_WIFI_PICKER) {
494             removeNotification();
495         }
496     }
497 
498     /**
499      * Displays the Failed To Connect notification after the Connecting notification is shown for
500      * {@link #TIME_TO_SHOW_CONNECTING_MILLIS} duration.
501      */
updateOnFailedToConnect()502     private void updateOnFailedToConnect() {
503         if (mState == State.CONNECTING_IN_NOTIFICATION) {
504             Notification notification = mWifiNotificationHelper.createFailedToConnectNotification();
505             postNotification(notification);
506             mState = State.CONNECT_FAILED;
507             mHandler.postDelayed(
508                     () -> {
509                         if (mState == State.CONNECT_FAILED) {
510                             removeNotification();
511                         }
512                     },
513                     TIME_TO_SHOW_FAILED_MILLIS);
514         } else if (mState == State.CONNECTING_IN_WIFI_PICKER) {
515             removeNotification();
516         }
517     }
518 
519     /** Handles behavior when notification is dismissed. */
handleNotificationDeleted()520     private void handleNotificationDeleted() {
521         mState = State.NO_RECOMMENDATION;
522         mRecommendedNetwork = null;
523     }
524 
postNotification(Notification notification)525     private void postNotification(Notification notification) {
526         mNotificationManager.notify(NOTIFICATION_TAG, NOTIFICATION_ID, notification);
527     }
528 
529     /**
530      * Clears variables related to tracking whether a notification has been shown recently and
531      * clears the current notification.
532      */
resetNotification()533     private void resetNotification() {
534         if (mState != State.NO_RECOMMENDATION) {
535             removeNotification();
536         }
537         mRecommendedNetwork = null;
538         mNotificationRepeatTime = 0;
539         mNumScansSinceNetworkStateChange = 0;
540         mOpenNetworksLoggingRepeatTime = 0;
541     }
542 
removeNotification()543     private void removeNotification() {
544         mNotificationManager.cancel(NOTIFICATION_TAG, NOTIFICATION_ID);
545         mState = State.NO_RECOMMENDATION;
546         mRecommendedNetwork = null;
547     }
548 
dump(FileDescriptor fd, PrintWriter pw, String[] args)549     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
550         pw.println("mNotificationEnabled " + mNotificationEnabled);
551         pw.println("mNotificationRepeatTime " + mNotificationRepeatTime);
552         pw.println("mState " + mState);
553         pw.println("mNumScansSinceNetworkStateChange " + mNumScansSinceNetworkStateChange);
554     }
555 
556     private class NotificationEnabledSettingObserver extends ContentObserver {
NotificationEnabledSettingObserver(Handler handler)557         NotificationEnabledSettingObserver(Handler handler) {
558             super(handler);
559         }
560 
register()561         public void register() {
562             mContentResolver.registerContentObserver(
563                     Settings.Global.getUriFor(
564                             Settings.Global.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON),
565                     true,
566                     this);
567             mNotificationEnabled = getValue();
568         }
569 
unregister()570         public void unregister() {
571             mContentResolver.unregisterContentObserver(this);
572         }
573 
574         @Override
onChange(boolean selfChange)575         public void onChange(boolean selfChange) {
576             super.onChange(selfChange);
577 
578             mNotificationEnabled = getValue();
579             resetNotification();
580         }
581 
getValue()582         private boolean getValue() {
583             return Settings.Global.getInt(
584                             mContentResolver,
585                             Settings.Global.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON,
586                             0)
587                     == 1;
588         }
589     }
590 }
591