OSDN Git Service

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