Home | History | Annotate | Download | only in wifi
      1 /*
      2  * Copyright (C) 2008 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 android.net.wifi;
     18 
     19 import static android.net.wifi.WifiManager.WIFI_STATE_DISABLED;
     20 import static android.net.wifi.WifiManager.WIFI_STATE_DISABLING;
     21 import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED;
     22 import static android.net.wifi.WifiManager.WIFI_STATE_ENABLING;
     23 import static android.net.wifi.WifiManager.WIFI_STATE_UNKNOWN;
     24 
     25 import android.app.ActivityManagerNative;
     26 import android.net.NetworkInfo;
     27 import android.net.NetworkStateTracker;
     28 import android.net.DhcpInfo;
     29 import android.net.NetworkUtils;
     30 import android.net.ConnectivityManager;
     31 import android.net.NetworkInfo.DetailedState;
     32 import android.net.NetworkInfo.State;
     33 import android.os.Message;
     34 import android.os.Parcelable;
     35 import android.os.Handler;
     36 import android.os.HandlerThread;
     37 import android.os.SystemProperties;
     38 import android.os.Looper;
     39 import android.os.RemoteException;
     40 import android.os.ServiceManager;
     41 import android.os.WorkSource;
     42 import android.provider.Settings;
     43 import android.text.TextUtils;
     44 import android.util.EventLog;
     45 import android.util.Log;
     46 import android.util.Config;
     47 import android.app.Notification;
     48 import android.app.PendingIntent;
     49 import android.bluetooth.BluetoothDevice;
     50 import android.bluetooth.BluetoothHeadset;
     51 import android.bluetooth.BluetoothA2dp;
     52 import android.content.ContentResolver;
     53 import android.content.Intent;
     54 import android.content.Context;
     55 import android.database.ContentObserver;
     56 import com.android.internal.app.IBatteryStats;
     57 
     58 import java.net.UnknownHostException;
     59 import java.util.ArrayList;
     60 import java.util.List;
     61 import java.util.Set;
     62 import java.util.concurrent.atomic.AtomicInteger;
     63 import java.util.concurrent.atomic.AtomicBoolean;
     64 
     65 /**
     66  * Track the state of Wifi connectivity. All event handling is done here,
     67  * and all changes in connectivity state are initiated here.
     68  *
     69  * @hide
     70  */
     71 public class WifiStateTracker extends NetworkStateTracker {
     72 
     73     private static final boolean LOCAL_LOGD = Config.LOGD || false;
     74 
     75     private static final String TAG = "WifiStateTracker";
     76 
     77     // Event log tags (must be in sync with event-log-tags)
     78     private static final int EVENTLOG_NETWORK_STATE_CHANGED = 50021;
     79     private static final int EVENTLOG_SUPPLICANT_STATE_CHANGED = 50022;
     80     private static final int EVENTLOG_DRIVER_STATE_CHANGED = 50023;
     81     private static final int EVENTLOG_INTERFACE_CONFIGURATION_STATE_CHANGED = 50024;
     82     private static final int EVENTLOG_SUPPLICANT_CONNECTION_STATE_CHANGED = 50025;
     83 
     84     // Event codes
     85     private static final int EVENT_SUPPLICANT_CONNECTION             = 1;
     86     private static final int EVENT_SUPPLICANT_DISCONNECT             = 2;
     87     private static final int EVENT_SUPPLICANT_STATE_CHANGED          = 3;
     88     private static final int EVENT_NETWORK_STATE_CHANGED             = 4;
     89     private static final int EVENT_SCAN_RESULTS_AVAILABLE            = 5;
     90     private static final int EVENT_INTERFACE_CONFIGURATION_SUCCEEDED = 6;
     91     private static final int EVENT_INTERFACE_CONFIGURATION_FAILED    = 7;
     92     private static final int EVENT_POLL_INTERVAL                     = 8;
     93     private static final int EVENT_DHCP_START                        = 9;
     94     private static final int EVENT_DEFERRED_DISCONNECT               = 10;
     95     private static final int EVENT_DEFERRED_RECONNECT                = 11;
     96     /**
     97      * The driver is started or stopped. The object will be the state: true for
     98      * started, false for stopped.
     99      */
    100     private static final int EVENT_DRIVER_STATE_CHANGED              = 12;
    101     private static final int EVENT_PASSWORD_KEY_MAY_BE_INCORRECT     = 13;
    102     private static final int EVENT_MAYBE_START_SCAN_POST_DISCONNECT  = 14;
    103 
    104     /**
    105      * The driver state indication.
    106      */
    107     private static final int DRIVER_STARTED                          = 0;
    108     private static final int DRIVER_STOPPED                          = 1;
    109     private static final int DRIVER_HUNG                             = 2;
    110 
    111     /**
    112      * Interval in milliseconds between polling for connection
    113      * status items that are not sent via asynchronous events.
    114      * An example is RSSI (signal strength).
    115      */
    116     private static final int POLL_STATUS_INTERVAL_MSECS = 3000;
    117 
    118     /**
    119      * The max number of the WPA supplicant loop iterations before we
    120      * decide that the loop should be terminated:
    121      */
    122     private static final int MAX_SUPPLICANT_LOOP_ITERATIONS = 4;
    123 
    124     /**
    125      * When a DISCONNECT event is received, we defer handling it to
    126      * allow for the possibility that the DISCONNECT is about to
    127      * be followed shortly by a CONNECT to the same network we were
    128      * just connected to. In such a case, we don't want to report
    129      * the network as down, nor do we want to reconfigure the network
    130      * interface, etc. If we get a CONNECT event for another network
    131      * within the delay window, we immediately handle the pending
    132      * disconnect before processing the CONNECT.<p/>
    133      * The five second delay is chosen somewhat arbitrarily, but is
    134      * meant to cover most of the cases where a DISCONNECT/CONNECT
    135      * happens to a network.
    136      */
    137     private static final int DISCONNECT_DELAY_MSECS = 5000;
    138     /**
    139      * When the supplicant goes idle after we do an explicit disconnect
    140      * following a DHCP failure, we need to kick the supplicant into
    141      * trying to associate with access points.
    142      */
    143     private static final int RECONNECT_DELAY_MSECS = 2000;
    144 
    145     /**
    146      * When the supplicant disconnects from an AP it sometimes forgets
    147      * to restart scanning.  Wait this delay before asking it to start
    148      * scanning (in case it forgot).  15 sec is the standard delay between
    149      * scans.
    150      */
    151     private static final int KICKSTART_SCANNING_DELAY_MSECS = 15000;
    152 
    153     /**
    154      * The maximum number of times we will retry a connection to an access point
    155      * for which we have failed in acquiring an IP address from DHCP. A value of
    156      * N means that we will make N+1 connection attempts in all.
    157      * <p>
    158      * See {@link Settings.Secure#WIFI_MAX_DHCP_RETRY_COUNT}. This is the default
    159      * value if a Settings value is not present.
    160      */
    161     private static final int DEFAULT_MAX_DHCP_RETRIES = 9;
    162 
    163     private static final int DRIVER_POWER_MODE_AUTO = 0;
    164     private static final int DRIVER_POWER_MODE_ACTIVE = 1;
    165 
    166     /**
    167      * The current WPA supplicant loop state (used to detect looping behavior):
    168      */
    169     private SupplicantState mSupplicantLoopState = SupplicantState.DISCONNECTED;
    170 
    171     /**
    172      * The current number of WPA supplicant loop iterations:
    173      */
    174     private int mNumSupplicantLoopIterations = 0;
    175 
    176     /**
    177      * The current number of supplicant state changes.  This is used to determine
    178      * if we've received any new info since we found out it was DISCONNECTED or
    179      * INACTIVE.  If we haven't for X ms, we then request a scan - it should have
    180      * done that automatically, but sometimes some firmware does not.
    181      */
    182     private int mNumSupplicantStateChanges = 0;
    183 
    184     /**
    185      * True if we received an event that that a password-key may be incorrect.
    186      * If the next incoming supplicant state change event is DISCONNECT,
    187      * broadcast a message that we have a possible password error and disable
    188      * the network.
    189      */
    190     private boolean mPasswordKeyMayBeIncorrect = false;
    191 
    192     public static final int SUPPL_SCAN_HANDLING_NORMAL = 1;
    193     public static final int SUPPL_SCAN_HANDLING_LIST_ONLY = 2;
    194 
    195     private WifiMonitor mWifiMonitor;
    196     private WifiInfo mWifiInfo;
    197     private List<ScanResult> mScanResults;
    198     private WifiManager mWM;
    199     private boolean mHaveIpAddress;
    200     private boolean mObtainingIpAddress;
    201     private boolean mTornDownByConnMgr;
    202     /**
    203      * A DISCONNECT event has been received, but processing it
    204      * is being deferred.
    205      */
    206     private boolean mDisconnectPending;
    207     /**
    208      * An operation has been performed as a result of which we expect the next event
    209      * will be a DISCONNECT.
    210      */
    211     private boolean mDisconnectExpected;
    212     private DhcpHandler mDhcpTarget;
    213     private DhcpInfo mDhcpInfo;
    214     private int mLastSignalLevel = -1;
    215     private String mLastBssid;
    216     private String mLastSsid;
    217     private int mLastNetworkId = -1;
    218     private boolean mUseStaticIp = false;
    219     private int mReconnectCount;
    220 
    221     /* Tracks if any network in the configuration is disabled */
    222     private AtomicBoolean mIsAnyNetworkDisabled = new AtomicBoolean(false);
    223 
    224     // used to store the (non-persisted) num determined during device boot
    225     // (from mcc or other phone info) before the driver is started.
    226     private int mNumAllowedChannels = 0;
    227 
    228     // Variables relating to the 'available networks' notification
    229 
    230     /**
    231      * The icon to show in the 'available networks' notification. This will also
    232      * be the ID of the Notification given to the NotificationManager.
    233      */
    234     private static final int ICON_NETWORKS_AVAILABLE =
    235             com.android.internal.R.drawable.stat_notify_wifi_in_range;
    236     /**
    237      * When a notification is shown, we wait this amount before possibly showing it again.
    238      */
    239     private final long NOTIFICATION_REPEAT_DELAY_MS;
    240     /**
    241      * Whether the user has set the setting to show the 'available networks' notification.
    242      */
    243     private boolean mNotificationEnabled;
    244     /**
    245      * Observes the user setting to keep {@link #mNotificationEnabled} in sync.
    246      */
    247     private NotificationEnabledSettingObserver mNotificationEnabledSettingObserver;
    248     /**
    249      * The {@link System#currentTimeMillis()} must be at least this value for us
    250      * to show the notification again.
    251      */
    252     private long mNotificationRepeatTime;
    253     /**
    254      * The Notification object given to the NotificationManager.
    255      */
    256     private Notification mNotification;
    257     /**
    258      * Whether the notification is being shown, as set by us. That is, if the
    259      * user cancels the notification, we will not receive the callback so this
    260      * will still be true. We only guarantee if this is false, then the
    261      * notification is not showing.
    262      */
    263     private boolean mNotificationShown;
    264     /**
    265      * The number of continuous scans that must occur before consider the
    266      * supplicant in a scanning state. This allows supplicant to associate with
    267      * remembered networks that are in the scan results.
    268      */
    269     private static final int NUM_SCANS_BEFORE_ACTUALLY_SCANNING = 3;
    270     /**
    271      * The number of scans since the last network state change. When this
    272      * exceeds {@link #NUM_SCANS_BEFORE_ACTUALLY_SCANNING}, we consider the
    273      * supplicant to actually be scanning. When the network state changes to
    274      * something other than scanning, we reset this to 0.
    275      */
    276     private int mNumScansSinceNetworkStateChange;
    277     /**
    278      * Observes the static IP address settings.
    279      */
    280     private SettingsObserver mSettingsObserver;
    281 
    282     private boolean mIsScanModeActive;
    283     private boolean mEnableRssiPolling;
    284     private boolean mIsHighPerfEnabled;
    285     private int mPowerModeRefCount = 0;
    286     private int mOptimizationsDisabledRefCount = 0;
    287 
    288     /**
    289      * One of  {@link WifiManager#WIFI_STATE_DISABLED},
    290      *         {@link WifiManager#WIFI_STATE_DISABLING},
    291      *         {@link WifiManager#WIFI_STATE_ENABLED},
    292      *         {@link WifiManager#WIFI_STATE_ENABLING},
    293      *         {@link WifiManager#WIFI_STATE_UNKNOWN}
    294      *
    295      * getWifiState() is not synchronized to make sure it's always fast,
    296      * even when the instance lock is held on other slow operations.
    297      * Use a atomic variable for state.
    298      */
    299     private final AtomicInteger mWifiState = new AtomicInteger(WIFI_STATE_UNKNOWN);
    300 
    301     // Wi-Fi run states:
    302     private static final int RUN_STATE_STARTING = 1;
    303     private static final int RUN_STATE_RUNNING  = 2;
    304     private static final int RUN_STATE_STOPPING = 3;
    305     private static final int RUN_STATE_STOPPED  = 4;
    306 
    307     private static final String mRunStateNames[] = {
    308             "Starting",
    309             "Running",
    310             "Stopping",
    311             "Stopped"
    312     };
    313     private int mRunState;
    314 
    315     private final IBatteryStats mBatteryStats;
    316 
    317     private boolean mIsScanOnly;
    318 
    319     private BluetoothA2dp mBluetoothA2dp;
    320 
    321     private String mInterfaceName;
    322     private static String LS = System.getProperty("line.separator");
    323 
    324     private static String[] sDnsPropNames;
    325 
    326     /**
    327      * Keep track of whether we last told the battery stats we had started.
    328      */
    329     private boolean mReportedRunning = false;
    330 
    331     /**
    332      * Most recently set source of starting WIFI.
    333      */
    334     private final WorkSource mRunningWifiUids = new WorkSource();
    335 
    336     /**
    337      * The last reported UIDs that were responsible for starting WIFI.
    338      */
    339     private final WorkSource mLastRunningWifiUids = new WorkSource();
    340 
    341     /**
    342      * A structure for supplying information about a supplicant state
    343      * change in the STATE_CHANGE event message that comes from the
    344      * WifiMonitor
    345      * thread.
    346      */
    347     private static class SupplicantStateChangeResult {
    348         SupplicantStateChangeResult(int networkId, String BSSID, SupplicantState state) {
    349             this.state = state;
    350             this.BSSID = BSSID;
    351             this.networkId = networkId;
    352         }
    353         int networkId;
    354         String BSSID;
    355         SupplicantState state;
    356     }
    357 
    358     /**
    359      * A structure for supplying information about a connection in
    360      * the CONNECTED event message that comes from the WifiMonitor
    361      * thread.
    362      */
    363     private static class NetworkStateChangeResult {
    364         NetworkStateChangeResult(DetailedState state, String BSSID, int networkId) {
    365             this.state = state;
    366             this.BSSID = BSSID;
    367             this.networkId = networkId;
    368         }
    369         DetailedState state;
    370         String BSSID;
    371         int networkId;
    372     }
    373 
    374     public WifiStateTracker(Context context, Handler target) {
    375         super(context, target, ConnectivityManager.TYPE_WIFI, 0, "WIFI", "");
    376 
    377         mWifiInfo = new WifiInfo();
    378         mWifiMonitor = new WifiMonitor(this);
    379         mHaveIpAddress = false;
    380         mObtainingIpAddress = false;
    381         setTornDownByConnMgr(false);
    382         mDisconnectPending = false;
    383         mScanResults = new ArrayList<ScanResult>();
    384         // Allocate DHCP info object once, and fill it in on each request
    385         mDhcpInfo = new DhcpInfo();
    386         mRunState = RUN_STATE_STARTING;
    387 
    388         // Setting is in seconds
    389         NOTIFICATION_REPEAT_DELAY_MS = Settings.Secure.getInt(context.getContentResolver(),
    390                 Settings.Secure.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY, 900) * 1000l;
    391         mNotificationEnabledSettingObserver = new NotificationEnabledSettingObserver(new Handler());
    392         mNotificationEnabledSettingObserver.register();
    393 
    394         mSettingsObserver = new SettingsObserver(new Handler());
    395 
    396         mInterfaceName = SystemProperties.get("wifi.interface", "tiwlan0");
    397         sDnsPropNames = new String[] {
    398             "dhcp." + mInterfaceName + ".dns1",
    399             "dhcp." + mInterfaceName + ".dns2"
    400         };
    401         mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo"));
    402 
    403     }
    404 
    405     /**
    406      * Helper method: sets the supplicant state and keeps the network
    407      * info updated.
    408      * @param state the new state
    409      */
    410     private void setSupplicantState(SupplicantState state) {
    411         mWifiInfo.setSupplicantState(state);
    412         updateNetworkInfo();
    413         checkPollTimer();
    414     }
    415 
    416     public SupplicantState getSupplicantState() {
    417         return mWifiInfo.getSupplicantState();
    418     }
    419 
    420     /**
    421      * Helper method: sets the supplicant state and keeps the network
    422      * info updated (string version).
    423      * @param stateName the string name of the new state
    424      */
    425     private void setSupplicantState(String stateName) {
    426         mWifiInfo.setSupplicantState(stateName);
    427         updateNetworkInfo();
    428         checkPollTimer();
    429     }
    430 
    431     /**
    432      * Helper method: sets the boolean indicating that the connection
    433      * manager asked the network to be torn down (and so only the connection
    434      * manager can set it up again).
    435      * network info updated.
    436      * @param flag {@code true} if explicitly disabled.
    437      */
    438     private void setTornDownByConnMgr(boolean flag) {
    439         mTornDownByConnMgr = flag;
    440         updateNetworkInfo();
    441     }
    442 
    443     /**
    444      * Return the IP addresses of the DNS servers available for the WLAN
    445      * network interface.
    446      * @return a list of DNS addresses, with no holes.
    447      */
    448     public String[] getNameServers() {
    449         return getNameServerList(sDnsPropNames);
    450     }
    451 
    452     /**
    453      * Return the name of our WLAN network interface.
    454      * @return the name of our interface.
    455      */
    456     public String getInterfaceName() {
    457         return mInterfaceName;
    458     }
    459 
    460     /**
    461      * Return the system properties name associated with the tcp buffer sizes
    462      * for this network.
    463      */
    464     public String getTcpBufferSizesPropName() {
    465         return "net.tcp.buffersize.wifi";
    466     }
    467 
    468     public void startMonitoring() {
    469         /*
    470          * Get a handle on the WifiManager. This cannot be done in our
    471          * constructor, because the Wifi service is not yet registered.
    472          */
    473         mWM = (WifiManager)mContext.getSystemService(Context.WIFI_SERVICE);
    474     }
    475 
    476     public void startEventLoop() {
    477         mWifiMonitor.startMonitoring();
    478     }
    479 
    480     /**
    481      * Wi-Fi is considered available as long as we have a connection to the
    482      * supplicant daemon and there is at least one enabled network. If a teardown
    483      * was explicitly requested, then Wi-Fi can be restarted with a reconnect
    484      * request, so it is considered available. If the driver has been stopped
    485      * for any reason other than a teardown request, Wi-Fi is considered
    486      * unavailable.
    487      * @return {@code true} if Wi-Fi connections are possible
    488      */
    489     public synchronized boolean isAvailable() {
    490         /*
    491          * TODO: Need to also look at scan results to see whether we're
    492          * in range of any access points. If we have scan results that
    493          * are no more than N seconds old, use those, otherwise, initiate
    494          * a scan and wait for the results. This only matters if we
    495          * allow mobile to be the preferred network.
    496          */
    497         SupplicantState suppState = mWifiInfo.getSupplicantState();
    498         return suppState != SupplicantState.UNINITIALIZED &&
    499                 suppState != SupplicantState.INACTIVE &&
    500                 (mTornDownByConnMgr || !isDriverStopped());
    501     }
    502 
    503     /**
    504      * {@inheritDoc}
    505      * There are currently no defined Wi-Fi subtypes.
    506      */
    507     public int getNetworkSubtype() {
    508         return 0;
    509     }
    510 
    511     /**
    512      * Helper method: updates the network info object to keep it in sync with
    513      * the Wi-Fi state tracker.
    514      */
    515     private void updateNetworkInfo() {
    516         mNetworkInfo.setIsAvailable(isAvailable());
    517     }
    518 
    519     /**
    520      * Report whether the Wi-Fi connection is fully configured for data.
    521      * @return {@code true} if the {@link SupplicantState} is
    522      * {@link android.net.wifi.SupplicantState#COMPLETED COMPLETED}.
    523      */
    524     public boolean isConnectionCompleted() {
    525         return mWifiInfo.getSupplicantState() == SupplicantState.COMPLETED;
    526     }
    527 
    528     /**
    529      * Report whether the Wi-Fi connection has successfully acquired an IP address.
    530      * @return {@code true} if the Wi-Fi connection has been assigned an IP address.
    531      */
    532     public boolean hasIpAddress() {
    533         return mHaveIpAddress;
    534     }
    535 
    536     /**
    537      * Send the tracker a notification that a user-entered password key
    538      * may be incorrect (i.e., caused authentication to fail).
    539      */
    540     void notifyPasswordKeyMayBeIncorrect() {
    541         sendEmptyMessage(EVENT_PASSWORD_KEY_MAY_BE_INCORRECT);
    542     }
    543 
    544     /**
    545      * Send the tracker a notification that a connection to the supplicant
    546      * daemon has been established.
    547      */
    548     void notifySupplicantConnection() {
    549         sendEmptyMessage(EVENT_SUPPLICANT_CONNECTION);
    550     }
    551 
    552     /**
    553      * Send the tracker a notification that the state of the supplicant
    554      * has changed.
    555      * @param networkId the configured network on which the state change occurred
    556      * @param newState the new {@code SupplicantState}
    557      */
    558     void notifyStateChange(int networkId, String BSSID, SupplicantState newState) {
    559         Message msg = Message.obtain(
    560             this, EVENT_SUPPLICANT_STATE_CHANGED,
    561             new SupplicantStateChangeResult(networkId, BSSID, newState));
    562         msg.sendToTarget();
    563     }
    564 
    565     /**
    566      * Send the tracker a notification that the state of Wifi connectivity
    567      * has changed.
    568      * @param networkId the configured network on which the state change occurred
    569      * @param newState the new network state
    570      * @param BSSID when the new state is {@link DetailedState#CONNECTED
    571      * NetworkInfo.DetailedState.CONNECTED},
    572      * this is the MAC address of the access point. Otherwise, it
    573      * is {@code null}.
    574      */
    575     void notifyStateChange(DetailedState newState, String BSSID, int networkId) {
    576         Message msg = Message.obtain(
    577             this, EVENT_NETWORK_STATE_CHANGED,
    578             new NetworkStateChangeResult(newState, BSSID, networkId));
    579         msg.sendToTarget();
    580     }
    581 
    582     /**
    583      * Send the tracker a notification that a scan has completed, and results
    584      * are available.
    585      */
    586     void notifyScanResultsAvailable() {
    587         // reset the supplicant's handling of scan results to "normal" mode
    588         setScanResultHandling(SUPPL_SCAN_HANDLING_NORMAL);
    589         sendEmptyMessage(EVENT_SCAN_RESULTS_AVAILABLE);
    590     }
    591 
    592     /**
    593      * Send the tracker a notification that we can no longer communicate with
    594      * the supplicant daemon.
    595      */
    596     void notifySupplicantLost() {
    597         sendEmptyMessage(EVENT_SUPPLICANT_DISCONNECT);
    598     }
    599 
    600     /**
    601      * Send the tracker a notification that the Wi-Fi driver has been stopped.
    602      */
    603     void notifyDriverStopped() {
    604         mRunState = RUN_STATE_STOPPED;
    605 
    606         // Send a driver stopped message to our handler
    607         Message.obtain(this, EVENT_DRIVER_STATE_CHANGED, DRIVER_STOPPED, 0).sendToTarget();
    608     }
    609 
    610     /**
    611      * Send the tracker a notification that the Wi-Fi driver has been restarted after
    612      * having been stopped.
    613      */
    614     void notifyDriverStarted() {
    615         // Send a driver started message to our handler
    616         Message.obtain(this, EVENT_DRIVER_STATE_CHANGED, DRIVER_STARTED, 0).sendToTarget();
    617     }
    618 
    619     /**
    620      * Send the tracker a notification that the Wi-Fi driver has hung and needs restarting.
    621      */
    622     void notifyDriverHung() {
    623         // Send a driver hanged message to our handler
    624         Message.obtain(this, EVENT_DRIVER_STATE_CHANGED, DRIVER_HUNG, 0).sendToTarget();
    625     }
    626 
    627     /**
    628      * Set the interval timer for polling connection information
    629      * that is not delivered asynchronously.
    630      */
    631     private synchronized void checkPollTimer() {
    632         if (mEnableRssiPolling &&
    633                 mWifiInfo.getSupplicantState() == SupplicantState.COMPLETED &&
    634                 !hasMessages(EVENT_POLL_INTERVAL)) {
    635             sendEmptyMessageDelayed(EVENT_POLL_INTERVAL, POLL_STATUS_INTERVAL_MSECS);
    636         }
    637     }
    638 
    639     /**
    640      * TODO: mRunState is not synchronized in some places
    641      * address this as part of re-architect.
    642      *
    643      * TODO: We are exposing an additional public synchronized call
    644      * for a wakelock optimization in WifiService. Remove it
    645      * when we handle the wakelock in ConnectivityService.
    646      */
    647     public synchronized boolean isDriverStopped() {
    648         return mRunState == RUN_STATE_STOPPED || mRunState == RUN_STATE_STOPPING;
    649     }
    650 
    651     public void updateBatteryWorkSourceLocked(WorkSource newSource) {
    652         try {
    653             if (newSource != null) {
    654                 mRunningWifiUids.set(newSource);
    655             }
    656             if (mRunState == RUN_STATE_RUNNING) {
    657                 if (mReportedRunning) {
    658                     // If the work source has changed since last time, need
    659                     // to remove old work from battery stats.
    660                     if (mLastRunningWifiUids.diff(mRunningWifiUids)) {
    661                         mBatteryStats.noteWifiRunningChanged(mLastRunningWifiUids,
    662                                 mRunningWifiUids);
    663                         mLastRunningWifiUids.set(mRunningWifiUids);
    664                     }
    665                 } else {
    666                     // Now being started, report it.
    667                     mBatteryStats.noteWifiRunning(mRunningWifiUids);
    668                     mLastRunningWifiUids.set(mRunningWifiUids);
    669                     mReportedRunning = true;
    670                 }
    671             } else if (mRunState == RUN_STATE_STOPPED) {
    672                 if (mReportedRunning) {
    673                     // Last reported we were running, time to stop.
    674                     mBatteryStats.noteWifiStopped(mLastRunningWifiUids);
    675                     mLastRunningWifiUids.clear();
    676                     mReportedRunning = false;
    677                 }
    678             } else {
    679                 // State in transition -- nothing to update yet.
    680             }
    681         } catch (RemoteException ignore) {
    682         }
    683     }
    684 
    685     /**
    686      * Set the run state to either "normal" or "scan-only".
    687      * @param scanOnlyMode true if the new mode should be scan-only.
    688      */
    689     public synchronized void setScanOnlyMode(boolean scanOnlyMode) {
    690         // do nothing unless scan-only mode is changing
    691         if (mIsScanOnly != scanOnlyMode) {
    692             int scanType = (scanOnlyMode ?
    693                     SUPPL_SCAN_HANDLING_LIST_ONLY : SUPPL_SCAN_HANDLING_NORMAL);
    694             if (LOCAL_LOGD) Log.v(TAG, "Scan-only mode changing to " + scanOnlyMode + " scanType=" + scanType);
    695             if (setScanResultHandling(scanType)) {
    696                 mIsScanOnly = scanOnlyMode;
    697                 if (!isDriverStopped()) {
    698                     if (scanOnlyMode) {
    699                         disconnect();
    700                     } else {
    701                         reconnectCommand();
    702                     }
    703                 }
    704             }
    705         }
    706     }
    707 
    708     /**
    709      * Set suspend mode optimizations. These include:
    710      * - packet filtering
    711      * - turn off roaming
    712      * - DTIM settings
    713      *
    714      * Uses reference counting to keep the suspend optimizations disabled
    715      * as long as one entity wants optimizations disabled.
    716      *
    717      * For example, WifiLock can keep suspend optimizations disabled
    718      * or the user setting (wifi never sleeps) can keep suspend optimizations
    719      * disabled. As long as one entity wants it disabled, it should stay
    720      * that way
    721      *
    722      * @param enabled true if optimizations need enabled, false otherwise
    723      */
    724     public synchronized void setSuspendModeOptimizations(boolean enabled) {
    725 
    726         /* It is good to plumb suspend optimization enable
    727          * or disable even if ref count indicates already done
    728          * since we could have a case of previous failure.
    729          */
    730         if (!enabled) {
    731             mOptimizationsDisabledRefCount++;
    732         } else {
    733             mOptimizationsDisabledRefCount--;
    734             if (mOptimizationsDisabledRefCount > 0) {
    735                 return;
    736             } else {
    737                 /* Keep refcount from becoming negative */
    738                 mOptimizationsDisabledRefCount = 0;
    739             }
    740         }
    741 
    742         if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
    743             return;
    744         }
    745 
    746         WifiNative.setSuspendOptimizationsCommand(enabled);
    747     }
    748 
    749 
    750     /**
    751      * Set high performance mode of operation. This would mean
    752      * use active power mode and disable suspend optimizations
    753      * @param enabled true if enabled, false otherwise
    754      */
    755     public synchronized void setHighPerfMode(boolean enabled) {
    756         if (mIsHighPerfEnabled != enabled) {
    757             if (enabled) {
    758                 setPowerMode(DRIVER_POWER_MODE_ACTIVE);
    759                 setSuspendModeOptimizations(false);
    760             } else {
    761                 setPowerMode(DRIVER_POWER_MODE_AUTO);
    762                 setSuspendModeOptimizations(true);
    763             }
    764             mIsHighPerfEnabled = enabled;
    765             Log.d(TAG,"high performance mode: " + enabled);
    766         }
    767     }
    768 
    769 
    770     private void checkIsBluetoothPlaying() {
    771         boolean isBluetoothPlaying = false;
    772         Set<BluetoothDevice> connected = mBluetoothA2dp.getConnectedSinks();
    773 
    774         for (BluetoothDevice device : connected) {
    775             if (mBluetoothA2dp.getSinkState(device) == BluetoothA2dp.STATE_PLAYING) {
    776                 isBluetoothPlaying = true;
    777                 break;
    778             }
    779         }
    780         setBluetoothScanMode(isBluetoothPlaying);
    781     }
    782 
    783     public void enableRssiPolling(boolean enable) {
    784         if (mEnableRssiPolling != enable) {
    785             mEnableRssiPolling = enable;
    786             checkPollTimer();
    787         }
    788     }
    789 
    790     /**
    791      * We release the wakelock in WifiService
    792      * using a timer.
    793      *
    794      * TODO:
    795      * Releasing wakelock using both timer and
    796      * a call from ConnectivityService requires
    797      * a rethink. We had problems where WifiService
    798      * could keep a wakelock forever if we delete
    799      * messages in the asynchronous call
    800      * from ConnectivityService
    801      */
    802     @Override
    803     public void releaseWakeLock() {
    804     }
    805 
    806     /**
    807      * Tracks the WPA supplicant states to detect "loop" situations.
    808      * @param newSupplicantState The new WPA supplicant state.
    809      * @return {@code true} if the supplicant loop should be stopped
    810      * and {@code false} if it should continue.
    811      */
    812     private boolean isSupplicantLooping(SupplicantState newSupplicantState) {
    813         if (SupplicantState.ASSOCIATING.ordinal() <= newSupplicantState.ordinal()
    814             && newSupplicantState.ordinal() < SupplicantState.COMPLETED.ordinal()) {
    815             if (mSupplicantLoopState != newSupplicantState) {
    816                 if (newSupplicantState.ordinal() < mSupplicantLoopState.ordinal()) {
    817                     ++mNumSupplicantLoopIterations;
    818                 }
    819 
    820                 mSupplicantLoopState = newSupplicantState;
    821             }
    822         } else if (newSupplicantState == SupplicantState.COMPLETED) {
    823             resetSupplicantLoopState();
    824         }
    825 
    826         return mNumSupplicantLoopIterations >= MAX_SUPPLICANT_LOOP_ITERATIONS;
    827     }
    828 
    829     /**
    830      * Resets the WPA supplicant loop state.
    831      */
    832     private void resetSupplicantLoopState() {
    833         mNumSupplicantLoopIterations = 0;
    834     }
    835 
    836     @Override
    837     public void handleMessage(Message msg) {
    838         Intent intent;
    839 
    840         switch (msg.what) {
    841             case EVENT_SUPPLICANT_CONNECTION:
    842                 mRunState = RUN_STATE_RUNNING;
    843                 String macaddr;
    844                 synchronized (this) {
    845                     updateBatteryWorkSourceLocked(null);
    846                     macaddr = WifiNative.getMacAddressCommand();
    847                 }
    848                 if (macaddr != null) {
    849                     mWifiInfo.setMacAddress(macaddr);
    850                 }
    851 
    852                 checkUseStaticIp();
    853                 /* Reset notification state on new connection */
    854                 resetNotificationTimer();
    855                 /*
    856                  * DHCP requests are blocking, so run them in a separate thread.
    857                  */
    858                 HandlerThread dhcpThread = new HandlerThread("DHCP Handler Thread");
    859                 dhcpThread.start();
    860                 mDhcpTarget = new DhcpHandler(dhcpThread.getLooper(), this);
    861                 mIsScanModeActive = true;
    862                 mIsHighPerfEnabled = false;
    863                 mOptimizationsDisabledRefCount = 0;
    864                 mPowerModeRefCount = 0;
    865                 mTornDownByConnMgr = false;
    866                 mLastBssid = null;
    867                 mLastSsid = null;
    868                 mIsAnyNetworkDisabled.set(false);
    869                 requestConnectionInfo();
    870                 SupplicantState supplState = mWifiInfo.getSupplicantState();
    871 
    872                 if (LOCAL_LOGD) Log.v(TAG, "Connection to supplicant established, state=" +
    873                     supplState);
    874                 // Wi-Fi supplicant connection state changed:
    875                 // [31- 2] Reserved for future use
    876                 // [ 1- 0] Connected to supplicant (1), disconnected from supplicant (0) ,
    877                 //         or supplicant died (2)
    878                 EventLog.writeEvent(EVENTLOG_SUPPLICANT_CONNECTION_STATE_CHANGED, 1);
    879                 /*
    880                  * The COMPLETED state change from the supplicant may have occurred
    881                  * in between polling for supplicant availability, in which case
    882                  * we didn't perform a DHCP request to get an IP address.
    883                  */
    884                 if (supplState == SupplicantState.COMPLETED) {
    885                     mLastBssid = mWifiInfo.getBSSID();
    886                     mLastSsid = mWifiInfo.getSSID();
    887                     configureInterface();
    888                 }
    889                 if (ActivityManagerNative.isSystemReady()) {
    890                     intent = new Intent(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
    891                     intent.putExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, true);
    892                     mContext.sendBroadcast(intent);
    893                 }
    894                 if (supplState == SupplicantState.COMPLETED && mHaveIpAddress) {
    895                     setDetailedState(DetailedState.CONNECTED);
    896                 } else {
    897                     setDetailedState(WifiInfo.getDetailedStateOf(supplState));
    898                 }
    899                 /*
    900                  * Filter out multicast packets. This saves battery power, since
    901                  * the CPU doesn't have to spend time processing packets that
    902                  * are going to end up being thrown away.
    903                  */
    904                 mWM.initializeMulticastFiltering();
    905 
    906                 if (mBluetoothA2dp == null) {
    907                     mBluetoothA2dp = new BluetoothA2dp(mContext);
    908                 }
    909                 checkIsBluetoothPlaying();
    910 
    911                 // initialize this after the supplicant is alive
    912                 setNumAllowedChannels();
    913                 break;
    914 
    915             case EVENT_SUPPLICANT_DISCONNECT:
    916                 mRunState = RUN_STATE_STOPPED;
    917                 synchronized (this) {
    918                     updateBatteryWorkSourceLocked(null);
    919                 }
    920                 boolean died = mWifiState.get() != WIFI_STATE_DISABLED &&
    921                                mWifiState.get() != WIFI_STATE_DISABLING;
    922                 if (died) {
    923                     if (LOCAL_LOGD) Log.v(TAG, "Supplicant died unexpectedly");
    924                 } else {
    925                     if (LOCAL_LOGD) Log.v(TAG, "Connection to supplicant lost");
    926                 }
    927                 // Wi-Fi supplicant connection state changed:
    928                 // [31- 2] Reserved for future use
    929                 // [ 1- 0] Connected to supplicant (1), disconnected from supplicant (0) ,
    930                 //         or supplicant died (2)
    931                 EventLog.writeEvent(EVENTLOG_SUPPLICANT_CONNECTION_STATE_CHANGED, died ? 2 : 0);
    932                 closeSupplicantConnection();
    933 
    934                 if (died) {
    935                     resetConnections(true);
    936                 }
    937                 // When supplicant dies, kill the DHCP thread
    938                 if (mDhcpTarget != null) {
    939                     mDhcpTarget.getLooper().quit();
    940                     mDhcpTarget = null;
    941                 }
    942                 mContext.removeStickyBroadcast(new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION));
    943                 if (ActivityManagerNative.isSystemReady()) {
    944                     intent = new Intent(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
    945                     intent.putExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, false);
    946                     mContext.sendBroadcast(intent);
    947                 }
    948                 setDetailedState(DetailedState.DISCONNECTED);
    949                 setSupplicantState(SupplicantState.UNINITIALIZED);
    950                 mHaveIpAddress = false;
    951                 mObtainingIpAddress = false;
    952                 if (died) {
    953                     mWM.setWifiEnabled(false);
    954                 }
    955                 break;
    956 
    957             case EVENT_MAYBE_START_SCAN_POST_DISCONNECT:
    958                 // Only do this if we haven't gotten a new supplicant status since the timer
    959                 // started
    960                 if (mNumSupplicantStateChanges == msg.arg1) {
    961                     scan(false); // do a passive scan
    962                 }
    963                 break;
    964 
    965             case EVENT_SUPPLICANT_STATE_CHANGED:
    966                 mNumSupplicantStateChanges++;
    967                 SupplicantStateChangeResult supplicantStateResult =
    968                     (SupplicantStateChangeResult) msg.obj;
    969                 SupplicantState newState = supplicantStateResult.state;
    970                 SupplicantState currentState = mWifiInfo.getSupplicantState();
    971 
    972                 // Wi-Fi supplicant state changed:
    973                 // [31- 6] Reserved for future use
    974                 // [ 5- 0] Supplicant state ordinal (as defined by SupplicantState)
    975                 int eventLogParam = (newState.ordinal() & 0x3f);
    976                 EventLog.writeEvent(EVENTLOG_SUPPLICANT_STATE_CHANGED, eventLogParam);
    977 
    978                 if (LOCAL_LOGD) Log.v(TAG, "Changing supplicant state: "
    979                                       + currentState +
    980                                       " ==> " + newState);
    981 
    982                 int networkId = supplicantStateResult.networkId;
    983 
    984                 /**
    985                  * The SupplicantState BSSID value is valid in ASSOCIATING state only.
    986                  * The NetworkState BSSID value comes upon a successful connection.
    987                  */
    988                 if (supplicantStateResult.state == SupplicantState.ASSOCIATING) {
    989                     mLastBssid = supplicantStateResult.BSSID;
    990                 }
    991                 /*
    992                  * If we get disconnect or inactive we need to start our
    993                  * watchdog timer to start a scan
    994                  */
    995                 if (newState == SupplicantState.DISCONNECTED ||
    996                         newState == SupplicantState.INACTIVE) {
    997                     sendMessageDelayed(obtainMessage(EVENT_MAYBE_START_SCAN_POST_DISCONNECT,
    998                             mNumSupplicantStateChanges, 0), KICKSTART_SCANNING_DELAY_MSECS);
    999                 }
   1000 
   1001 
   1002                 /*
   1003                  * Did we get to DISCONNECTED state due to an
   1004                  * authentication (password) failure?
   1005                  */
   1006                 boolean failedToAuthenticate = false;
   1007                 if (newState == SupplicantState.DISCONNECTED) {
   1008                     failedToAuthenticate = mPasswordKeyMayBeIncorrect;
   1009                 }
   1010                 mPasswordKeyMayBeIncorrect = false;
   1011 
   1012                 /*
   1013                  * Keep track of the supplicant state and check if we should
   1014                  * disable the network
   1015                  */
   1016                 boolean disabledNetwork = false;
   1017                 if (isSupplicantLooping(newState)) {
   1018                     if (LOCAL_LOGD) {
   1019                         Log.v(TAG,
   1020                               "Stop WPA supplicant loop and disable network");
   1021                     }
   1022                     disabledNetwork = wifiManagerDisableNetwork(networkId);
   1023                 }
   1024 
   1025                 if (disabledNetwork) {
   1026                     /*
   1027                      * Reset the loop state if we disabled the network
   1028                      */
   1029                     resetSupplicantLoopState();
   1030                 } else if (newState != currentState ||
   1031                         (newState == SupplicantState.DISCONNECTED && isDriverStopped())) {
   1032                     setSupplicantState(newState);
   1033                     if (newState == SupplicantState.DORMANT) {
   1034                         DetailedState newDetailedState;
   1035                         Message reconnectMsg = obtainMessage(EVENT_DEFERRED_RECONNECT, mLastBssid);
   1036                         if (mIsScanOnly || mRunState == RUN_STATE_STOPPING) {
   1037                             newDetailedState = DetailedState.IDLE;
   1038                         } else {
   1039                             newDetailedState = DetailedState.FAILED;
   1040                         }
   1041                         handleDisconnectedState(newDetailedState, true);
   1042                         /**
   1043                          * If we were associated with a network (networkId != -1),
   1044                          * assume we reached this state because of a failed attempt
   1045                          * to acquire an IP address, and attempt another connection
   1046                          * and IP address acquisition in RECONNECT_DELAY_MSECS
   1047                          * milliseconds.
   1048                          */
   1049                         if (mRunState == RUN_STATE_RUNNING && !mIsScanOnly && networkId != -1) {
   1050                             sendMessageDelayed(reconnectMsg, RECONNECT_DELAY_MSECS);
   1051                         } else if (mRunState == RUN_STATE_STOPPING) {
   1052                             stopDriver();
   1053                         } else if (mRunState == RUN_STATE_STARTING && !mIsScanOnly) {
   1054                             reconnectCommand();
   1055                         }
   1056                     } else if (newState == SupplicantState.DISCONNECTED) {
   1057                         mHaveIpAddress = false;
   1058                         if (isDriverStopped() || mDisconnectExpected) {
   1059                             handleDisconnectedState(DetailedState.DISCONNECTED, true);
   1060                         } else {
   1061                             scheduleDisconnect();
   1062                         }
   1063                     } else if (newState != SupplicantState.COMPLETED && !mDisconnectPending) {
   1064                         /**
   1065                          * Ignore events that don't change the connectivity state,
   1066                          * such as WPA rekeying operations.
   1067                          */
   1068                         if (!(currentState == SupplicantState.COMPLETED &&
   1069                                (newState == SupplicantState.ASSOCIATING ||
   1070                                 newState == SupplicantState.ASSOCIATED ||
   1071                                 newState == SupplicantState.FOUR_WAY_HANDSHAKE ||
   1072                                 newState == SupplicantState.GROUP_HANDSHAKE))) {
   1073                             setDetailedState(WifiInfo.getDetailedStateOf(newState));
   1074                         }
   1075                     }
   1076 
   1077                     mDisconnectExpected = false;
   1078                     intent = new Intent(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
   1079                     intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
   1080                             | Intent.FLAG_RECEIVER_REPLACE_PENDING);
   1081                     intent.putExtra(WifiManager.EXTRA_NEW_STATE, (Parcelable)newState);
   1082                     if (failedToAuthenticate) {
   1083                         if (LOCAL_LOGD) Log.d(TAG, "Failed to authenticate, disabling network " + networkId);
   1084                         wifiManagerDisableNetwork(networkId);
   1085                         intent.putExtra(
   1086                             WifiManager.EXTRA_SUPPLICANT_ERROR,
   1087                             WifiManager.ERROR_AUTHENTICATING);
   1088                     }
   1089                     mContext.sendStickyBroadcast(intent);
   1090                 }
   1091                 break;
   1092 
   1093             case EVENT_NETWORK_STATE_CHANGED:
   1094                 /*
   1095                  * Each CONNECT or DISCONNECT generates a pair of events.
   1096                  * One is a supplicant state change event, and the other
   1097                  * is a network state change event. For connects, the
   1098                  * supplicant event always arrives first, followed by
   1099                  * the network state change event. Only the latter event
   1100                  * has the BSSID, which we are interested in capturing.
   1101                  * For disconnects, the order is the opposite -- the
   1102                  * network state change event comes first, followed by
   1103                  * the supplicant state change event.
   1104                  */
   1105                 NetworkStateChangeResult result =
   1106                     (NetworkStateChangeResult) msg.obj;
   1107 
   1108                 // Wi-Fi network state changed:
   1109                 // [31- 6] Reserved for future use
   1110                 // [ 5- 0] Detailed state ordinal (as defined by NetworkInfo.DetailedState)
   1111                 eventLogParam = (result.state.ordinal() & 0x3f);
   1112                 EventLog.writeEvent(EVENTLOG_NETWORK_STATE_CHANGED, eventLogParam);
   1113 
   1114                 if (LOCAL_LOGD) Log.v(TAG, "New network state is " + result.state);
   1115                 /*
   1116                  * If we're in scan-only mode, don't advance the state machine, and
   1117                  * don't report the state change to clients.
   1118                  */
   1119                 if (mIsScanOnly) {
   1120                     if (LOCAL_LOGD) Log.v(TAG, "Dropping event in scan-only mode");
   1121                     break;
   1122                 }
   1123                 if (result.state != DetailedState.SCANNING) {
   1124                     /*
   1125                      * Reset the scan count since there was a network state
   1126                      * change. This could be from supplicant trying to associate
   1127                      * with a network.
   1128                      */
   1129                     mNumScansSinceNetworkStateChange = 0;
   1130                 }
   1131                 /*
   1132                  * If the supplicant sent us a CONNECTED event, we don't
   1133                  * want to send out an indication of overall network
   1134                  * connectivity until we have our IP address. If the
   1135                  * supplicant sent us a DISCONNECTED event, we delay
   1136                  * sending a notification in case a reconnection to
   1137                  * the same access point occurs within a short time.
   1138                  */
   1139                 if (result.state == DetailedState.DISCONNECTED) {
   1140                     if (mWifiInfo.getSupplicantState() != SupplicantState.DORMANT) {
   1141                         scheduleDisconnect();
   1142                     }
   1143                     break;
   1144                 }
   1145                 requestConnectionStatus(mWifiInfo);
   1146                 if (!(result.state == DetailedState.CONNECTED &&
   1147                         (!mHaveIpAddress || mDisconnectPending))) {
   1148                     setDetailedState(result.state);
   1149                 }
   1150 
   1151                 if (result.state == DetailedState.CONNECTED) {
   1152                     /*
   1153                      * Remove the 'available networks' notification when we
   1154                      * successfully connect to a network.
   1155                      */
   1156                     setNotificationVisible(false, 0, false, 0);
   1157                     boolean wasDisconnectPending = mDisconnectPending;
   1158                     cancelDisconnect();
   1159                     /*
   1160                      * The connection is fully configured as far as link-level
   1161                      * connectivity is concerned, but we may still need to obtain
   1162                      * an IP address.
   1163                      */
   1164                     if (wasDisconnectPending) {
   1165                         DetailedState saveState = getNetworkInfo().getDetailedState();
   1166                         handleDisconnectedState(DetailedState.DISCONNECTED, false);
   1167                         setDetailedStateInternal(saveState);
   1168                     }
   1169 
   1170                     configureInterface();
   1171                     mLastBssid = result.BSSID;
   1172                     mLastSsid = mWifiInfo.getSSID();
   1173                     mLastNetworkId = result.networkId;
   1174                     if (mHaveIpAddress) {
   1175                         setDetailedState(DetailedState.CONNECTED);
   1176                     } else {
   1177                         setDetailedState(DetailedState.OBTAINING_IPADDR);
   1178                     }
   1179                 }
   1180                 sendNetworkStateChangeBroadcast(mWifiInfo.getBSSID());
   1181                 break;
   1182 
   1183             case EVENT_SCAN_RESULTS_AVAILABLE:
   1184                 if (ActivityManagerNative.isSystemReady()) {
   1185                     mContext.sendBroadcast(new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
   1186                 }
   1187                 sendScanResultsAvailable();
   1188                 /**
   1189                  * On receiving the first scan results after connecting to
   1190                  * the supplicant, switch scan mode over to passive.
   1191                  */
   1192                 setScanMode(false);
   1193                 break;
   1194 
   1195             case EVENT_POLL_INTERVAL:
   1196                 if (mWifiInfo.getSupplicantState() != SupplicantState.UNINITIALIZED) {
   1197                     requestPolledInfo(mWifiInfo, true);
   1198                     checkPollTimer();
   1199                 }
   1200                 break;
   1201 
   1202             case EVENT_DEFERRED_DISCONNECT:
   1203                 if (mWifiInfo.getSupplicantState() != SupplicantState.UNINITIALIZED) {
   1204                     handleDisconnectedState(DetailedState.DISCONNECTED, true);
   1205                 }
   1206                 break;
   1207 
   1208             case EVENT_DEFERRED_RECONNECT:
   1209                 /**
   1210                  * mLastBssid can be null when there is a reconnect
   1211                  * request on the first BSSID we connect to
   1212                  */
   1213                 String BSSID = (msg.obj != null) ? msg.obj.toString() : null;
   1214                 /**
   1215                  * If we've exceeded the maximum number of retries for reconnecting
   1216                  * to a given network, disable the network
   1217                  */
   1218                 if (mWifiInfo.getSupplicantState() != SupplicantState.UNINITIALIZED) {
   1219                     if (++mReconnectCount > getMaxDhcpRetries()) {
   1220                         if (LOCAL_LOGD) {
   1221                             Log.d(TAG, "Failed reconnect count: " +
   1222                                     mReconnectCount + " Disabling " + BSSID);
   1223                         }
   1224                         mWM.disableNetwork(mLastNetworkId);
   1225                     }
   1226                     reconnectCommand();
   1227                 }
   1228                 break;
   1229 
   1230             case EVENT_INTERFACE_CONFIGURATION_SUCCEEDED:
   1231                 /**
   1232                  * Since this event is sent from another thread, it might have been
   1233                  * sent after we closed our connection to the supplicant in the course
   1234                  * of disabling Wi-Fi. In that case, we should just ignore the event.
   1235                  */
   1236                 if (mWifiInfo.getSupplicantState() == SupplicantState.UNINITIALIZED) {
   1237                     break;
   1238                 }
   1239                 mReconnectCount = 0;
   1240                 mHaveIpAddress = true;
   1241                 mObtainingIpAddress = false;
   1242                 mWifiInfo.setIpAddress(mDhcpInfo.ipAddress);
   1243                 mLastSignalLevel = -1; // force update of signal strength
   1244                 if (mNetworkInfo.getDetailedState() != DetailedState.CONNECTED) {
   1245                     setDetailedState(DetailedState.CONNECTED);
   1246                     sendNetworkStateChangeBroadcast(mWifiInfo.getBSSID());
   1247                 } else {
   1248                     msg = mTarget.obtainMessage(EVENT_CONFIGURATION_CHANGED, mNetworkInfo);
   1249                     msg.sendToTarget();
   1250                 }
   1251                 if (LOCAL_LOGD) Log.v(TAG, "IP configuration: " + mDhcpInfo);
   1252                 // Wi-Fi interface configuration state changed:
   1253                 // [31- 1] Reserved for future use
   1254                 // [ 0- 0] Interface configuration succeeded (1) or failed (0)
   1255                 EventLog.writeEvent(EVENTLOG_INTERFACE_CONFIGURATION_STATE_CHANGED, 1);
   1256 
   1257                 // We've connected successfully, so allow the notification again in the future
   1258                 resetNotificationTimer();
   1259                 break;
   1260 
   1261             case EVENT_INTERFACE_CONFIGURATION_FAILED:
   1262                 if (mWifiInfo.getSupplicantState() != SupplicantState.UNINITIALIZED) {
   1263                     // Wi-Fi interface configuration state changed:
   1264                     // [31- 1] Reserved for future use
   1265                     // [ 0- 0] Interface configuration succeeded (1) or failed (0)
   1266                     EventLog.writeEvent(EVENTLOG_INTERFACE_CONFIGURATION_STATE_CHANGED, 0);
   1267                     mHaveIpAddress = false;
   1268                     mWifiInfo.setIpAddress(0);
   1269                     mObtainingIpAddress = false;
   1270                     disconnect();
   1271                 }
   1272                 break;
   1273 
   1274             case EVENT_DRIVER_STATE_CHANGED:
   1275                 // Wi-Fi driver state changed:
   1276                 // 0 STARTED
   1277                 // 1 STOPPED
   1278                 // 2 HUNG
   1279                 EventLog.writeEvent(EVENTLOG_DRIVER_STATE_CHANGED, msg.arg1);
   1280 
   1281                 switch (msg.arg1) {
   1282                 case DRIVER_STARTED:
   1283                     /**
   1284                      * Set the number of allowed radio channels according
   1285                      * to the system setting, since it gets reset by the
   1286                      * driver upon changing to the STARTED state.
   1287                      */
   1288                     setNumAllowedChannels();
   1289                     synchronized (this) {
   1290                         macaddr = WifiNative.getMacAddressCommand();
   1291                         if (macaddr != null) {
   1292                             mWifiInfo.setMacAddress(macaddr);
   1293                         }
   1294                         mRunState = RUN_STATE_RUNNING;
   1295                         if (!mIsScanOnly) {
   1296                             reconnectCommand();
   1297                         } else {
   1298                             // In some situations, supplicant needs to be kickstarted to
   1299                             // start the background scanning
   1300                             scan(true);
   1301                         }
   1302                     }
   1303                     break;
   1304                 case DRIVER_HUNG:
   1305                     Log.e(TAG, "Wifi Driver reports HUNG - reloading.");
   1306                     /**
   1307                      * restart the driver - toggle off and on
   1308                      */
   1309                     mWM.setWifiEnabled(false);
   1310                     mWM.setWifiEnabled(true);
   1311                     break;
   1312                 }
   1313                 synchronized (this) {
   1314                     updateBatteryWorkSourceLocked(null);
   1315                 }
   1316                 break;
   1317 
   1318             case EVENT_PASSWORD_KEY_MAY_BE_INCORRECT:
   1319                 mPasswordKeyMayBeIncorrect = true;
   1320                 break;
   1321         }
   1322     }
   1323 
   1324     private boolean wifiManagerDisableNetwork(int networkId) {
   1325         boolean disabledNetwork = false;
   1326         if (0 <= networkId) {
   1327             disabledNetwork = mWM.disableNetwork(networkId);
   1328             if (LOCAL_LOGD) {
   1329                 if (disabledNetwork) {
   1330                     Log.v(TAG, "Disabled network: " + networkId);
   1331                 }
   1332             }
   1333         }
   1334         if (LOCAL_LOGD) {
   1335             if (!disabledNetwork) {
   1336                 Log.e(TAG, "Failed to disable network:" +
   1337                       " invalid network id: " + networkId);
   1338             }
   1339         }
   1340         return disabledNetwork;
   1341     }
   1342 
   1343     private void configureInterface() {
   1344         checkPollTimer();
   1345         mLastSignalLevel = -1;
   1346         if (!mUseStaticIp) {
   1347             if (!mHaveIpAddress && !mObtainingIpAddress) {
   1348                 mObtainingIpAddress = true;
   1349                 mDhcpTarget.sendEmptyMessage(EVENT_DHCP_START);
   1350             }
   1351         } else {
   1352             int event;
   1353             if (NetworkUtils.configureInterface(mInterfaceName, mDhcpInfo)) {
   1354                 mHaveIpAddress = true;
   1355                 event = EVENT_INTERFACE_CONFIGURATION_SUCCEEDED;
   1356                 if (LOCAL_LOGD) Log.v(TAG, "Static IP configuration succeeded");
   1357             } else {
   1358                 mHaveIpAddress = false;
   1359                 event = EVENT_INTERFACE_CONFIGURATION_FAILED;
   1360                 if (LOCAL_LOGD) Log.v(TAG, "Static IP configuration failed");
   1361             }
   1362             sendEmptyMessage(event);
   1363         }
   1364     }
   1365 
   1366     /**
   1367      * Reset our IP state and send out broadcasts following a disconnect.
   1368      * @param newState the {@code DetailedState} to set. Should be either
   1369      * {@code DISCONNECTED} or {@code FAILED}.
   1370      * @param disableInterface indicates whether the interface should
   1371      * be disabled
   1372      */
   1373     private void handleDisconnectedState(DetailedState newState, boolean disableInterface) {
   1374         if (mDisconnectPending) {
   1375             cancelDisconnect();
   1376         }
   1377         mDisconnectExpected = false;
   1378         resetConnections(disableInterface);
   1379         setDetailedState(newState);
   1380         sendNetworkStateChangeBroadcast(mLastBssid);
   1381         mWifiInfo.setBSSID(null);
   1382         mLastBssid = null;
   1383         mLastSsid = null;
   1384         mDisconnectPending = false;
   1385     }
   1386 
   1387     /**
   1388      * Resets the Wi-Fi Connections by clearing any state, resetting any sockets
   1389      * using the interface, stopping DHCP, and disabling the interface.
   1390      */
   1391     public void resetConnections(boolean disableInterface) {
   1392         if (LOCAL_LOGD) Log.d(TAG, "Reset connections and stopping DHCP");
   1393         mHaveIpAddress = false;
   1394         mObtainingIpAddress = false;
   1395         mWifiInfo.setIpAddress(0);
   1396 
   1397         /*
   1398          * Reset connection depends on both the interface and the IP assigned,
   1399          * so it should be done before any chance of the IP being lost.
   1400          */
   1401         NetworkUtils.resetConnections(mInterfaceName);
   1402 
   1403         // Stop DHCP
   1404         if (mDhcpTarget != null) {
   1405             mDhcpTarget.setCancelCallback(true);
   1406             mDhcpTarget.removeMessages(EVENT_DHCP_START);
   1407         }
   1408         if (!NetworkUtils.stopDhcp(mInterfaceName)) {
   1409             Log.e(TAG, "Could not stop DHCP");
   1410         }
   1411 
   1412         /**
   1413          * Interface is re-enabled in the supplicant
   1414          * when moving out of ASSOCIATING state
   1415          */
   1416         if(disableInterface) {
   1417             if (LOCAL_LOGD) Log.d(TAG, "Disabling interface");
   1418             NetworkUtils.disableInterface(mInterfaceName);
   1419         }
   1420     }
   1421 
   1422     /**
   1423      * The supplicant is reporting that we are disconnected from the current
   1424      * access point. Often, however, a disconnect will be followed very shortly
   1425      * by a reconnect to the same access point. Therefore, we delay resetting
   1426      * the connection's IP state for a bit.
   1427      */
   1428     private void scheduleDisconnect() {
   1429         mDisconnectPending = true;
   1430         if (!hasMessages(EVENT_DEFERRED_DISCONNECT)) {
   1431             sendEmptyMessageDelayed(EVENT_DEFERRED_DISCONNECT, DISCONNECT_DELAY_MSECS);
   1432         }
   1433     }
   1434 
   1435     private void cancelDisconnect() {
   1436         mDisconnectPending = false;
   1437         removeMessages(EVENT_DEFERRED_DISCONNECT);
   1438     }
   1439 
   1440     public DhcpInfo getDhcpInfo() {
   1441         return mDhcpInfo;
   1442     }
   1443 
   1444     public synchronized List<ScanResult> getScanResultsList() {
   1445         return mScanResults;
   1446     }
   1447 
   1448     public synchronized void setScanResultsList(List<ScanResult> scanList) {
   1449         mScanResults = scanList;
   1450     }
   1451 
   1452     /**
   1453      * Get status information for the current connection, if any.
   1454      * @return a {@link WifiInfo} object containing information about the current connection
   1455      */
   1456     public WifiInfo requestConnectionInfo() {
   1457         requestConnectionStatus(mWifiInfo);
   1458         requestPolledInfo(mWifiInfo, false);
   1459         return mWifiInfo;
   1460     }
   1461 
   1462     private void requestConnectionStatus(WifiInfo info) {
   1463         String reply = status();
   1464         if (reply == null) {
   1465             return;
   1466         }
   1467         /*
   1468          * Parse the reply from the supplicant to the status command, and update
   1469          * local state accordingly. The reply is a series of lines of the form
   1470          * "name=value".
   1471          */
   1472         String SSID = null;
   1473         String BSSID = null;
   1474         String suppState = null;
   1475         int netId = -1;
   1476         String[] lines = reply.split("\n");
   1477         for (String line : lines) {
   1478             String[] prop = line.split(" *= *");
   1479             if (prop.length < 2)
   1480                 continue;
   1481             String name = prop[0];
   1482             String value = prop[1];
   1483             if (name.equalsIgnoreCase("id"))
   1484                 netId = Integer.parseInt(value);
   1485             else if (name.equalsIgnoreCase("ssid"))
   1486                 SSID = value;
   1487             else if (name.equalsIgnoreCase("bssid"))
   1488                 BSSID = value;
   1489             else if (name.equalsIgnoreCase("wpa_state"))
   1490                 suppState = value;
   1491         }
   1492         info.setNetworkId(netId);
   1493         info.setSSID(SSID);
   1494         info.setBSSID(BSSID);
   1495         /*
   1496          * We only set the supplicant state if the previous state was
   1497          * UNINITIALIZED. This should only happen when we first connect to
   1498          * the supplicant. Once we're connected, we should always receive
   1499          * an event upon any state change, but in this case, we want to
   1500          * make sure any listeners are made aware of the state change.
   1501          */
   1502         if (mWifiInfo.getSupplicantState() == SupplicantState.UNINITIALIZED && suppState != null)
   1503             setSupplicantState(suppState);
   1504     }
   1505 
   1506     /**
   1507      * Get the dynamic information that is not reported via events.
   1508      * @param info the object into which the information should be captured.
   1509      */
   1510     private synchronized void requestPolledInfo(WifiInfo info, boolean polling)
   1511     {
   1512         int newRssi = (polling ? getRssiApprox() : getRssi());
   1513         if (newRssi != -1 && -200 < newRssi && newRssi < 256) { // screen out invalid values
   1514             /* some implementations avoid negative values by adding 256
   1515              * so we need to adjust for that here.
   1516              */
   1517             if (newRssi > 0) newRssi -= 256;
   1518             info.setRssi(newRssi);
   1519             /*
   1520              * Rather then sending the raw RSSI out every time it
   1521              * changes, we precalculate the signal level that would
   1522              * be displayed in the status bar, and only send the
   1523              * broadcast if that much more coarse-grained number
   1524              * changes. This cuts down greatly on the number of
   1525              * broadcasts, at the cost of not informing others
   1526              * interested in RSSI of all the changes in signal
   1527              * level.
   1528              */
   1529             // TODO: The second arg to the call below needs to be a symbol somewhere, but
   1530             // it's actually the size of an array of icons that's private
   1531             // to StatusBar Policy.
   1532             int newSignalLevel = WifiManager.calculateSignalLevel(newRssi, 4);
   1533             if (newSignalLevel != mLastSignalLevel) {
   1534                 sendRssiChangeBroadcast(newRssi);
   1535             }
   1536             mLastSignalLevel = newSignalLevel;
   1537         } else {
   1538             info.setRssi(-200);
   1539         }
   1540         int newLinkSpeed = getLinkSpeed();
   1541         if (newLinkSpeed != -1) {
   1542             info.setLinkSpeed(newLinkSpeed);
   1543         }
   1544     }
   1545 
   1546     private void sendRssiChangeBroadcast(final int newRssi) {
   1547         if (ActivityManagerNative.isSystemReady()) {
   1548             Intent intent = new Intent(WifiManager.RSSI_CHANGED_ACTION);
   1549             intent.putExtra(WifiManager.EXTRA_NEW_RSSI, newRssi);
   1550             mContext.sendBroadcast(intent);
   1551         }
   1552     }
   1553 
   1554     private void sendNetworkStateChangeBroadcast(String bssid) {
   1555         Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
   1556         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
   1557                 | Intent.FLAG_RECEIVER_REPLACE_PENDING);
   1558         intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, mNetworkInfo);
   1559         if (bssid != null)
   1560             intent.putExtra(WifiManager.EXTRA_BSSID, bssid);
   1561         mContext.sendStickyBroadcast(intent);
   1562     }
   1563 
   1564     /**
   1565      * Disable Wi-Fi connectivity by stopping the driver.
   1566      */
   1567     public boolean teardown() {
   1568         if (!mTornDownByConnMgr) {
   1569             if (disconnectAndStop()) {
   1570                 setTornDownByConnMgr(true);
   1571                 return true;
   1572             } else {
   1573                 return false;
   1574             }
   1575         } else {
   1576             return true;
   1577         }
   1578     }
   1579 
   1580     /**
   1581      * Reenable Wi-Fi connectivity by restarting the driver.
   1582      */
   1583     public boolean reconnect() {
   1584         if (mTornDownByConnMgr) {
   1585             if (restart()) {
   1586                 setTornDownByConnMgr(false);
   1587                 return true;
   1588             } else {
   1589                 return false;
   1590             }
   1591         } else {
   1592             return true;
   1593         }
   1594     }
   1595 
   1596     /**
   1597      * We want to stop the driver, but if we're connected to a network,
   1598      * we first want to disconnect, so that the supplicant is always in
   1599      * a known state (DISCONNECTED) when the driver is stopped.
   1600      * @return {@code true} if the operation succeeds, which means that the
   1601      * disconnect or stop command was initiated.
   1602      */
   1603     public synchronized boolean disconnectAndStop() {
   1604         boolean ret = true;;
   1605         if (mRunState != RUN_STATE_STOPPING && mRunState != RUN_STATE_STOPPED) {
   1606             // Take down any open network notifications
   1607             setNotificationVisible(false, 0, false, 0);
   1608 
   1609             if (mWifiInfo.getSupplicantState() == SupplicantState.DORMANT) {
   1610                 ret = stopDriver();
   1611             } else {
   1612                 ret = disconnect();
   1613             }
   1614             mRunState = RUN_STATE_STOPPING;
   1615         }
   1616         return ret;
   1617     }
   1618 
   1619     public synchronized boolean restart() {
   1620         if (isDriverStopped()) {
   1621             mRunState = RUN_STATE_STARTING;
   1622             resetConnections(true);
   1623             return startDriver();
   1624         }
   1625         return true;
   1626     }
   1627 
   1628     public int getWifiState() {
   1629         return mWifiState.get();
   1630     }
   1631 
   1632     public void setWifiState(int wifiState) {
   1633         mWifiState.set(wifiState);
   1634     }
   1635 
   1636     public boolean isAnyNetworkDisabled() {
   1637         return mIsAnyNetworkDisabled.get();
   1638     }
   1639 
   1640    /**
   1641      * The WifiNative interface functions are listed below.
   1642      * The only native call that is not synchronized on
   1643      * WifiStateTracker is waitForEvent() which waits on a
   1644      * seperate monitor channel.
   1645      *
   1646      * All supplicant commands need the wifi to be in an
   1647      * enabled state. This can be done by checking the
   1648      * mWifiState to be WIFI_STATE_ENABLED.
   1649      *
   1650      * All commands that can cause commands to driver
   1651      * initiated need the driver state to be started.
   1652      * This is done by checking isDriverStopped() to
   1653      * be false.
   1654      */
   1655 
   1656     /**
   1657      * Load the driver and firmware
   1658      *
   1659      * @return {@code true} if the operation succeeds, {@code false} otherwise
   1660      */
   1661     public synchronized boolean loadDriver() {
   1662         return WifiNative.loadDriver();
   1663     }
   1664 
   1665     /**
   1666      * Unload the driver and firmware
   1667      *
   1668      * @return {@code true} if the operation succeeds, {@code false} otherwise
   1669      */
   1670     public synchronized boolean unloadDriver() {
   1671         return WifiNative.unloadDriver();
   1672     }
   1673 
   1674     /**
   1675      * Check the supplicant config and
   1676      * start the supplicant daemon
   1677      *
   1678      * @return {@code true} if the operation succeeds, {@code false} otherwise
   1679      */
   1680     public synchronized boolean startSupplicant() {
   1681         return WifiNative.startSupplicant();
   1682     }
   1683 
   1684     /**
   1685      * Stop the supplicant daemon
   1686      *
   1687      * @return {@code true} if the operation succeeds, {@code false} otherwise
   1688      */
   1689     public synchronized boolean stopSupplicant() {
   1690         return WifiNative.stopSupplicant();
   1691     }
   1692 
   1693     /**
   1694      * Establishes two channels - control channel for commands
   1695      * and monitor channel for notifying WifiMonitor
   1696      *
   1697      * @return {@code true} if the operation succeeds, {@code false} otherwise
   1698      */
   1699     public synchronized boolean connectToSupplicant() {
   1700         return WifiNative.connectToSupplicant();
   1701     }
   1702 
   1703     /**
   1704      * Close the control/monitor channels to supplicant
   1705      */
   1706     public synchronized void closeSupplicantConnection() {
   1707         WifiNative.closeSupplicantConnection();
   1708     }
   1709 
   1710     /**
   1711      * Check if the supplicant is alive
   1712      *
   1713      * @return {@code true} if the operation succeeds, {@code false} otherwise
   1714      */
   1715     public synchronized boolean ping() {
   1716         if (mWifiState.get() != WIFI_STATE_ENABLED) {
   1717             return false;
   1718         }
   1719         return WifiNative.pingCommand();
   1720     }
   1721 
   1722     /**
   1723      * initiate an active or passive scan
   1724      *
   1725      * @param forceActive true if it is a active scan
   1726      * @return {@code true} if the operation succeeds, {@code false} otherwise
   1727      */
   1728     public synchronized boolean scan(boolean forceActive) {
   1729         if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
   1730             return false;
   1731         }
   1732         return WifiNative.scanCommand(forceActive);
   1733     }
   1734 
   1735     /**
   1736      * Specifies whether the supplicant or driver
   1737      * take care of initiating scan and doing AP selection
   1738      *
   1739      * @param mode
   1740      *    SUPPL_SCAN_HANDLING_NORMAL
   1741      *    SUPPL_SCAN_HANDLING_LIST_ONLY
   1742      * @return {@code true} if the operation succeeds, {@code false} otherwise
   1743      */
   1744     public synchronized boolean setScanResultHandling(int mode) {
   1745         if (mWifiState.get() != WIFI_STATE_ENABLED) {
   1746             return false;
   1747         }
   1748         return WifiNative.setScanResultHandlingCommand(mode);
   1749     }
   1750 
   1751     /**
   1752      * Fetch the scan results from the supplicant
   1753      *
   1754      * @return example result string
   1755      * 00:bb:cc:dd:cc:ee       2427    166     [WPA-EAP-TKIP][WPA2-EAP-CCMP]   Net1
   1756      * 00:bb:cc:dd:cc:ff       2412    165     [WPA-EAP-TKIP][WPA2-EAP-CCMP]   Net2
   1757      */
   1758     public synchronized String scanResults() {
   1759         if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
   1760             return null;
   1761         }
   1762         return WifiNative.scanResultsCommand();
   1763     }
   1764 
   1765     /**
   1766      * Set the scan mode - active or passive
   1767      *
   1768      * @return {@code true} if the operation succeeds, {@code false} otherwise
   1769      */
   1770     public synchronized boolean setScanMode(boolean isScanModeActive) {
   1771         if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
   1772             return false;
   1773         }
   1774         if (mIsScanModeActive != isScanModeActive) {
   1775             return WifiNative.setScanModeCommand(mIsScanModeActive = isScanModeActive);
   1776         }
   1777         return true;
   1778     }
   1779 
   1780     /**
   1781      * Disconnect from Access Point
   1782      *
   1783      * @return {@code true} if the operation succeeds, {@code false} otherwise
   1784      */
   1785     public synchronized boolean disconnect() {
   1786         if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
   1787             return false;
   1788         }
   1789         return WifiNative.disconnectCommand();
   1790     }
   1791 
   1792     /**
   1793      * Initiate a reconnection to AP
   1794      *
   1795      * @return {@code true} if the operation succeeds, {@code false} otherwise
   1796      */
   1797     public synchronized boolean reconnectCommand() {
   1798         if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
   1799             return false;
   1800         }
   1801         return WifiNative.reconnectCommand();
   1802     }
   1803 
   1804     /**
   1805      * Add a network
   1806      *
   1807      * @return network id of the new network
   1808      */
   1809     public synchronized int addNetwork() {
   1810         if (mWifiState.get() != WIFI_STATE_ENABLED) {
   1811             return -1;
   1812         }
   1813         return WifiNative.addNetworkCommand();
   1814     }
   1815 
   1816     /**
   1817      * Delete a network
   1818      *
   1819      * @param networkId id of the network to be removed
   1820      * @return {@code true} if the operation succeeds, {@code false} otherwise
   1821      */
   1822     public synchronized boolean removeNetwork(int networkId) {
   1823         if (mWifiState.get() != WIFI_STATE_ENABLED) {
   1824             return false;
   1825         }
   1826         return mDisconnectExpected = WifiNative.removeNetworkCommand(networkId);
   1827     }
   1828 
   1829     /**
   1830      * Enable a network
   1831      *
   1832      * @param netId network id of the network
   1833      * @param disableOthers true, if all other networks have to be disabled
   1834      * @return {@code true} if the operation succeeds, {@code false} otherwise
   1835      */
   1836     public synchronized boolean enableNetwork(int netId, boolean disableOthers) {
   1837         if (mWifiState.get() != WIFI_STATE_ENABLED) {
   1838             return false;
   1839         }
   1840         if (disableOthers) mIsAnyNetworkDisabled.set(true);
   1841         return WifiNative.enableNetworkCommand(netId, disableOthers);
   1842     }
   1843 
   1844     /**
   1845      * Enable all networks
   1846      *
   1847      * @param networks list of configured networks
   1848      */
   1849     public synchronized void enableAllNetworks(List<WifiConfiguration> networks) {
   1850         if (mWifiState.get() != WIFI_STATE_ENABLED) {
   1851             return;
   1852         }
   1853         mIsAnyNetworkDisabled.set(false);
   1854         for (WifiConfiguration config : networks) {
   1855             if (config.status == WifiConfiguration.Status.DISABLED) {
   1856                 WifiNative.enableNetworkCommand(config.networkId, false);
   1857             }
   1858         }
   1859     }
   1860 
   1861     /**
   1862      * Disable a network
   1863      *
   1864      * @param netId network id of the network
   1865      * @return {@code true} if the operation succeeds, {@code false} otherwise
   1866      */
   1867     public synchronized boolean disableNetwork(int netId) {
   1868         if (mWifiState.get() != WIFI_STATE_ENABLED) {
   1869             return false;
   1870         }
   1871         mIsAnyNetworkDisabled.set(true);
   1872         return WifiNative.disableNetworkCommand(netId);
   1873     }
   1874 
   1875     /**
   1876      * Initiate a re-association in supplicant
   1877      *
   1878      * @return {@code true} if the operation succeeds, {@code false} otherwise
   1879      */
   1880     public synchronized boolean reassociate() {
   1881         if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
   1882             return false;
   1883         }
   1884         return WifiNative.reassociateCommand();
   1885     }
   1886 
   1887     /**
   1888      * Blacklist a BSSID. This will avoid the AP if there are
   1889      * alternate APs to connect
   1890      *
   1891      * @param bssid BSSID of the network
   1892      * @return {@code true} if the operation succeeds, {@code false} otherwise
   1893      */
   1894     public synchronized boolean addToBlacklist(String bssid) {
   1895         if (mWifiState.get() != WIFI_STATE_ENABLED) {
   1896             return false;
   1897         }
   1898         return WifiNative.addToBlacklistCommand(bssid);
   1899     }
   1900 
   1901     /**
   1902      * Clear the blacklist list
   1903      *
   1904      * @return {@code true} if the operation succeeds, {@code false} otherwise
   1905      */
   1906     public synchronized boolean clearBlacklist() {
   1907         if (mWifiState.get() != WIFI_STATE_ENABLED) {
   1908             return false;
   1909         }
   1910         return WifiNative.clearBlacklistCommand();
   1911     }
   1912 
   1913     /**
   1914      * List all configured networks
   1915      *
   1916      * @return list of networks or null on failure
   1917      */
   1918     public synchronized String listNetworks() {
   1919         if (mWifiState.get() != WIFI_STATE_ENABLED) {
   1920             return null;
   1921         }
   1922         return WifiNative.listNetworksCommand();
   1923     }
   1924 
   1925     /**
   1926      * Get network setting by name
   1927      *
   1928      * @param netId network id of the network
   1929      * @param name network variable key
   1930      * @return value corresponding to key
   1931      */
   1932     public synchronized String getNetworkVariable(int netId, String name) {
   1933         if (mWifiState.get() != WIFI_STATE_ENABLED) {
   1934             return null;
   1935         }
   1936         return WifiNative.getNetworkVariableCommand(netId, name);
   1937     }
   1938 
   1939     /**
   1940      * Set network setting by name
   1941      *
   1942      * @param netId network id of the network
   1943      * @param name network variable key
   1944      * @param value network variable value
   1945      * @return {@code true} if the operation succeeds, {@code false} otherwise
   1946      */
   1947     public synchronized boolean setNetworkVariable(int netId, String name, String value) {
   1948         if (mWifiState.get() != WIFI_STATE_ENABLED) {
   1949             return false;
   1950         }
   1951         return WifiNative.setNetworkVariableCommand(netId, name, value);
   1952     }
   1953 
   1954     /**
   1955      * Get detailed status of the connection
   1956      *
   1957      * @return Example status result
   1958      *  bssid=aa:bb:cc:dd:ee:ff
   1959      *  ssid=TestNet
   1960      *  id=3
   1961      *  pairwise_cipher=NONE
   1962      *  group_cipher=NONE
   1963      *  key_mgmt=NONE
   1964      *  wpa_state=COMPLETED
   1965      *  ip_address=X.X.X.X
   1966      */
   1967     public synchronized String status() {
   1968         if (mWifiState.get() != WIFI_STATE_ENABLED) {
   1969             return null;
   1970         }
   1971         return WifiNative.statusCommand();
   1972     }
   1973 
   1974     /**
   1975      * Get RSSI to currently connected network
   1976      *
   1977      * @return RSSI value, -1 on failure
   1978      */
   1979     public synchronized int getRssi() {
   1980         if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
   1981             return -1;
   1982         }
   1983         return WifiNative.getRssiApproxCommand();
   1984     }
   1985 
   1986     /**
   1987      * Get approx RSSI to currently connected network
   1988      *
   1989      * @return RSSI value, -1 on failure
   1990      */
   1991     public synchronized int getRssiApprox() {
   1992         if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
   1993             return -1;
   1994         }
   1995         return WifiNative.getRssiApproxCommand();
   1996     }
   1997 
   1998     /**
   1999      * Get link speed to currently connected network
   2000      *
   2001      * @return link speed, -1 on failure
   2002      */
   2003     public synchronized int getLinkSpeed() {
   2004         if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
   2005             return -1;
   2006         }
   2007         return WifiNative.getLinkSpeedCommand();
   2008     }
   2009 
   2010     /**
   2011      * Start driver
   2012      *
   2013      * @return {@code true} if the operation succeeds, {@code false} otherwise
   2014      */
   2015     public synchronized boolean startDriver() {
   2016         if (mWifiState.get() != WIFI_STATE_ENABLED) {
   2017             return false;
   2018         }
   2019         return WifiNative.startDriverCommand();
   2020     }
   2021 
   2022     /**
   2023      * Stop driver
   2024      *
   2025      * @return {@code true} if the operation succeeds, {@code false} otherwise
   2026      */
   2027     public synchronized boolean stopDriver() {
   2028         /* Driver stop should not happen only when supplicant event
   2029          * DRIVER_STOPPED has already been handled */
   2030         if (mWifiState.get() != WIFI_STATE_ENABLED || mRunState == RUN_STATE_STOPPED) {
   2031             return false;
   2032         }
   2033         return WifiNative.stopDriverCommand();
   2034     }
   2035 
   2036     /**
   2037      * Start packet filtering
   2038      *
   2039      * @return {@code true} if the operation succeeds, {@code false} otherwise
   2040      */
   2041     public synchronized boolean startPacketFiltering() {
   2042         if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
   2043             return false;
   2044         }
   2045         return WifiNative.startPacketFiltering();
   2046     }
   2047 
   2048     /**
   2049      * Stop packet filtering
   2050      *
   2051      * @return {@code true} if the operation succeeds, {@code false} otherwise
   2052      */
   2053     public synchronized boolean stopPacketFiltering() {
   2054         if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
   2055             return false;
   2056         }
   2057         return WifiNative.stopPacketFiltering();
   2058     }
   2059 
   2060     /**
   2061      * Get power mode
   2062      * @return power mode
   2063      */
   2064     public synchronized int getPowerMode() {
   2065         if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) {
   2066             return -1;
   2067         }
   2068         return WifiNative.getPowerModeCommand();
   2069     }
   2070 
   2071     /**
   2072      * Set power mode
   2073      * @param mode
   2074      *     DRIVER_POWER_MODE_AUTO
   2075      *     DRIVER_POWER_MODE_ACTIVE
   2076      *
   2077      * Uses reference counting to keep power mode active
   2078      * as long as one entity wants power mode to be active.
   2079      *
   2080      * For example, WifiLock high perf mode can keep power mode active
   2081      * or a DHCP session can keep it active. As long as one entity wants
   2082      * it enabled, it should stay that way
   2083      *
   2084      */
   2085     private synchronized void setPowerMode(int mode) {
   2086 
   2087         /* It is good to plumb power mode change
   2088          * even if ref count indicates already done
   2089          * since we could have a case of previous failure.
   2090          */
   2091         switch(mode) {
   2092             case DRIVER_POWER_MODE_ACTIVE:
   2093                 mPowerModeRefCount++;
   2094                 break;
   2095             case DRIVER_POWER_MODE_AUTO:
   2096                 mPowerModeRefCount--;
   2097                 if (mPowerModeRefCount > 0) {
   2098                     return;
   2099                 } else {
   2100                     /* Keep refcount from becoming negative */
   2101                     mPowerModeRefCount = 0;
   2102                 }
   2103                 break;
   2104         }
   2105 
   2106         if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
   2107             return;
   2108         }
   2109 
   2110         WifiNative.setPowerModeCommand(mode);
   2111     }
   2112 
   2113     /**
   2114      * Set the number of allowed radio frequency channels from the system
   2115      * setting value, if any.
   2116      * @return {@code true} if the operation succeeds, {@code false} otherwise, e.g.,
   2117      * the number of channels is invalid.
   2118      */
   2119     public synchronized boolean setNumAllowedChannels() {
   2120         if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
   2121             return false;
   2122         }
   2123         try {
   2124             return setNumAllowedChannels(
   2125                     Settings.Secure.getInt(mContext.getContentResolver(),
   2126                     Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS));
   2127         } catch (Settings.SettingNotFoundException e) {
   2128             if (mNumAllowedChannels != 0) {
   2129                 WifiNative.setNumAllowedChannelsCommand(mNumAllowedChannels);
   2130             }
   2131             // otherwise, use the driver default
   2132         }
   2133         return true;
   2134     }
   2135 
   2136     /**
   2137      * Set the number of radio frequency channels that are allowed to be used
   2138      * in the current regulatory domain.
   2139      * @param numChannels the number of allowed channels. Must be greater than 0
   2140      * and less than or equal to 16.
   2141      * @return {@code true} if the operation succeeds, {@code false} otherwise, e.g.,
   2142      * {@code numChannels} is outside the valid range.
   2143      */
   2144     public synchronized boolean setNumAllowedChannels(int numChannels) {
   2145         if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
   2146             return false;
   2147         }
   2148         mNumAllowedChannels = numChannels;
   2149         return WifiNative.setNumAllowedChannelsCommand(numChannels);
   2150     }
   2151 
   2152     /**
   2153      * Get number of allowed channels
   2154      *
   2155      * @return channel count, -1 on failure
   2156      */
   2157     public synchronized int getNumAllowedChannels() {
   2158         if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
   2159             return -1;
   2160         }
   2161         return WifiNative.getNumAllowedChannelsCommand();
   2162     }
   2163 
   2164     /**
   2165      * Set bluetooth coex mode:
   2166      *
   2167      * @param mode
   2168      *  BLUETOOTH_COEXISTENCE_MODE_ENABLED
   2169      *  BLUETOOTH_COEXISTENCE_MODE_DISABLED
   2170      *  BLUETOOTH_COEXISTENCE_MODE_SENSE
   2171      * @return {@code true} if the operation succeeds, {@code false} otherwise
   2172      */
   2173     public synchronized boolean setBluetoothCoexistenceMode(int mode) {
   2174         if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
   2175             return false;
   2176         }
   2177         return WifiNative.setBluetoothCoexistenceModeCommand(mode);
   2178     }
   2179 
   2180     /**
   2181      * Enable or disable Bluetooth coexistence scan mode. When this mode is on,
   2182      * some of the low-level scan parameters used by the driver are changed to
   2183      * reduce interference with A2DP streaming.
   2184      *
   2185      * @param isBluetoothPlaying whether to enable or disable this mode
   2186      */
   2187     public synchronized void setBluetoothScanMode(boolean isBluetoothPlaying) {
   2188         if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
   2189             return;
   2190         }
   2191         WifiNative.setBluetoothCoexistenceScanModeCommand(isBluetoothPlaying);
   2192     }
   2193 
   2194     /**
   2195      * Save configuration on supplicant
   2196      *
   2197      * @return {@code true} if the operation succeeds, {@code false} otherwise
   2198      */
   2199     public synchronized boolean saveConfig() {
   2200         if (mWifiState.get() != WIFI_STATE_ENABLED) {
   2201             return false;
   2202         }
   2203         return WifiNative.saveConfigCommand();
   2204     }
   2205 
   2206     /**
   2207      * Reload the configuration from file
   2208      *
   2209      * @return {@code true} if the operation succeeds, {@code false} otherwise
   2210      */
   2211     public synchronized boolean reloadConfig() {
   2212         if (mWifiState.get() != WIFI_STATE_ENABLED) {
   2213             return false;
   2214         }
   2215         return WifiNative.reloadConfigCommand();
   2216     }
   2217 
   2218     public boolean setRadio(boolean turnOn) {
   2219         return mWM.setWifiEnabled(turnOn);
   2220     }
   2221 
   2222     /**
   2223      * {@inheritDoc}
   2224      * There are currently no Wi-Fi-specific features supported.
   2225      * @param feature the name of the feature
   2226      * @return {@code -1} indicating failure, always
   2227      */
   2228     public int startUsingNetworkFeature(String feature, int callingPid, int callingUid) {
   2229         return -1;
   2230     }
   2231 
   2232     /**
   2233      * {@inheritDoc}
   2234      * There are currently no Wi-Fi-specific features supported.
   2235      * @param feature the name of the feature
   2236      * @return {@code -1} indicating failure, always
   2237      */
   2238     public int stopUsingNetworkFeature(String feature, int callingPid, int callingUid) {
   2239         return -1;
   2240     }
   2241 
   2242     @Override
   2243     public void interpretScanResultsAvailable() {
   2244 
   2245         // If we shouldn't place a notification on available networks, then
   2246         // don't bother doing any of the following
   2247         if (!mNotificationEnabled) return;
   2248 
   2249         NetworkInfo networkInfo = getNetworkInfo();
   2250 
   2251         State state = networkInfo.getState();
   2252         if ((state == NetworkInfo.State.DISCONNECTED)
   2253                 || (state == NetworkInfo.State.UNKNOWN)) {
   2254 
   2255             // Look for an open network
   2256             List<ScanResult> scanResults = getScanResultsList();
   2257             if (scanResults != null) {
   2258                 int numOpenNetworks = 0;
   2259                 for (int i = scanResults.size() - 1; i >= 0; i--) {
   2260                     ScanResult scanResult = scanResults.get(i);
   2261 
   2262                     if (TextUtils.isEmpty(scanResult.capabilities)) {
   2263                         numOpenNetworks++;
   2264                     }
   2265                 }
   2266 
   2267                 if (numOpenNetworks > 0) {
   2268                     if (++mNumScansSinceNetworkStateChange >= NUM_SCANS_BEFORE_ACTUALLY_SCANNING) {
   2269                         /*
   2270                          * We've scanned continuously at least
   2271                          * NUM_SCANS_BEFORE_NOTIFICATION times. The user
   2272                          * probably does not have a remembered network in range,
   2273                          * since otherwise supplicant would have tried to
   2274                          * associate and thus resetting this counter.
   2275                          */
   2276                         setNotificationVisible(true, numOpenNetworks, false, 0);
   2277                     }
   2278                     return;
   2279                 }
   2280             }
   2281         }
   2282 
   2283         // No open networks in range, remove the notification
   2284         setNotificationVisible(false, 0, false, 0);
   2285     }
   2286 
   2287     /**
   2288      * Display or don't display a notification that there are open Wi-Fi networks.
   2289      * @param visible {@code true} if notification should be visible, {@code false} otherwise
   2290      * @param numNetworks the number networks seen
   2291      * @param force {@code true} to force notification to be shown/not-shown,
   2292      * even if it is already shown/not-shown.
   2293      * @param delay time in milliseconds after which the notification should be made
   2294      * visible or invisible.
   2295      */
   2296     public void setNotificationVisible(boolean visible, int numNetworks, boolean force, int delay) {
   2297 
   2298         // Since we use auto cancel on the notification, when the
   2299         // mNetworksAvailableNotificationShown is true, the notification may
   2300         // have actually been canceled.  However, when it is false we know
   2301         // for sure that it is not being shown (it will not be shown any other
   2302         // place than here)
   2303 
   2304         // If it should be hidden and it is already hidden, then noop
   2305         if (!visible && !mNotificationShown && !force) {
   2306             return;
   2307         }
   2308 
   2309         Message message;
   2310         if (visible) {
   2311 
   2312             // Not enough time has passed to show the notification again
   2313             if (System.currentTimeMillis() < mNotificationRepeatTime) {
   2314                 return;
   2315             }
   2316 
   2317             if (mNotification == null) {
   2318                 // Cache the Notification mainly so we can remove the
   2319                 // EVENT_NOTIFICATION_CHANGED message with this Notification from
   2320                 // the queue later
   2321                 mNotification = new Notification();
   2322                 mNotification.when = 0;
   2323                 mNotification.icon = ICON_NETWORKS_AVAILABLE;
   2324                 mNotification.flags = Notification.FLAG_AUTO_CANCEL;
   2325                 mNotification.contentIntent = PendingIntent.getActivity(mContext, 0,
   2326                         new Intent(WifiManager.ACTION_PICK_WIFI_NETWORK), 0);
   2327             }
   2328 
   2329             CharSequence title = mContext.getResources().getQuantityText(
   2330                     com.android.internal.R.plurals.wifi_available, numNetworks);
   2331             CharSequence details = mContext.getResources().getQuantityText(
   2332                     com.android.internal.R.plurals.wifi_available_detailed, numNetworks);
   2333             mNotification.tickerText = title;
   2334             mNotification.setLatestEventInfo(mContext, title, details, mNotification.contentIntent);
   2335 
   2336             mNotificationRepeatTime = System.currentTimeMillis() + NOTIFICATION_REPEAT_DELAY_MS;
   2337 
   2338             message = mTarget.obtainMessage(EVENT_NOTIFICATION_CHANGED, 1,
   2339                     ICON_NETWORKS_AVAILABLE, mNotification);
   2340 
   2341         } else {
   2342 
   2343             // Remove any pending messages to show the notification
   2344             mTarget.removeMessages(EVENT_NOTIFICATION_CHANGED, mNotification);
   2345 
   2346             message = mTarget.obtainMessage(EVENT_NOTIFICATION_CHANGED, 0, ICON_NETWORKS_AVAILABLE);
   2347         }
   2348 
   2349         mTarget.sendMessageDelayed(message, delay);
   2350 
   2351         mNotificationShown = visible;
   2352     }
   2353 
   2354     /**
   2355      * Clears variables related to tracking whether a notification has been
   2356      * shown recently.
   2357      * <p>
   2358      * After calling this method, the timer that prevents notifications from
   2359      * being shown too often will be cleared.
   2360      */
   2361     private void resetNotificationTimer() {
   2362         mNotificationRepeatTime = 0;
   2363         mNumScansSinceNetworkStateChange = 0;
   2364     }
   2365 
   2366     @Override
   2367     public String toString() {
   2368         StringBuffer sb = new StringBuffer();
   2369         sb.append("interface ").append(mInterfaceName);
   2370         sb.append(" runState=");
   2371         if (mRunState >= 1 && mRunState <= mRunStateNames.length) {
   2372             sb.append(mRunStateNames[mRunState-1]);
   2373         } else {
   2374             sb.append(mRunState);
   2375         }
   2376         sb.append(LS).append(mWifiInfo).append(LS);
   2377         sb.append(mDhcpInfo).append(LS);
   2378         sb.append("haveIpAddress=").append(mHaveIpAddress).
   2379                 append(", obtainingIpAddress=").append(mObtainingIpAddress).
   2380                 append(", scanModeActive=").append(mIsScanModeActive).append(LS).
   2381                 append("lastSignalLevel=").append(mLastSignalLevel).
   2382                 append(", explicitlyDisabled=").append(mTornDownByConnMgr);
   2383         return sb.toString();
   2384     }
   2385 
   2386     private class DhcpHandler extends Handler {
   2387 
   2388         private Handler mTarget;
   2389 
   2390         /**
   2391          * Whether to skip the DHCP result callback to the target. For example,
   2392          * this could be set if the network we were requesting an IP for has
   2393          * since been disconnected.
   2394          * <p>
   2395          * Note: There is still a chance where the client's intended DHCP
   2396          * request not being canceled. For example, we are request for IP on
   2397          * A, and he queues request for IP on B, and then cancels the request on
   2398          * B while we're still requesting from A.
   2399          */
   2400         private boolean mCancelCallback;
   2401 
   2402         /**
   2403          * Instance of the bluetooth headset helper. This needs to be created
   2404          * early because there is a delay before it actually 'connects', as
   2405          * noted by its javadoc. If we check before it is connected, it will be
   2406          * in an error state and we will not disable coexistence.
   2407          */
   2408         private BluetoothHeadset mBluetoothHeadset;
   2409 
   2410         public DhcpHandler(Looper looper, Handler target) {
   2411             super(looper);
   2412             mTarget = target;
   2413 
   2414             mBluetoothHeadset = new BluetoothHeadset(mContext, null);
   2415         }
   2416 
   2417         public void handleMessage(Message msg) {
   2418             int event;
   2419 
   2420             switch (msg.what) {
   2421                 case EVENT_DHCP_START:
   2422 
   2423                     boolean modifiedBluetoothCoexistenceMode = false;
   2424                     int powerMode = DRIVER_POWER_MODE_AUTO;
   2425 
   2426                     if (shouldDisableCoexistenceMode()) {
   2427                         /*
   2428                          * There are problems setting the Wi-Fi driver's power
   2429                          * mode to active when bluetooth coexistence mode is
   2430                          * enabled or sense.
   2431                          * <p>
   2432                          * We set Wi-Fi to active mode when
   2433                          * obtaining an IP address because we've found
   2434                          * compatibility issues with some routers with low power
   2435                          * mode.
   2436                          * <p>
   2437                          * In order for this active power mode to properly be set,
   2438                          * we disable coexistence mode until we're done with
   2439                          * obtaining an IP address.  One exception is if we
   2440                          * are currently connected to a headset, since disabling
   2441                          * coexistence would interrupt that connection.
   2442                          */
   2443                         modifiedBluetoothCoexistenceMode = true;
   2444 
   2445                         // Disable the coexistence mode
   2446                         setBluetoothCoexistenceMode(
   2447                                 WifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED);
   2448                     }
   2449 
   2450                     powerMode = getPowerMode();
   2451                     if (powerMode < 0) {
   2452                         // Handle the case where supplicant driver does not support
   2453                         // getPowerModeCommand.
   2454                         powerMode = DRIVER_POWER_MODE_AUTO;
   2455                     }
   2456                     if (powerMode != DRIVER_POWER_MODE_ACTIVE) {
   2457                         setPowerMode(DRIVER_POWER_MODE_ACTIVE);
   2458                     }
   2459 
   2460                     synchronized (this) {
   2461                         // A new request is being made, so assume we will callback
   2462                         mCancelCallback = false;
   2463                     }
   2464                     Log.d(TAG, "DhcpHandler: DHCP request started");
   2465                     if (NetworkUtils.runDhcp(mInterfaceName, mDhcpInfo)) {
   2466                         event = EVENT_INTERFACE_CONFIGURATION_SUCCEEDED;
   2467                         if (LOCAL_LOGD) Log.v(TAG, "DhcpHandler: DHCP request succeeded");
   2468                     } else {
   2469                         event = EVENT_INTERFACE_CONFIGURATION_FAILED;
   2470                         Log.i(TAG, "DhcpHandler: DHCP request failed: " +
   2471                             NetworkUtils.getDhcpError());
   2472                     }
   2473 
   2474                     if (powerMode != DRIVER_POWER_MODE_ACTIVE) {
   2475                         setPowerMode(powerMode);
   2476                     }
   2477 
   2478                     if (modifiedBluetoothCoexistenceMode) {
   2479                         // Set the coexistence mode back to its default value
   2480                         setBluetoothCoexistenceMode(
   2481                                 WifiNative.BLUETOOTH_COEXISTENCE_MODE_SENSE);
   2482                     }
   2483 
   2484                     synchronized (this) {
   2485                         if (!mCancelCallback) {
   2486                             mTarget.sendEmptyMessage(event);
   2487                         }
   2488                     }
   2489                     break;
   2490             }
   2491         }
   2492 
   2493         public synchronized void setCancelCallback(boolean cancelCallback) {
   2494             mCancelCallback = cancelCallback;
   2495         }
   2496 
   2497         /**
   2498          * Whether to disable coexistence mode while obtaining IP address. This
   2499          * logic will return true only if the current bluetooth
   2500          * headset/handsfree state is disconnected. This means if it is in an
   2501          * error state, we will NOT disable coexistence mode to err on the side
   2502          * of safety.
   2503          *
   2504          * @return Whether to disable coexistence mode.
   2505          */
   2506         private boolean shouldDisableCoexistenceMode() {
   2507             int state = mBluetoothHeadset.getState(mBluetoothHeadset.getCurrentHeadset());
   2508             return state == BluetoothHeadset.STATE_DISCONNECTED;
   2509         }
   2510     }
   2511 
   2512     private void checkUseStaticIp() {
   2513         mUseStaticIp = false;
   2514         final ContentResolver cr = mContext.getContentResolver();
   2515         try {
   2516             if (Settings.System.getInt(cr, Settings.System.WIFI_USE_STATIC_IP) == 0) {
   2517                 return;
   2518             }
   2519         } catch (Settings.SettingNotFoundException e) {
   2520             return;
   2521         }
   2522 
   2523         try {
   2524             String addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_IP);
   2525             if (addr != null) {
   2526                 mDhcpInfo.ipAddress = stringToIpAddr(addr);
   2527             } else {
   2528                 return;
   2529             }
   2530             addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_GATEWAY);
   2531             if (addr != null) {
   2532                 mDhcpInfo.gateway = stringToIpAddr(addr);
   2533             } else {
   2534                 return;
   2535             }
   2536             addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_NETMASK);
   2537             if (addr != null) {
   2538                 mDhcpInfo.netmask = stringToIpAddr(addr);
   2539             } else {
   2540                 return;
   2541             }
   2542             addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_DNS1);
   2543             if (addr != null) {
   2544                 mDhcpInfo.dns1 = stringToIpAddr(addr);
   2545             } else {
   2546                 return;
   2547             }
   2548             addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_DNS2);
   2549             if (addr != null) {
   2550                 mDhcpInfo.dns2 = stringToIpAddr(addr);
   2551             } else {
   2552                 mDhcpInfo.dns2 = 0;
   2553             }
   2554         } catch (UnknownHostException e) {
   2555             return;
   2556         }
   2557         mUseStaticIp = true;
   2558     }
   2559 
   2560     private static int stringToIpAddr(String addrString) throws UnknownHostException {
   2561         try {
   2562             String[] parts = addrString.split("\\.");
   2563             if (parts.length != 4) {
   2564                 throw new UnknownHostException(addrString);
   2565             }
   2566 
   2567             int a = Integer.parseInt(parts[0])      ;
   2568             int b = Integer.parseInt(parts[1]) <<  8;
   2569             int c = Integer.parseInt(parts[2]) << 16;
   2570             int d = Integer.parseInt(parts[3]) << 24;
   2571 
   2572             return a | b | c | d;
   2573         } catch (NumberFormatException ex) {
   2574             throw new UnknownHostException(addrString);
   2575         }
   2576     }
   2577 
   2578     private int getMaxDhcpRetries() {
   2579         return Settings.Secure.getInt(mContext.getContentResolver(),
   2580                                       Settings.Secure.WIFI_MAX_DHCP_RETRY_COUNT,
   2581                                       DEFAULT_MAX_DHCP_RETRIES);
   2582     }
   2583 
   2584     private class SettingsObserver extends ContentObserver {
   2585         public SettingsObserver(Handler handler) {
   2586             super(handler);
   2587             ContentResolver cr = mContext.getContentResolver();
   2588             cr.registerContentObserver(Settings.System.getUriFor(
   2589                 Settings.System.WIFI_USE_STATIC_IP), false, this);
   2590             cr.registerContentObserver(Settings.System.getUriFor(
   2591                 Settings.System.WIFI_STATIC_IP), false, this);
   2592             cr.registerContentObserver(Settings.System.getUriFor(
   2593                 Settings.System.WIFI_STATIC_GATEWAY), false, this);
   2594             cr.registerContentObserver(Settings.System.getUriFor(
   2595                 Settings.System.WIFI_STATIC_NETMASK), false, this);
   2596             cr.registerContentObserver(Settings.System.getUriFor(
   2597                 Settings.System.WIFI_STATIC_DNS1), false, this);
   2598             cr.registerContentObserver(Settings.System.getUriFor(
   2599                 Settings.System.WIFI_STATIC_DNS2), false, this);
   2600         }
   2601 
   2602         public void onChange(boolean selfChange) {
   2603             super.onChange(selfChange);
   2604 
   2605             boolean wasStaticIp = mUseStaticIp;
   2606             int oIp, oGw, oMsk, oDns1, oDns2;
   2607             oIp = oGw = oMsk = oDns1 = oDns2 = 0;
   2608             if (wasStaticIp) {
   2609                 oIp = mDhcpInfo.ipAddress;
   2610                 oGw = mDhcpInfo.gateway;
   2611                 oMsk = mDhcpInfo.netmask;
   2612                 oDns1 = mDhcpInfo.dns1;
   2613                 oDns2 = mDhcpInfo.dns2;
   2614             }
   2615             checkUseStaticIp();
   2616 
   2617             if (mWifiInfo.getSupplicantState() == SupplicantState.UNINITIALIZED) {
   2618                 return;
   2619             }
   2620 
   2621             boolean changed =
   2622                 (wasStaticIp != mUseStaticIp) ||
   2623                     (wasStaticIp && (
   2624                         oIp   != mDhcpInfo.ipAddress ||
   2625                         oGw   != mDhcpInfo.gateway ||
   2626                         oMsk  != mDhcpInfo.netmask ||
   2627                         oDns1 != mDhcpInfo.dns1 ||
   2628                         oDns2 != mDhcpInfo.dns2));
   2629 
   2630             if (changed) {
   2631                 resetConnections(true);
   2632                 configureInterface();
   2633                 if (mUseStaticIp) {
   2634                     Message msg = mTarget.obtainMessage(EVENT_CONFIGURATION_CHANGED, mNetworkInfo);
   2635                     msg.sendToTarget();
   2636                 }
   2637             }
   2638         }
   2639     }
   2640 
   2641     private class NotificationEnabledSettingObserver extends ContentObserver {
   2642 
   2643         public NotificationEnabledSettingObserver(Handler handler) {
   2644             super(handler);
   2645         }
   2646 
   2647         public void register() {
   2648             ContentResolver cr = mContext.getContentResolver();
   2649             cr.registerContentObserver(Settings.Secure.getUriFor(
   2650                 Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON), true, this);
   2651             mNotificationEnabled = getValue();
   2652         }
   2653 
   2654         @Override
   2655         public void onChange(boolean selfChange) {
   2656             super.onChange(selfChange);
   2657 
   2658             mNotificationEnabled = getValue();
   2659             if (!mNotificationEnabled) {
   2660                 // Remove any notification that may be showing
   2661                 setNotificationVisible(false, 0, true, 0);
   2662             }
   2663 
   2664             resetNotificationTimer();
   2665         }
   2666 
   2667         private boolean getValue() {
   2668             return Settings.Secure.getInt(mContext.getContentResolver(),
   2669                     Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 1) == 1;
   2670         }
   2671     }
   2672 }
   2673