Home | History | Annotate | Download | only in server
      1 /*
      2  * Copyright (C) 2010 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;
     18 
     19 import android.app.AlarmManager;
     20 import android.app.Notification;
     21 import android.app.NotificationManager;
     22 import android.app.PendingIntent;
     23 import android.bluetooth.BluetoothAdapter;
     24 import android.content.BroadcastReceiver;
     25 import android.content.ContentResolver;
     26 import android.content.Context;
     27 import android.content.Intent;
     28 import android.content.IntentFilter;
     29 import android.content.pm.PackageManager;
     30 import android.database.ContentObserver;
     31 import android.net.wifi.IWifiManager;
     32 import android.net.wifi.ScanResult;
     33 import android.net.wifi.SupplicantState;
     34 import android.net.wifi.WifiInfo;
     35 import android.net.wifi.WifiManager;
     36 import android.net.wifi.WifiStateMachine;
     37 import android.net.wifi.WifiConfiguration;
     38 import android.net.wifi.WifiWatchdogStateMachine;
     39 import android.net.wifi.WifiConfiguration.KeyMgmt;
     40 import android.net.wifi.WpsInfo;
     41 import android.net.wifi.WpsResult;
     42 import android.net.ConnectivityManager;
     43 import android.net.DhcpInfo;
     44 import android.net.NetworkInfo;
     45 import android.net.NetworkInfo.State;
     46 import android.net.NetworkInfo.DetailedState;
     47 import android.net.TrafficStats;
     48 import android.os.Binder;
     49 import android.os.Handler;
     50 import android.os.Messenger;
     51 import android.os.HandlerThread;
     52 import android.os.IBinder;
     53 import android.os.INetworkManagementService;
     54 import android.os.Message;
     55 import android.os.RemoteException;
     56 import android.os.ServiceManager;
     57 import android.os.SystemProperties;
     58 import android.os.WorkSource;
     59 import android.provider.Settings;
     60 import android.text.TextUtils;
     61 import android.util.Slog;
     62 
     63 import java.util.ArrayList;
     64 import java.util.HashMap;
     65 import java.util.List;
     66 import java.util.Set;
     67 import java.util.concurrent.atomic.AtomicInteger;
     68 import java.util.concurrent.atomic.AtomicBoolean;
     69 import java.io.FileDescriptor;
     70 import java.io.PrintWriter;
     71 
     72 import com.android.internal.app.IBatteryStats;
     73 import com.android.internal.telephony.TelephonyIntents;
     74 import com.android.internal.util.AsyncChannel;
     75 import com.android.server.am.BatteryStatsService;
     76 import com.android.internal.R;
     77 
     78 /**
     79  * WifiService handles remote WiFi operation requests by implementing
     80  * the IWifiManager interface.
     81  *
     82  * @hide
     83  */
     84 //TODO: Clean up multiple locks and implement WifiService
     85 // as a SM to track soft AP/client/adhoc bring up based
     86 // on device idle state, airplane mode and boot.
     87 
     88 public class WifiService extends IWifiManager.Stub {
     89     private static final String TAG = "WifiService";
     90     private static final boolean DBG = false;
     91 
     92     private final WifiStateMachine mWifiStateMachine;
     93 
     94     private Context mContext;
     95 
     96     private AlarmManager mAlarmManager;
     97     private PendingIntent mIdleIntent;
     98     private static final int IDLE_REQUEST = 0;
     99     private boolean mScreenOff;
    100     private boolean mDeviceIdle;
    101     private boolean mEmergencyCallbackMode = false;
    102     private int mPluggedType;
    103 
    104     private final LockList mLocks = new LockList();
    105     // some wifi lock statistics
    106     private int mFullHighPerfLocksAcquired;
    107     private int mFullHighPerfLocksReleased;
    108     private int mFullLocksAcquired;
    109     private int mFullLocksReleased;
    110     private int mScanLocksAcquired;
    111     private int mScanLocksReleased;
    112 
    113     /* A mapping from UID to scan count */
    114     private HashMap<Integer, Integer> mScanCount =
    115             new HashMap<Integer, Integer>();
    116 
    117     private final List<Multicaster> mMulticasters =
    118             new ArrayList<Multicaster>();
    119     private int mMulticastEnabled;
    120     private int mMulticastDisabled;
    121 
    122     private final IBatteryStats mBatteryStats;
    123 
    124     private boolean mEnableTrafficStatsPoll = false;
    125     private int mTrafficStatsPollToken = 0;
    126     private long mTxPkts;
    127     private long mRxPkts;
    128     /* Tracks last reported data activity */
    129     private int mDataActivity;
    130     private String mInterfaceName;
    131 
    132     /**
    133      * Interval in milliseconds between polling for traffic
    134      * statistics
    135      */
    136     private static final int POLL_TRAFFIC_STATS_INTERVAL_MSECS = 1000;
    137 
    138     /**
    139      * See {@link Settings.Secure#WIFI_IDLE_MS}. This is the default value if a
    140      * Settings.Secure value is not present. This timeout value is chosen as
    141      * the approximate point at which the battery drain caused by Wi-Fi
    142      * being enabled but not active exceeds the battery drain caused by
    143      * re-establishing a connection to the mobile data network.
    144      */
    145     private static final long DEFAULT_IDLE_MS = 15 * 60 * 1000; /* 15 minutes */
    146 
    147     private static final String ACTION_DEVICE_IDLE =
    148             "com.android.server.WifiManager.action.DEVICE_IDLE";
    149 
    150     private static final int WIFI_DISABLED                  = 0;
    151     private static final int WIFI_ENABLED                   = 1;
    152     /* Wifi enabled while in airplane mode */
    153     private static final int WIFI_ENABLED_AIRPLANE_OVERRIDE = 2;
    154     /* Wifi disabled due to airplane mode on */
    155     private static final int WIFI_DISABLED_AIRPLANE_ON      = 3;
    156 
    157     /* Persisted state that tracks the wifi & airplane interaction from settings */
    158     private AtomicInteger mPersistWifiState = new AtomicInteger(WIFI_DISABLED);
    159     /* Tracks current airplane mode state */
    160     private AtomicBoolean mAirplaneModeOn = new AtomicBoolean(false);
    161     /* Tracks whether wifi is enabled from WifiStateMachine's perspective */
    162     private boolean mWifiEnabled;
    163 
    164     private boolean mIsReceiverRegistered = false;
    165 
    166 
    167     NetworkInfo mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, "WIFI", "");
    168 
    169     // Variables relating to the 'available networks' notification
    170     /**
    171      * The icon to show in the 'available networks' notification. This will also
    172      * be the ID of the Notification given to the NotificationManager.
    173      */
    174     private static final int ICON_NETWORKS_AVAILABLE =
    175             com.android.internal.R.drawable.stat_notify_wifi_in_range;
    176     /**
    177      * When a notification is shown, we wait this amount before possibly showing it again.
    178      */
    179     private final long NOTIFICATION_REPEAT_DELAY_MS;
    180     /**
    181      * Whether the user has set the setting to show the 'available networks' notification.
    182      */
    183     private boolean mNotificationEnabled;
    184     /**
    185      * Observes the user setting to keep {@link #mNotificationEnabled} in sync.
    186      */
    187     private NotificationEnabledSettingObserver mNotificationEnabledSettingObserver;
    188     /**
    189      * The {@link System#currentTimeMillis()} must be at least this value for us
    190      * to show the notification again.
    191      */
    192     private long mNotificationRepeatTime;
    193     /**
    194      * The Notification object given to the NotificationManager.
    195      */
    196     private Notification mNotification;
    197     /**
    198      * Whether the notification is being shown, as set by us. That is, if the
    199      * user cancels the notification, we will not receive the callback so this
    200      * will still be true. We only guarantee if this is false, then the
    201      * notification is not showing.
    202      */
    203     private boolean mNotificationShown;
    204     /**
    205      * The number of continuous scans that must occur before consider the
    206      * supplicant in a scanning state. This allows supplicant to associate with
    207      * remembered networks that are in the scan results.
    208      */
    209     private static final int NUM_SCANS_BEFORE_ACTUALLY_SCANNING = 3;
    210     /**
    211      * The number of scans since the last network state change. When this
    212      * exceeds {@link #NUM_SCANS_BEFORE_ACTUALLY_SCANNING}, we consider the
    213      * supplicant to actually be scanning. When the network state changes to
    214      * something other than scanning, we reset this to 0.
    215      */
    216     private int mNumScansSinceNetworkStateChange;
    217 
    218     /**
    219      * Asynchronous channel to WifiStateMachine
    220      */
    221     private AsyncChannel mWifiStateMachineChannel;
    222 
    223     /**
    224      * Clients receiving asynchronous messages
    225      */
    226     private List<AsyncChannel> mClients = new ArrayList<AsyncChannel>();
    227 
    228     /**
    229      * Handles client connections
    230      */
    231     private class AsyncServiceHandler extends Handler {
    232 
    233         AsyncServiceHandler(android.os.Looper looper) {
    234             super(looper);
    235         }
    236 
    237         @Override
    238         public void handleMessage(Message msg) {
    239             switch (msg.what) {
    240                 case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: {
    241                     if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
    242                         Slog.d(TAG, "New client listening to asynchronous messages");
    243                         mClients.add((AsyncChannel) msg.obj);
    244                     } else {
    245                         Slog.e(TAG, "Client connection failure, error=" + msg.arg1);
    246                     }
    247                     break;
    248                 }
    249                 case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
    250                     if (msg.arg1 == AsyncChannel.STATUS_SEND_UNSUCCESSFUL) {
    251                         Slog.d(TAG, "Send failed, client connection lost");
    252                     } else {
    253                         Slog.d(TAG, "Client connection lost with reason: " + msg.arg1);
    254                     }
    255                     mClients.remove((AsyncChannel) msg.obj);
    256                     break;
    257                 }
    258                 case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: {
    259                     AsyncChannel ac = new AsyncChannel();
    260                     ac.connect(mContext, this, msg.replyTo);
    261                     break;
    262                 }
    263                 case WifiManager.ENABLE_TRAFFIC_STATS_POLL: {
    264                     mEnableTrafficStatsPoll = (msg.arg1 == 1);
    265                     mTrafficStatsPollToken++;
    266                     if (mEnableTrafficStatsPoll) {
    267                         notifyOnDataActivity();
    268                         sendMessageDelayed(Message.obtain(this, WifiManager.TRAFFIC_STATS_POLL,
    269                                 mTrafficStatsPollToken, 0), POLL_TRAFFIC_STATS_INTERVAL_MSECS);
    270                     }
    271                     break;
    272                 }
    273                 case WifiManager.TRAFFIC_STATS_POLL: {
    274                     if (msg.arg1 == mTrafficStatsPollToken) {
    275                         notifyOnDataActivity();
    276                         sendMessageDelayed(Message.obtain(this, WifiManager.TRAFFIC_STATS_POLL,
    277                                 mTrafficStatsPollToken, 0), POLL_TRAFFIC_STATS_INTERVAL_MSECS);
    278                     }
    279                     break;
    280                 }
    281                 case WifiManager.CONNECT_NETWORK: {
    282                     mWifiStateMachine.sendMessage(Message.obtain(msg));
    283                     break;
    284                 }
    285                 case WifiManager.SAVE_NETWORK: {
    286                     mWifiStateMachine.sendMessage(Message.obtain(msg));
    287                     break;
    288                 }
    289                 case WifiManager.FORGET_NETWORK: {
    290                     mWifiStateMachine.sendMessage(Message.obtain(msg));
    291                     break;
    292                 }
    293                 case WifiManager.START_WPS: {
    294                     mWifiStateMachine.sendMessage(Message.obtain(msg));
    295                     break;
    296                 }
    297                 case WifiManager.CANCEL_WPS: {
    298                     mWifiStateMachine.sendMessage(Message.obtain(msg));
    299                     break;
    300                 }
    301                 case WifiManager.DISABLE_NETWORK: {
    302                     mWifiStateMachine.sendMessage(Message.obtain(msg));
    303                     break;
    304                 }
    305                 default: {
    306                     Slog.d(TAG, "WifiServicehandler.handleMessage ignoring msg=" + msg);
    307                     break;
    308                 }
    309             }
    310         }
    311     }
    312     private AsyncServiceHandler mAsyncServiceHandler;
    313 
    314     /**
    315      * Handles interaction with WifiStateMachine
    316      */
    317     private class WifiStateMachineHandler extends Handler {
    318         private AsyncChannel mWsmChannel;
    319 
    320         WifiStateMachineHandler(android.os.Looper looper) {
    321             super(looper);
    322             mWsmChannel = new AsyncChannel();
    323             mWsmChannel.connect(mContext, this, mWifiStateMachine.getHandler());
    324         }
    325 
    326         @Override
    327         public void handleMessage(Message msg) {
    328             switch (msg.what) {
    329                 case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: {
    330                     if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
    331                         mWifiStateMachineChannel = mWsmChannel;
    332                     } else {
    333                         Slog.e(TAG, "WifiStateMachine connection failure, error=" + msg.arg1);
    334                         mWifiStateMachineChannel = null;
    335                     }
    336                     break;
    337                 }
    338                 case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
    339                     Slog.e(TAG, "WifiStateMachine channel lost, msg.arg1 =" + msg.arg1);
    340                     mWifiStateMachineChannel = null;
    341                     //Re-establish connection to state machine
    342                     mWsmChannel.connect(mContext, this, mWifiStateMachine.getHandler());
    343                     break;
    344                 }
    345                 default: {
    346                     Slog.d(TAG, "WifiStateMachineHandler.handleMessage ignoring msg=" + msg);
    347                     break;
    348                 }
    349             }
    350         }
    351     }
    352     WifiStateMachineHandler mWifiStateMachineHandler;
    353 
    354     /**
    355      * Temporary for computing UIDS that are responsible for starting WIFI.
    356      * Protected by mWifiStateTracker lock.
    357      */
    358     private final WorkSource mTmpWorkSource = new WorkSource();
    359     private WifiWatchdogStateMachine mWifiWatchdogStateMachine;
    360 
    361     WifiService(Context context) {
    362         mContext = context;
    363 
    364         mInterfaceName =  SystemProperties.get("wifi.interface", "wlan0");
    365 
    366         mWifiStateMachine = new WifiStateMachine(mContext, mInterfaceName);
    367         mWifiStateMachine.enableRssiPolling(true);
    368         mBatteryStats = BatteryStatsService.getService();
    369 
    370         mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
    371         Intent idleIntent = new Intent(ACTION_DEVICE_IDLE, null);
    372         mIdleIntent = PendingIntent.getBroadcast(mContext, IDLE_REQUEST, idleIntent, 0);
    373 
    374         mContext.registerReceiver(
    375                 new BroadcastReceiver() {
    376                     @Override
    377                     public void onReceive(Context context, Intent intent) {
    378                         mAirplaneModeOn.set(isAirplaneModeOn());
    379                         handleAirplaneModeToggled(mAirplaneModeOn.get());
    380                         updateWifiState();
    381                     }
    382                 },
    383                 new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED));
    384 
    385         IntentFilter filter = new IntentFilter();
    386         filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
    387         filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
    388         filter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
    389 
    390         mContext.registerReceiver(
    391                 new BroadcastReceiver() {
    392                     @Override
    393                     public void onReceive(Context context, Intent intent) {
    394                         if (intent.getAction().equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
    395                             int wifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
    396                                     WifiManager.WIFI_STATE_DISABLED);
    397 
    398                             mWifiEnabled = (wifiState == WifiManager.WIFI_STATE_ENABLED);
    399 
    400                            // reset & clear notification on any wifi state change
    401                             resetNotification();
    402                         } else if (intent.getAction().equals(
    403                                 WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
    404                             mNetworkInfo = (NetworkInfo) intent.getParcelableExtra(
    405                                     WifiManager.EXTRA_NETWORK_INFO);
    406                             // reset & clear notification on a network connect & disconnect
    407                             switch(mNetworkInfo.getDetailedState()) {
    408                                 case CONNECTED:
    409                                 case DISCONNECTED:
    410                                     evaluateTrafficStatsPolling();
    411                                     resetNotification();
    412                                     break;
    413                             }
    414                         } else if (intent.getAction().equals(
    415                                 WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
    416                             checkAndSetNotification();
    417                         }
    418                     }
    419                 }, filter);
    420 
    421         HandlerThread wifiThread = new HandlerThread("WifiService");
    422         wifiThread.start();
    423         mAsyncServiceHandler = new AsyncServiceHandler(wifiThread.getLooper());
    424         mWifiStateMachineHandler = new WifiStateMachineHandler(wifiThread.getLooper());
    425 
    426         // Setting is in seconds
    427         NOTIFICATION_REPEAT_DELAY_MS = Settings.Secure.getInt(context.getContentResolver(),
    428                 Settings.Secure.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY, 900) * 1000l;
    429         mNotificationEnabledSettingObserver = new NotificationEnabledSettingObserver(new Handler());
    430         mNotificationEnabledSettingObserver.register();
    431     }
    432 
    433     /**
    434      * Check if Wi-Fi needs to be enabled and start
    435      * if needed
    436      *
    437      * This function is used only at boot time
    438      */
    439     public void checkAndStartWifi() {
    440         mAirplaneModeOn.set(isAirplaneModeOn());
    441         mPersistWifiState.set(getPersistedWifiState());
    442         /* Start if Wi-Fi should be enabled or the saved state indicates Wi-Fi was on */
    443         boolean wifiEnabled = shouldWifiBeEnabled() || testAndClearWifiSavedState();
    444         Slog.i(TAG, "WifiService starting up with Wi-Fi " +
    445                 (wifiEnabled ? "enabled" : "disabled"));
    446 
    447         // If we are already disabled (could be due to airplane mode), avoid changing persist
    448         // state here
    449         if (wifiEnabled) setWifiEnabled(wifiEnabled);
    450 
    451         mWifiWatchdogStateMachine = WifiWatchdogStateMachine.
    452                makeWifiWatchdogStateMachine(mContext);
    453 
    454     }
    455 
    456     private boolean testAndClearWifiSavedState() {
    457         final ContentResolver cr = mContext.getContentResolver();
    458         int wifiSavedState = 0;
    459         try {
    460             wifiSavedState = Settings.Secure.getInt(cr, Settings.Secure.WIFI_SAVED_STATE);
    461             if(wifiSavedState == 1)
    462                 Settings.Secure.putInt(cr, Settings.Secure.WIFI_SAVED_STATE, 0);
    463         } catch (Settings.SettingNotFoundException e) {
    464             ;
    465         }
    466         return (wifiSavedState == 1);
    467     }
    468 
    469     private int getPersistedWifiState() {
    470         final ContentResolver cr = mContext.getContentResolver();
    471         try {
    472             return Settings.Secure.getInt(cr, Settings.Secure.WIFI_ON);
    473         } catch (Settings.SettingNotFoundException e) {
    474             Settings.Secure.putInt(cr, Settings.Secure.WIFI_ON, WIFI_DISABLED);
    475             return WIFI_DISABLED;
    476         }
    477     }
    478 
    479     private boolean shouldWifiBeEnabled() {
    480         if (mAirplaneModeOn.get()) {
    481             return mPersistWifiState.get() == WIFI_ENABLED_AIRPLANE_OVERRIDE;
    482         } else {
    483             return mPersistWifiState.get() != WIFI_DISABLED;
    484         }
    485     }
    486 
    487     private void handleWifiToggled(boolean wifiEnabled) {
    488         boolean airplaneEnabled = mAirplaneModeOn.get() && isAirplaneToggleable();
    489         if (wifiEnabled) {
    490             if (airplaneEnabled) {
    491                 persistWifiState(WIFI_ENABLED_AIRPLANE_OVERRIDE);
    492             } else {
    493                 persistWifiState(WIFI_ENABLED);
    494             }
    495         } else {
    496             // When wifi state is disabled, we do not care
    497             // if airplane mode is on or not. The scenario of
    498             // wifi being disabled due to airplane mode being turned on
    499             // is handled handleAirplaneModeToggled()
    500             persistWifiState(WIFI_DISABLED);
    501         }
    502     }
    503 
    504     private void handleAirplaneModeToggled(boolean airplaneEnabled) {
    505         if (airplaneEnabled) {
    506             // Wifi disabled due to airplane on
    507             if (mWifiEnabled) {
    508                 persistWifiState(WIFI_DISABLED_AIRPLANE_ON);
    509             }
    510         } else {
    511             /* On airplane mode disable, restore wifi state if necessary */
    512             if (testAndClearWifiSavedState() ||
    513                     mPersistWifiState.get() == WIFI_ENABLED_AIRPLANE_OVERRIDE) {
    514                 persistWifiState(WIFI_ENABLED);
    515             }
    516         }
    517     }
    518 
    519     private void persistWifiState(int state) {
    520         final ContentResolver cr = mContext.getContentResolver();
    521         mPersistWifiState.set(state);
    522         Settings.Secure.putInt(cr, Settings.Secure.WIFI_ON, state);
    523     }
    524 
    525     /**
    526      * see {@link android.net.wifi.WifiManager#pingSupplicant()}
    527      * @return {@code true} if the operation succeeds, {@code false} otherwise
    528      */
    529     public boolean pingSupplicant() {
    530         enforceAccessPermission();
    531         if (mWifiStateMachineChannel != null) {
    532             return mWifiStateMachine.syncPingSupplicant(mWifiStateMachineChannel);
    533         } else {
    534             Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
    535             return false;
    536         }
    537     }
    538 
    539     /**
    540      * see {@link android.net.wifi.WifiManager#startScan()}
    541      */
    542     public void startScan(boolean forceActive) {
    543         enforceChangePermission();
    544 
    545         int uid = Binder.getCallingUid();
    546         int count = 0;
    547         synchronized (mScanCount) {
    548             if (mScanCount.containsKey(uid)) {
    549                 count = mScanCount.get(uid);
    550             }
    551             mScanCount.put(uid, ++count);
    552         }
    553         mWifiStateMachine.startScan(forceActive);
    554     }
    555 
    556     private void enforceAccessPermission() {
    557         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_WIFI_STATE,
    558                                                 "WifiService");
    559     }
    560 
    561     private void enforceChangePermission() {
    562         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CHANGE_WIFI_STATE,
    563                                                 "WifiService");
    564 
    565     }
    566 
    567     private void enforceMulticastChangePermission() {
    568         mContext.enforceCallingOrSelfPermission(
    569                 android.Manifest.permission.CHANGE_WIFI_MULTICAST_STATE,
    570                 "WifiService");
    571     }
    572 
    573     /**
    574      * see {@link android.net.wifi.WifiManager#setWifiEnabled(boolean)}
    575      * @param enable {@code true} to enable, {@code false} to disable.
    576      * @return {@code true} if the enable/disable operation was
    577      *         started or is already in the queue.
    578      */
    579     public synchronized boolean setWifiEnabled(boolean enable) {
    580         enforceChangePermission();
    581         Slog.d(TAG, "setWifiEnabled: " + enable + " pid=" + Binder.getCallingPid()
    582                     + ", uid=" + Binder.getCallingUid());
    583         if (DBG) {
    584             Slog.e(TAG, "Invoking mWifiStateMachine.setWifiEnabled\n");
    585         }
    586 
    587         if (enable) {
    588             reportStartWorkSource();
    589         }
    590         mWifiStateMachine.setWifiEnabled(enable);
    591 
    592         /*
    593          * Caller might not have WRITE_SECURE_SETTINGS,
    594          * only CHANGE_WIFI_STATE is enforced
    595          */
    596 
    597         long ident = Binder.clearCallingIdentity();
    598         handleWifiToggled(enable);
    599         Binder.restoreCallingIdentity(ident);
    600 
    601         if (enable) {
    602             if (!mIsReceiverRegistered) {
    603                 registerForBroadcasts();
    604                 mIsReceiverRegistered = true;
    605             }
    606         } else if (mIsReceiverRegistered) {
    607             mContext.unregisterReceiver(mReceiver);
    608             mIsReceiverRegistered = false;
    609         }
    610 
    611         return true;
    612     }
    613 
    614     /**
    615      * see {@link WifiManager#getWifiState()}
    616      * @return One of {@link WifiManager#WIFI_STATE_DISABLED},
    617      *         {@link WifiManager#WIFI_STATE_DISABLING},
    618      *         {@link WifiManager#WIFI_STATE_ENABLED},
    619      *         {@link WifiManager#WIFI_STATE_ENABLING},
    620      *         {@link WifiManager#WIFI_STATE_UNKNOWN}
    621      */
    622     public int getWifiEnabledState() {
    623         enforceAccessPermission();
    624         return mWifiStateMachine.syncGetWifiState();
    625     }
    626 
    627     /**
    628      * see {@link android.net.wifi.WifiManager#setWifiApEnabled(WifiConfiguration, boolean)}
    629      * @param wifiConfig SSID, security and channel details as
    630      *        part of WifiConfiguration
    631      * @param enabled true to enable and false to disable
    632      */
    633     public void setWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled) {
    634         enforceChangePermission();
    635         mWifiStateMachine.setWifiApEnabled(wifiConfig, enabled);
    636     }
    637 
    638     /**
    639      * see {@link WifiManager#getWifiApState()}
    640      * @return One of {@link WifiManager#WIFI_AP_STATE_DISABLED},
    641      *         {@link WifiManager#WIFI_AP_STATE_DISABLING},
    642      *         {@link WifiManager#WIFI_AP_STATE_ENABLED},
    643      *         {@link WifiManager#WIFI_AP_STATE_ENABLING},
    644      *         {@link WifiManager#WIFI_AP_STATE_FAILED}
    645      */
    646     public int getWifiApEnabledState() {
    647         enforceAccessPermission();
    648         return mWifiStateMachine.syncGetWifiApState();
    649     }
    650 
    651     /**
    652      * see {@link WifiManager#getWifiApConfiguration()}
    653      * @return soft access point configuration
    654      */
    655     public WifiConfiguration getWifiApConfiguration() {
    656         enforceAccessPermission();
    657         return mWifiStateMachine.syncGetWifiApConfiguration();
    658     }
    659 
    660     /**
    661      * see {@link WifiManager#setWifiApConfiguration(WifiConfiguration)}
    662      * @param wifiConfig WifiConfiguration details for soft access point
    663      */
    664     public void setWifiApConfiguration(WifiConfiguration wifiConfig) {
    665         enforceChangePermission();
    666         if (wifiConfig == null)
    667             return;
    668         mWifiStateMachine.setWifiApConfiguration(wifiConfig);
    669     }
    670 
    671     /**
    672      * see {@link android.net.wifi.WifiManager#disconnect()}
    673      */
    674     public void disconnect() {
    675         enforceChangePermission();
    676         mWifiStateMachine.disconnectCommand();
    677     }
    678 
    679     /**
    680      * see {@link android.net.wifi.WifiManager#reconnect()}
    681      */
    682     public void reconnect() {
    683         enforceChangePermission();
    684         mWifiStateMachine.reconnectCommand();
    685     }
    686 
    687     /**
    688      * see {@link android.net.wifi.WifiManager#reassociate()}
    689      */
    690     public void reassociate() {
    691         enforceChangePermission();
    692         mWifiStateMachine.reassociateCommand();
    693     }
    694 
    695     /**
    696      * see {@link android.net.wifi.WifiManager#getConfiguredNetworks()}
    697      * @return the list of configured networks
    698      */
    699     public List<WifiConfiguration> getConfiguredNetworks() {
    700         enforceAccessPermission();
    701         if (mWifiStateMachineChannel != null) {
    702             return mWifiStateMachine.syncGetConfiguredNetworks(mWifiStateMachineChannel);
    703         } else {
    704             Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
    705             return null;
    706         }
    707     }
    708 
    709     /**
    710      * see {@link android.net.wifi.WifiManager#addOrUpdateNetwork(WifiConfiguration)}
    711      * @return the supplicant-assigned identifier for the new or updated
    712      * network if the operation succeeds, or {@code -1} if it fails
    713      */
    714     public int addOrUpdateNetwork(WifiConfiguration config) {
    715         enforceChangePermission();
    716         if (mWifiStateMachineChannel != null) {
    717             return mWifiStateMachine.syncAddOrUpdateNetwork(mWifiStateMachineChannel, config);
    718         } else {
    719             Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
    720             return -1;
    721         }
    722     }
    723 
    724      /**
    725      * See {@link android.net.wifi.WifiManager#removeNetwork(int)}
    726      * @param netId the integer that identifies the network configuration
    727      * to the supplicant
    728      * @return {@code true} if the operation succeeded
    729      */
    730     public boolean removeNetwork(int netId) {
    731         enforceChangePermission();
    732         if (mWifiStateMachineChannel != null) {
    733             return mWifiStateMachine.syncRemoveNetwork(mWifiStateMachineChannel, netId);
    734         } else {
    735             Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
    736             return false;
    737         }
    738     }
    739 
    740     /**
    741      * See {@link android.net.wifi.WifiManager#enableNetwork(int, boolean)}
    742      * @param netId the integer that identifies the network configuration
    743      * to the supplicant
    744      * @param disableOthers if true, disable all other networks.
    745      * @return {@code true} if the operation succeeded
    746      */
    747     public boolean enableNetwork(int netId, boolean disableOthers) {
    748         enforceChangePermission();
    749         if (mWifiStateMachineChannel != null) {
    750             return mWifiStateMachine.syncEnableNetwork(mWifiStateMachineChannel, netId,
    751                     disableOthers);
    752         } else {
    753             Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
    754             return false;
    755         }
    756     }
    757 
    758     /**
    759      * See {@link android.net.wifi.WifiManager#disableNetwork(int)}
    760      * @param netId the integer that identifies the network configuration
    761      * to the supplicant
    762      * @return {@code true} if the operation succeeded
    763      */
    764     public boolean disableNetwork(int netId) {
    765         enforceChangePermission();
    766         if (mWifiStateMachineChannel != null) {
    767             return mWifiStateMachine.syncDisableNetwork(mWifiStateMachineChannel, netId);
    768         } else {
    769             Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
    770             return false;
    771         }
    772     }
    773 
    774     /**
    775      * See {@link android.net.wifi.WifiManager#getConnectionInfo()}
    776      * @return the Wi-Fi information, contained in {@link WifiInfo}.
    777      */
    778     public WifiInfo getConnectionInfo() {
    779         enforceAccessPermission();
    780         /*
    781          * Make sure we have the latest information, by sending
    782          * a status request to the supplicant.
    783          */
    784         return mWifiStateMachine.syncRequestConnectionInfo();
    785     }
    786 
    787     /**
    788      * Return the results of the most recent access point scan, in the form of
    789      * a list of {@link ScanResult} objects.
    790      * @return the list of results
    791      */
    792     public List<ScanResult> getScanResults() {
    793         enforceAccessPermission();
    794         return mWifiStateMachine.syncGetScanResultsList();
    795     }
    796 
    797     /**
    798      * Tell the supplicant to persist the current list of configured networks.
    799      * @return {@code true} if the operation succeeded
    800      *
    801      * TODO: deprecate this
    802      */
    803     public boolean saveConfiguration() {
    804         boolean result = true;
    805         enforceChangePermission();
    806         if (mWifiStateMachineChannel != null) {
    807             return mWifiStateMachine.syncSaveConfig(mWifiStateMachineChannel);
    808         } else {
    809             Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
    810             return false;
    811         }
    812     }
    813 
    814     /**
    815      * Set the country code
    816      * @param countryCode ISO 3166 country code.
    817      * @param persist {@code true} if the setting should be remembered.
    818      *
    819      * The persist behavior exists so that wifi can fall back to the last
    820      * persisted country code on a restart, when the locale information is
    821      * not available from telephony.
    822      */
    823     public void setCountryCode(String countryCode, boolean persist) {
    824         Slog.i(TAG, "WifiService trying to set country code to " + countryCode +
    825                 " with persist set to " + persist);
    826         enforceChangePermission();
    827         mWifiStateMachine.setCountryCode(countryCode, persist);
    828     }
    829 
    830     /**
    831      * Set the operational frequency band
    832      * @param band One of
    833      *     {@link WifiManager#WIFI_FREQUENCY_BAND_AUTO},
    834      *     {@link WifiManager#WIFI_FREQUENCY_BAND_5GHZ},
    835      *     {@link WifiManager#WIFI_FREQUENCY_BAND_2GHZ},
    836      * @param persist {@code true} if the setting should be remembered.
    837      *
    838      */
    839     public void setFrequencyBand(int band, boolean persist) {
    840         enforceChangePermission();
    841         if (!isDualBandSupported()) return;
    842         Slog.i(TAG, "WifiService trying to set frequency band to " + band +
    843                 " with persist set to " + persist);
    844         mWifiStateMachine.setFrequencyBand(band, persist);
    845     }
    846 
    847 
    848     /**
    849      * Get the operational frequency band
    850      */
    851     public int getFrequencyBand() {
    852         enforceAccessPermission();
    853         return mWifiStateMachine.getFrequencyBand();
    854     }
    855 
    856     public boolean isDualBandSupported() {
    857         //TODO: Should move towards adding a driver API that checks at runtime
    858         return mContext.getResources().getBoolean(
    859                 com.android.internal.R.bool.config_wifi_dual_band_support);
    860     }
    861 
    862     /**
    863      * Return the DHCP-assigned addresses from the last successful DHCP request,
    864      * if any.
    865      * @return the DHCP information
    866      */
    867     public DhcpInfo getDhcpInfo() {
    868         enforceAccessPermission();
    869         return mWifiStateMachine.syncGetDhcpInfo();
    870     }
    871 
    872     /**
    873      * see {@link android.net.wifi.WifiManager#startWifi}
    874      *
    875      */
    876     public void startWifi() {
    877         enforceChangePermission();
    878         /* TODO: may be add permissions for access only to connectivity service
    879          * TODO: if a start issued, keep wifi alive until a stop issued irrespective
    880          * of WifiLock & device idle status unless wifi enabled status is toggled
    881          */
    882 
    883         mWifiStateMachine.setDriverStart(true, mEmergencyCallbackMode);
    884         mWifiStateMachine.reconnectCommand();
    885     }
    886 
    887     /**
    888      * see {@link android.net.wifi.WifiManager#stopWifi}
    889      *
    890      */
    891     public void stopWifi() {
    892         enforceChangePermission();
    893         /* TODO: may be add permissions for access only to connectivity service
    894          * TODO: if a stop is issued, wifi is brought up only by startWifi
    895          * unless wifi enabled status is toggled
    896          */
    897         mWifiStateMachine.setDriverStart(false, mEmergencyCallbackMode);
    898     }
    899 
    900 
    901     /**
    902      * see {@link android.net.wifi.WifiManager#addToBlacklist}
    903      *
    904      */
    905     public void addToBlacklist(String bssid) {
    906         enforceChangePermission();
    907 
    908         mWifiStateMachine.addToBlacklist(bssid);
    909     }
    910 
    911     /**
    912      * see {@link android.net.wifi.WifiManager#clearBlacklist}
    913      *
    914      */
    915     public void clearBlacklist() {
    916         enforceChangePermission();
    917 
    918         mWifiStateMachine.clearBlacklist();
    919     }
    920 
    921     /**
    922      * Get a reference to handler. This is used by a client to establish
    923      * an AsyncChannel communication with WifiService
    924      */
    925     public Messenger getWifiServiceMessenger() {
    926         /* Enforce the highest permissions
    927            TODO: when we consider exposing the asynchronous API, think about
    928                  how to provide both access and change permissions seperately
    929          */
    930         enforceAccessPermission();
    931         enforceChangePermission();
    932         return new Messenger(mAsyncServiceHandler);
    933     }
    934 
    935     /** Get a reference to WifiStateMachine handler for AsyncChannel communication */
    936     public Messenger getWifiStateMachineMessenger() {
    937         enforceAccessPermission();
    938         enforceChangePermission();
    939         return mWifiStateMachine.getMessenger();
    940     }
    941 
    942     /**
    943      * Get the IP and proxy configuration file
    944      */
    945     public String getConfigFile() {
    946         enforceAccessPermission();
    947         return mWifiStateMachine.getConfigFile();
    948     }
    949 
    950     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
    951         @Override
    952         public void onReceive(Context context, Intent intent) {
    953             String action = intent.getAction();
    954 
    955             long idleMillis =
    956                 Settings.Secure.getLong(mContext.getContentResolver(),
    957                                         Settings.Secure.WIFI_IDLE_MS, DEFAULT_IDLE_MS);
    958             int stayAwakeConditions =
    959                 Settings.System.getInt(mContext.getContentResolver(),
    960                                        Settings.System.STAY_ON_WHILE_PLUGGED_IN, 0);
    961             if (action.equals(Intent.ACTION_SCREEN_ON)) {
    962                 if (DBG) {
    963                     Slog.d(TAG, "ACTION_SCREEN_ON");
    964                 }
    965                 mAlarmManager.cancel(mIdleIntent);
    966                 mScreenOff = false;
    967                 evaluateTrafficStatsPolling();
    968                 setDeviceIdleAndUpdateWifi(false);
    969             } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
    970                 if (DBG) {
    971                     Slog.d(TAG, "ACTION_SCREEN_OFF");
    972                 }
    973                 mScreenOff = true;
    974                 evaluateTrafficStatsPolling();
    975                 /*
    976                  * Set a timer to put Wi-Fi to sleep, but only if the screen is off
    977                  * AND the "stay on while plugged in" setting doesn't match the
    978                  * current power conditions (i.e, not plugged in, plugged in to USB,
    979                  * or plugged in to AC).
    980                  */
    981                 if (!shouldWifiStayAwake(stayAwakeConditions, mPluggedType)) {
    982                     //Delayed shutdown if wifi is connected
    983                     if (mNetworkInfo.getDetailedState() == DetailedState.CONNECTED) {
    984                         if (DBG) Slog.d(TAG, "setting ACTION_DEVICE_IDLE: " + idleMillis + " ms");
    985                         mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()
    986                                 + idleMillis, mIdleIntent);
    987                     } else {
    988                         setDeviceIdleAndUpdateWifi(true);
    989                     }
    990                 }
    991             } else if (action.equals(ACTION_DEVICE_IDLE)) {
    992                 setDeviceIdleAndUpdateWifi(true);
    993             } else if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
    994                 /*
    995                  * Set a timer to put Wi-Fi to sleep, but only if the screen is off
    996                  * AND we are transitioning from a state in which the device was supposed
    997                  * to stay awake to a state in which it is not supposed to stay awake.
    998                  * If "stay awake" state is not changing, we do nothing, to avoid resetting
    999                  * the already-set timer.
   1000                  */
   1001                 int pluggedType = intent.getIntExtra("plugged", 0);
   1002                 if (DBG) {
   1003                     Slog.d(TAG, "ACTION_BATTERY_CHANGED pluggedType: " + pluggedType);
   1004                 }
   1005                 if (mScreenOff && shouldWifiStayAwake(stayAwakeConditions, mPluggedType) &&
   1006                         !shouldWifiStayAwake(stayAwakeConditions, pluggedType)) {
   1007                     long triggerTime = System.currentTimeMillis() + idleMillis;
   1008                     if (DBG) {
   1009                         Slog.d(TAG, "setting ACTION_DEVICE_IDLE timer for " + idleMillis + "ms");
   1010                     }
   1011                     mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent);
   1012                 }
   1013 
   1014                 //Start scan stats tracking when device unplugged
   1015                 if (pluggedType == 0) {
   1016                     synchronized (mScanCount) {
   1017                         mScanCount.clear();
   1018                     }
   1019                 }
   1020                 mPluggedType = pluggedType;
   1021             } else if (action.equals(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED)) {
   1022                 int state = intent.getIntExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE,
   1023                         BluetoothAdapter.STATE_DISCONNECTED);
   1024                 mWifiStateMachine.sendBluetoothAdapterStateChange(state);
   1025             } else if (action.equals(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED)) {
   1026                 mEmergencyCallbackMode = intent.getBooleanExtra("phoneinECMState", false);
   1027                 updateWifiState();
   1028             }
   1029         }
   1030 
   1031         /**
   1032          * Determines whether the Wi-Fi chipset should stay awake or be put to
   1033          * sleep. Looks at the setting for the sleep policy and the current
   1034          * conditions.
   1035          *
   1036          * @see #shouldDeviceStayAwake(int, int)
   1037          */
   1038         private boolean shouldWifiStayAwake(int stayAwakeConditions, int pluggedType) {
   1039             //Never sleep as long as the user has not changed the settings
   1040             int wifiSleepPolicy = Settings.System.getInt(mContext.getContentResolver(),
   1041                     Settings.System.WIFI_SLEEP_POLICY,
   1042                     Settings.System.WIFI_SLEEP_POLICY_NEVER);
   1043 
   1044             if (wifiSleepPolicy == Settings.System.WIFI_SLEEP_POLICY_NEVER) {
   1045                 // Never sleep
   1046                 return true;
   1047             } else if ((wifiSleepPolicy == Settings.System.WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED) &&
   1048                     (pluggedType != 0)) {
   1049                 // Never sleep while plugged, and we're plugged
   1050                 return true;
   1051             } else {
   1052                 // Default
   1053                 return shouldDeviceStayAwake(stayAwakeConditions, pluggedType);
   1054             }
   1055         }
   1056 
   1057         /**
   1058          * Determine whether the bit value corresponding to {@code pluggedType} is set in
   1059          * the bit string {@code stayAwakeConditions}. Because a {@code pluggedType} value
   1060          * of {@code 0} isn't really a plugged type, but rather an indication that the
   1061          * device isn't plugged in at all, there is no bit value corresponding to a
   1062          * {@code pluggedType} value of {@code 0}. That is why we shift by
   1063          * {@code pluggedType - 1} instead of by {@code pluggedType}.
   1064          * @param stayAwakeConditions a bit string specifying which "plugged types" should
   1065          * keep the device (and hence Wi-Fi) awake.
   1066          * @param pluggedType the type of plug (USB, AC, or none) for which the check is
   1067          * being made
   1068          * @return {@code true} if {@code pluggedType} indicates that the device is
   1069          * supposed to stay awake, {@code false} otherwise.
   1070          */
   1071         private boolean shouldDeviceStayAwake(int stayAwakeConditions, int pluggedType) {
   1072             return (stayAwakeConditions & pluggedType) != 0;
   1073         }
   1074     };
   1075 
   1076     private void setDeviceIdleAndUpdateWifi(boolean deviceIdle) {
   1077         mDeviceIdle = deviceIdle;
   1078         reportStartWorkSource();
   1079         updateWifiState();
   1080     }
   1081 
   1082     private synchronized void reportStartWorkSource() {
   1083         mTmpWorkSource.clear();
   1084         if (mDeviceIdle) {
   1085             for (int i=0; i<mLocks.mList.size(); i++) {
   1086                 mTmpWorkSource.add(mLocks.mList.get(i).mWorkSource);
   1087             }
   1088         }
   1089         mWifiStateMachine.updateBatteryWorkSource(mTmpWorkSource);
   1090     }
   1091 
   1092     private void updateWifiState() {
   1093         boolean lockHeld = mLocks.hasLocks();
   1094         int strongestLockMode = WifiManager.WIFI_MODE_FULL;
   1095         boolean wifiShouldBeStarted;
   1096 
   1097         if (mEmergencyCallbackMode) {
   1098             wifiShouldBeStarted = false;
   1099         } else {
   1100             wifiShouldBeStarted = !mDeviceIdle || lockHeld;
   1101         }
   1102 
   1103         if (lockHeld) {
   1104             strongestLockMode = mLocks.getStrongestLockMode();
   1105         }
   1106         /* If device is not idle, lockmode cannot be scan only */
   1107         if (!mDeviceIdle && strongestLockMode == WifiManager.WIFI_MODE_SCAN_ONLY) {
   1108             strongestLockMode = WifiManager.WIFI_MODE_FULL;
   1109         }
   1110 
   1111         /* Disable tethering when airplane mode is enabled */
   1112         if (mAirplaneModeOn.get()) {
   1113             mWifiStateMachine.setWifiApEnabled(null, false);
   1114         }
   1115 
   1116         if (shouldWifiBeEnabled()) {
   1117             if (wifiShouldBeStarted) {
   1118                 reportStartWorkSource();
   1119                 mWifiStateMachine.setWifiEnabled(true);
   1120                 mWifiStateMachine.setScanOnlyMode(
   1121                         strongestLockMode == WifiManager.WIFI_MODE_SCAN_ONLY);
   1122                 mWifiStateMachine.setDriverStart(true, mEmergencyCallbackMode);
   1123                 mWifiStateMachine.setHighPerfModeEnabled(strongestLockMode
   1124                         == WifiManager.WIFI_MODE_FULL_HIGH_PERF);
   1125             } else {
   1126                 mWifiStateMachine.setDriverStart(false, mEmergencyCallbackMode);
   1127             }
   1128         } else {
   1129             mWifiStateMachine.setWifiEnabled(false);
   1130         }
   1131     }
   1132 
   1133     private void registerForBroadcasts() {
   1134         IntentFilter intentFilter = new IntentFilter();
   1135         intentFilter.addAction(Intent.ACTION_SCREEN_ON);
   1136         intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
   1137         intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
   1138         intentFilter.addAction(ACTION_DEVICE_IDLE);
   1139         intentFilter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
   1140         intentFilter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
   1141         mContext.registerReceiver(mReceiver, intentFilter);
   1142     }
   1143 
   1144     private boolean isAirplaneSensitive() {
   1145         String airplaneModeRadios = Settings.System.getString(mContext.getContentResolver(),
   1146                 Settings.System.AIRPLANE_MODE_RADIOS);
   1147         return airplaneModeRadios == null
   1148             || airplaneModeRadios.contains(Settings.System.RADIO_WIFI);
   1149     }
   1150 
   1151     private boolean isAirplaneToggleable() {
   1152         String toggleableRadios = Settings.System.getString(mContext.getContentResolver(),
   1153                 Settings.System.AIRPLANE_MODE_TOGGLEABLE_RADIOS);
   1154         return toggleableRadios != null
   1155             && toggleableRadios.contains(Settings.System.RADIO_WIFI);
   1156     }
   1157 
   1158     /**
   1159      * Returns true if Wi-Fi is sensitive to airplane mode, and airplane mode is
   1160      * currently on.
   1161      * @return {@code true} if airplane mode is on.
   1162      */
   1163     private boolean isAirplaneModeOn() {
   1164         return isAirplaneSensitive() && Settings.System.getInt(mContext.getContentResolver(),
   1165                 Settings.System.AIRPLANE_MODE_ON, 0) == 1;
   1166     }
   1167 
   1168     @Override
   1169     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
   1170         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
   1171                 != PackageManager.PERMISSION_GRANTED) {
   1172             pw.println("Permission Denial: can't dump WifiService from from pid="
   1173                     + Binder.getCallingPid()
   1174                     + ", uid=" + Binder.getCallingUid());
   1175             return;
   1176         }
   1177         pw.println("Wi-Fi is " + mWifiStateMachine.syncGetWifiStateByName());
   1178         pw.println("Stay-awake conditions: " +
   1179                 Settings.System.getInt(mContext.getContentResolver(),
   1180                                        Settings.System.STAY_ON_WHILE_PLUGGED_IN, 0));
   1181         pw.println();
   1182 
   1183         pw.println("Internal state:");
   1184         pw.println(mWifiStateMachine);
   1185         pw.println();
   1186         pw.println("Latest scan results:");
   1187         List<ScanResult> scanResults = mWifiStateMachine.syncGetScanResultsList();
   1188         if (scanResults != null && scanResults.size() != 0) {
   1189             pw.println("  BSSID              Frequency   RSSI  Flags             SSID");
   1190             for (ScanResult r : scanResults) {
   1191                 pw.printf("  %17s  %9d  %5d  %-16s  %s%n",
   1192                                          r.BSSID,
   1193                                          r.frequency,
   1194                                          r.level,
   1195                                          r.capabilities,
   1196                                          r.SSID == null ? "" : r.SSID);
   1197             }
   1198         }
   1199         pw.println();
   1200         pw.println("Locks acquired: " + mFullLocksAcquired + " full, " +
   1201                 mFullHighPerfLocksAcquired + " full high perf, " +
   1202                 mScanLocksAcquired + " scan");
   1203         pw.println("Locks released: " + mFullLocksReleased + " full, " +
   1204                 mFullHighPerfLocksReleased + " full high perf, " +
   1205                 mScanLocksReleased + " scan");
   1206         pw.println();
   1207         pw.println("Locks held:");
   1208         mLocks.dump(pw);
   1209 
   1210         pw.println("Scan count since last plugged in");
   1211         synchronized (mScanCount) {
   1212             for(int sc : mScanCount.keySet()) {
   1213                 pw.println("UID: " + sc + " Scan count: " + mScanCount.get(sc));
   1214             }
   1215         }
   1216 
   1217         pw.println();
   1218         pw.println("WifiWatchdogStateMachine dump");
   1219         mWifiWatchdogStateMachine.dump(pw);
   1220         pw.println("WifiStateMachine dump");
   1221         mWifiStateMachine.dump(fd, pw, args);
   1222     }
   1223 
   1224     private class WifiLock extends DeathRecipient {
   1225         WifiLock(int lockMode, String tag, IBinder binder, WorkSource ws) {
   1226             super(lockMode, tag, binder, ws);
   1227         }
   1228 
   1229         public void binderDied() {
   1230             synchronized (mLocks) {
   1231                 releaseWifiLockLocked(mBinder);
   1232             }
   1233         }
   1234 
   1235         public String toString() {
   1236             return "WifiLock{" + mTag + " type=" + mMode + " binder=" + mBinder + "}";
   1237         }
   1238     }
   1239 
   1240     private class LockList {
   1241         private List<WifiLock> mList;
   1242 
   1243         private LockList() {
   1244             mList = new ArrayList<WifiLock>();
   1245         }
   1246 
   1247         private synchronized boolean hasLocks() {
   1248             return !mList.isEmpty();
   1249         }
   1250 
   1251         private synchronized int getStrongestLockMode() {
   1252             if (mList.isEmpty()) {
   1253                 return WifiManager.WIFI_MODE_FULL;
   1254             }
   1255 
   1256             if (mFullHighPerfLocksAcquired > mFullHighPerfLocksReleased) {
   1257                 return WifiManager.WIFI_MODE_FULL_HIGH_PERF;
   1258             }
   1259 
   1260             if (mFullLocksAcquired > mFullLocksReleased) {
   1261                 return WifiManager.WIFI_MODE_FULL;
   1262             }
   1263 
   1264             return WifiManager.WIFI_MODE_SCAN_ONLY;
   1265         }
   1266 
   1267         private void addLock(WifiLock lock) {
   1268             if (findLockByBinder(lock.mBinder) < 0) {
   1269                 mList.add(lock);
   1270             }
   1271         }
   1272 
   1273         private WifiLock removeLock(IBinder binder) {
   1274             int index = findLockByBinder(binder);
   1275             if (index >= 0) {
   1276                 WifiLock ret = mList.remove(index);
   1277                 ret.unlinkDeathRecipient();
   1278                 return ret;
   1279             } else {
   1280                 return null;
   1281             }
   1282         }
   1283 
   1284         private int findLockByBinder(IBinder binder) {
   1285             int size = mList.size();
   1286             for (int i = size - 1; i >= 0; i--)
   1287                 if (mList.get(i).mBinder == binder)
   1288                     return i;
   1289             return -1;
   1290         }
   1291 
   1292         private void dump(PrintWriter pw) {
   1293             for (WifiLock l : mList) {
   1294                 pw.print("    ");
   1295                 pw.println(l);
   1296             }
   1297         }
   1298     }
   1299 
   1300     void enforceWakeSourcePermission(int uid, int pid) {
   1301         if (uid == android.os.Process.myUid()) {
   1302             return;
   1303         }
   1304         mContext.enforcePermission(android.Manifest.permission.UPDATE_DEVICE_STATS,
   1305                 pid, uid, null);
   1306     }
   1307 
   1308     public boolean acquireWifiLock(IBinder binder, int lockMode, String tag, WorkSource ws) {
   1309         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
   1310         if (lockMode != WifiManager.WIFI_MODE_FULL &&
   1311                 lockMode != WifiManager.WIFI_MODE_SCAN_ONLY &&
   1312                 lockMode != WifiManager.WIFI_MODE_FULL_HIGH_PERF) {
   1313             Slog.e(TAG, "Illegal argument, lockMode= " + lockMode);
   1314             if (DBG) throw new IllegalArgumentException("lockMode=" + lockMode);
   1315             return false;
   1316         }
   1317         if (ws != null && ws.size() == 0) {
   1318             ws = null;
   1319         }
   1320         if (ws != null) {
   1321             enforceWakeSourcePermission(Binder.getCallingUid(), Binder.getCallingPid());
   1322         }
   1323         if (ws == null) {
   1324             ws = new WorkSource(Binder.getCallingUid());
   1325         }
   1326         WifiLock wifiLock = new WifiLock(lockMode, tag, binder, ws);
   1327         synchronized (mLocks) {
   1328             return acquireWifiLockLocked(wifiLock);
   1329         }
   1330     }
   1331 
   1332     private void noteAcquireWifiLock(WifiLock wifiLock) throws RemoteException {
   1333         switch(wifiLock.mMode) {
   1334             case WifiManager.WIFI_MODE_FULL:
   1335             case WifiManager.WIFI_MODE_FULL_HIGH_PERF:
   1336                 mBatteryStats.noteFullWifiLockAcquiredFromSource(wifiLock.mWorkSource);
   1337                 break;
   1338             case WifiManager.WIFI_MODE_SCAN_ONLY:
   1339                 mBatteryStats.noteScanWifiLockAcquiredFromSource(wifiLock.mWorkSource);
   1340                 break;
   1341         }
   1342     }
   1343 
   1344     private void noteReleaseWifiLock(WifiLock wifiLock) throws RemoteException {
   1345         switch(wifiLock.mMode) {
   1346             case WifiManager.WIFI_MODE_FULL:
   1347             case WifiManager.WIFI_MODE_FULL_HIGH_PERF:
   1348                 mBatteryStats.noteFullWifiLockReleasedFromSource(wifiLock.mWorkSource);
   1349                 break;
   1350             case WifiManager.WIFI_MODE_SCAN_ONLY:
   1351                 mBatteryStats.noteScanWifiLockReleasedFromSource(wifiLock.mWorkSource);
   1352                 break;
   1353         }
   1354     }
   1355 
   1356     private boolean acquireWifiLockLocked(WifiLock wifiLock) {
   1357         if (DBG) Slog.d(TAG, "acquireWifiLockLocked: " + wifiLock);
   1358 
   1359         mLocks.addLock(wifiLock);
   1360 
   1361         long ident = Binder.clearCallingIdentity();
   1362         try {
   1363             noteAcquireWifiLock(wifiLock);
   1364             switch(wifiLock.mMode) {
   1365             case WifiManager.WIFI_MODE_FULL:
   1366                 ++mFullLocksAcquired;
   1367                 break;
   1368             case WifiManager.WIFI_MODE_FULL_HIGH_PERF:
   1369                 ++mFullHighPerfLocksAcquired;
   1370                 break;
   1371 
   1372             case WifiManager.WIFI_MODE_SCAN_ONLY:
   1373                 ++mScanLocksAcquired;
   1374                 break;
   1375             }
   1376 
   1377             // Be aggressive about adding new locks into the accounted state...
   1378             // we want to over-report rather than under-report.
   1379             reportStartWorkSource();
   1380 
   1381             updateWifiState();
   1382             return true;
   1383         } catch (RemoteException e) {
   1384             return false;
   1385         } finally {
   1386             Binder.restoreCallingIdentity(ident);
   1387         }
   1388     }
   1389 
   1390     public void updateWifiLockWorkSource(IBinder lock, WorkSource ws) {
   1391         int uid = Binder.getCallingUid();
   1392         int pid = Binder.getCallingPid();
   1393         if (ws != null && ws.size() == 0) {
   1394             ws = null;
   1395         }
   1396         if (ws != null) {
   1397             enforceWakeSourcePermission(uid, pid);
   1398         }
   1399         long ident = Binder.clearCallingIdentity();
   1400         try {
   1401             synchronized (mLocks) {
   1402                 int index = mLocks.findLockByBinder(lock);
   1403                 if (index < 0) {
   1404                     throw new IllegalArgumentException("Wifi lock not active");
   1405                 }
   1406                 WifiLock wl = mLocks.mList.get(index);
   1407                 noteReleaseWifiLock(wl);
   1408                 wl.mWorkSource = ws != null ? new WorkSource(ws) : new WorkSource(uid);
   1409                 noteAcquireWifiLock(wl);
   1410             }
   1411         } catch (RemoteException e) {
   1412         } finally {
   1413             Binder.restoreCallingIdentity(ident);
   1414         }
   1415     }
   1416 
   1417     public boolean releaseWifiLock(IBinder lock) {
   1418         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
   1419         synchronized (mLocks) {
   1420             return releaseWifiLockLocked(lock);
   1421         }
   1422     }
   1423 
   1424     private boolean releaseWifiLockLocked(IBinder lock) {
   1425         boolean hadLock;
   1426 
   1427         WifiLock wifiLock = mLocks.removeLock(lock);
   1428 
   1429         if (DBG) Slog.d(TAG, "releaseWifiLockLocked: " + wifiLock);
   1430 
   1431         hadLock = (wifiLock != null);
   1432 
   1433         long ident = Binder.clearCallingIdentity();
   1434         try {
   1435             if (hadLock) {
   1436                 noteReleaseWifiLock(wifiLock);
   1437                 switch(wifiLock.mMode) {
   1438                     case WifiManager.WIFI_MODE_FULL:
   1439                         ++mFullLocksReleased;
   1440                         break;
   1441                     case WifiManager.WIFI_MODE_FULL_HIGH_PERF:
   1442                         ++mFullHighPerfLocksReleased;
   1443                         break;
   1444                     case WifiManager.WIFI_MODE_SCAN_ONLY:
   1445                         ++mScanLocksReleased;
   1446                         break;
   1447                 }
   1448             }
   1449 
   1450             // TODO - should this only happen if you hadLock?
   1451             updateWifiState();
   1452 
   1453         } catch (RemoteException e) {
   1454         } finally {
   1455             Binder.restoreCallingIdentity(ident);
   1456         }
   1457 
   1458         return hadLock;
   1459     }
   1460 
   1461     private abstract class DeathRecipient
   1462             implements IBinder.DeathRecipient {
   1463         String mTag;
   1464         int mMode;
   1465         IBinder mBinder;
   1466         WorkSource mWorkSource;
   1467 
   1468         DeathRecipient(int mode, String tag, IBinder binder, WorkSource ws) {
   1469             super();
   1470             mTag = tag;
   1471             mMode = mode;
   1472             mBinder = binder;
   1473             mWorkSource = ws;
   1474             try {
   1475                 mBinder.linkToDeath(this, 0);
   1476             } catch (RemoteException e) {
   1477                 binderDied();
   1478             }
   1479         }
   1480 
   1481         void unlinkDeathRecipient() {
   1482             mBinder.unlinkToDeath(this, 0);
   1483         }
   1484     }
   1485 
   1486     private class Multicaster extends DeathRecipient {
   1487         Multicaster(String tag, IBinder binder) {
   1488             super(Binder.getCallingUid(), tag, binder, null);
   1489         }
   1490 
   1491         public void binderDied() {
   1492             Slog.e(TAG, "Multicaster binderDied");
   1493             synchronized (mMulticasters) {
   1494                 int i = mMulticasters.indexOf(this);
   1495                 if (i != -1) {
   1496                     removeMulticasterLocked(i, mMode);
   1497                 }
   1498             }
   1499         }
   1500 
   1501         public String toString() {
   1502             return "Multicaster{" + mTag + " binder=" + mBinder + "}";
   1503         }
   1504 
   1505         public int getUid() {
   1506             return mMode;
   1507         }
   1508     }
   1509 
   1510     public void initializeMulticastFiltering() {
   1511         enforceMulticastChangePermission();
   1512 
   1513         synchronized (mMulticasters) {
   1514             // if anybody had requested filters be off, leave off
   1515             if (mMulticasters.size() != 0) {
   1516                 return;
   1517             } else {
   1518                 mWifiStateMachine.startFilteringMulticastV4Packets();
   1519             }
   1520         }
   1521     }
   1522 
   1523     public void acquireMulticastLock(IBinder binder, String tag) {
   1524         enforceMulticastChangePermission();
   1525 
   1526         synchronized (mMulticasters) {
   1527             mMulticastEnabled++;
   1528             mMulticasters.add(new Multicaster(tag, binder));
   1529             // Note that we could call stopFilteringMulticastV4Packets only when
   1530             // our new size == 1 (first call), but this function won't
   1531             // be called often and by making the stopPacket call each
   1532             // time we're less fragile and self-healing.
   1533             mWifiStateMachine.stopFilteringMulticastV4Packets();
   1534         }
   1535 
   1536         int uid = Binder.getCallingUid();
   1537         Long ident = Binder.clearCallingIdentity();
   1538         try {
   1539             mBatteryStats.noteWifiMulticastEnabled(uid);
   1540         } catch (RemoteException e) {
   1541         } finally {
   1542             Binder.restoreCallingIdentity(ident);
   1543         }
   1544     }
   1545 
   1546     public void releaseMulticastLock() {
   1547         enforceMulticastChangePermission();
   1548 
   1549         int uid = Binder.getCallingUid();
   1550         synchronized (mMulticasters) {
   1551             mMulticastDisabled++;
   1552             int size = mMulticasters.size();
   1553             for (int i = size - 1; i >= 0; i--) {
   1554                 Multicaster m = mMulticasters.get(i);
   1555                 if ((m != null) && (m.getUid() == uid)) {
   1556                     removeMulticasterLocked(i, uid);
   1557                 }
   1558             }
   1559         }
   1560     }
   1561 
   1562     private void removeMulticasterLocked(int i, int uid)
   1563     {
   1564         Multicaster removed = mMulticasters.remove(i);
   1565 
   1566         if (removed != null) {
   1567             removed.unlinkDeathRecipient();
   1568         }
   1569         if (mMulticasters.size() == 0) {
   1570             mWifiStateMachine.startFilteringMulticastV4Packets();
   1571         }
   1572 
   1573         Long ident = Binder.clearCallingIdentity();
   1574         try {
   1575             mBatteryStats.noteWifiMulticastDisabled(uid);
   1576         } catch (RemoteException e) {
   1577         } finally {
   1578             Binder.restoreCallingIdentity(ident);
   1579         }
   1580     }
   1581 
   1582     public boolean isMulticastEnabled() {
   1583         enforceAccessPermission();
   1584 
   1585         synchronized (mMulticasters) {
   1586             return (mMulticasters.size() > 0);
   1587         }
   1588     }
   1589 
   1590     /**
   1591      * Evaluate if traffic stats polling is needed based on
   1592      * connection and screen on status
   1593      */
   1594     private void evaluateTrafficStatsPolling() {
   1595         Message msg;
   1596         if (mNetworkInfo.getDetailedState() == DetailedState.CONNECTED && !mScreenOff) {
   1597             msg = Message.obtain(mAsyncServiceHandler,
   1598                     WifiManager.ENABLE_TRAFFIC_STATS_POLL, 1, 0);
   1599         } else {
   1600             msg = Message.obtain(mAsyncServiceHandler,
   1601                     WifiManager.ENABLE_TRAFFIC_STATS_POLL, 0, 0);
   1602         }
   1603         msg.sendToTarget();
   1604     }
   1605 
   1606     private void notifyOnDataActivity() {
   1607         long sent, received;
   1608         long preTxPkts = mTxPkts, preRxPkts = mRxPkts;
   1609         int dataActivity = WifiManager.DATA_ACTIVITY_NONE;
   1610 
   1611         mTxPkts = TrafficStats.getTxPackets(mInterfaceName);
   1612         mRxPkts = TrafficStats.getRxPackets(mInterfaceName);
   1613 
   1614         if (preTxPkts > 0 || preRxPkts > 0) {
   1615             sent = mTxPkts - preTxPkts;
   1616             received = mRxPkts - preRxPkts;
   1617             if (sent > 0) {
   1618                 dataActivity |= WifiManager.DATA_ACTIVITY_OUT;
   1619             }
   1620             if (received > 0) {
   1621                 dataActivity |= WifiManager.DATA_ACTIVITY_IN;
   1622             }
   1623 
   1624             if (dataActivity != mDataActivity && !mScreenOff) {
   1625                 mDataActivity = dataActivity;
   1626                 for (AsyncChannel client : mClients) {
   1627                     client.sendMessage(WifiManager.DATA_ACTIVITY_NOTIFICATION, mDataActivity);
   1628                 }
   1629             }
   1630         }
   1631     }
   1632 
   1633 
   1634     private void checkAndSetNotification() {
   1635         // If we shouldn't place a notification on available networks, then
   1636         // don't bother doing any of the following
   1637         if (!mNotificationEnabled) return;
   1638 
   1639         State state = mNetworkInfo.getState();
   1640         if ((state == NetworkInfo.State.DISCONNECTED)
   1641                 || (state == NetworkInfo.State.UNKNOWN)) {
   1642             // Look for an open network
   1643             List<ScanResult> scanResults = mWifiStateMachine.syncGetScanResultsList();
   1644             if (scanResults != null) {
   1645                 int numOpenNetworks = 0;
   1646                 for (int i = scanResults.size() - 1; i >= 0; i--) {
   1647                     ScanResult scanResult = scanResults.get(i);
   1648 
   1649                     //A capability of [ESS] represents an open access point
   1650                     //that is available for an STA to connect
   1651                     if (scanResult.capabilities != null &&
   1652                             scanResult.capabilities.equals("[ESS]")) {
   1653                         numOpenNetworks++;
   1654                     }
   1655                 }
   1656 
   1657                 if (numOpenNetworks > 0) {
   1658                     if (++mNumScansSinceNetworkStateChange >= NUM_SCANS_BEFORE_ACTUALLY_SCANNING) {
   1659                         /*
   1660                          * We've scanned continuously at least
   1661                          * NUM_SCANS_BEFORE_NOTIFICATION times. The user
   1662                          * probably does not have a remembered network in range,
   1663                          * since otherwise supplicant would have tried to
   1664                          * associate and thus resetting this counter.
   1665                          */
   1666                         setNotificationVisible(true, numOpenNetworks, false, 0);
   1667                     }
   1668                     return;
   1669                 }
   1670             }
   1671         }
   1672 
   1673         // No open networks in range, remove the notification
   1674         setNotificationVisible(false, 0, false, 0);
   1675     }
   1676 
   1677     /**
   1678      * Clears variables related to tracking whether a notification has been
   1679      * shown recently and clears the current notification.
   1680      */
   1681     private void resetNotification() {
   1682         mNotificationRepeatTime = 0;
   1683         mNumScansSinceNetworkStateChange = 0;
   1684         setNotificationVisible(false, 0, false, 0);
   1685     }
   1686 
   1687     /**
   1688      * Display or don't display a notification that there are open Wi-Fi networks.
   1689      * @param visible {@code true} if notification should be visible, {@code false} otherwise
   1690      * @param numNetworks the number networks seen
   1691      * @param force {@code true} to force notification to be shown/not-shown,
   1692      * even if it is already shown/not-shown.
   1693      * @param delay time in milliseconds after which the notification should be made
   1694      * visible or invisible.
   1695      */
   1696     private void setNotificationVisible(boolean visible, int numNetworks, boolean force,
   1697             int delay) {
   1698 
   1699         // Since we use auto cancel on the notification, when the
   1700         // mNetworksAvailableNotificationShown is true, the notification may
   1701         // have actually been canceled.  However, when it is false we know
   1702         // for sure that it is not being shown (it will not be shown any other
   1703         // place than here)
   1704 
   1705         // If it should be hidden and it is already hidden, then noop
   1706         if (!visible && !mNotificationShown && !force) {
   1707             return;
   1708         }
   1709 
   1710         NotificationManager notificationManager = (NotificationManager) mContext
   1711                 .getSystemService(Context.NOTIFICATION_SERVICE);
   1712 
   1713         Message message;
   1714         if (visible) {
   1715 
   1716             // Not enough time has passed to show the notification again
   1717             if (System.currentTimeMillis() < mNotificationRepeatTime) {
   1718                 return;
   1719             }
   1720 
   1721             if (mNotification == null) {
   1722                 // Cache the Notification object.
   1723                 mNotification = new Notification();
   1724                 mNotification.when = 0;
   1725                 mNotification.icon = ICON_NETWORKS_AVAILABLE;
   1726                 mNotification.flags = Notification.FLAG_AUTO_CANCEL;
   1727                 mNotification.contentIntent = PendingIntent.getActivity(mContext, 0,
   1728                         new Intent(WifiManager.ACTION_PICK_WIFI_NETWORK), 0);
   1729             }
   1730 
   1731             CharSequence title = mContext.getResources().getQuantityText(
   1732                     com.android.internal.R.plurals.wifi_available, numNetworks);
   1733             CharSequence details = mContext.getResources().getQuantityText(
   1734                     com.android.internal.R.plurals.wifi_available_detailed, numNetworks);
   1735             mNotification.tickerText = title;
   1736             mNotification.setLatestEventInfo(mContext, title, details, mNotification.contentIntent);
   1737 
   1738             mNotificationRepeatTime = System.currentTimeMillis() + NOTIFICATION_REPEAT_DELAY_MS;
   1739 
   1740             notificationManager.notify(ICON_NETWORKS_AVAILABLE, mNotification);
   1741         } else {
   1742             notificationManager.cancel(ICON_NETWORKS_AVAILABLE);
   1743         }
   1744 
   1745         mNotificationShown = visible;
   1746     }
   1747 
   1748     private class NotificationEnabledSettingObserver extends ContentObserver {
   1749 
   1750         public NotificationEnabledSettingObserver(Handler handler) {
   1751             super(handler);
   1752         }
   1753 
   1754         public void register() {
   1755             ContentResolver cr = mContext.getContentResolver();
   1756             cr.registerContentObserver(Settings.Secure.getUriFor(
   1757                 Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON), true, this);
   1758             mNotificationEnabled = getValue();
   1759         }
   1760 
   1761         @Override
   1762         public void onChange(boolean selfChange) {
   1763             super.onChange(selfChange);
   1764 
   1765             mNotificationEnabled = getValue();
   1766             resetNotification();
   1767         }
   1768 
   1769         private boolean getValue() {
   1770             return Settings.Secure.getInt(mContext.getContentResolver(),
   1771                     Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 1) == 1;
   1772         }
   1773     }
   1774 
   1775 
   1776 }
   1777