• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 {
SupplicantStateChangeResult(int networkId, String BSSID, SupplicantState state)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 {
NetworkStateChangeResult(DetailedState state, String BSSID, int networkId)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 
WifiStateTracker(Context context, Handler target)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         mInterfaceName = SystemProperties.get("wifi.interface", "tiwlan0");
436         sDnsPropNames = new String[] {
437             "dhcp." + mInterfaceName + ".dns1",
438             "dhcp." + mInterfaceName + ".dns2"
439         };
440         mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo"));
441 
442     }
443 
444     /**
445      * Helper method: sets the supplicant state and keeps the network
446      * info updated.
447      * @param state the new state
448      */
setSupplicantState(SupplicantState state)449     private void setSupplicantState(SupplicantState state) {
450         mWifiInfo.setSupplicantState(state);
451         updateNetworkInfo();
452         checkPollTimer();
453     }
454 
getSupplicantState()455     public SupplicantState getSupplicantState() {
456         return mWifiInfo.getSupplicantState();
457     }
458 
459     /**
460      * Helper method: sets the supplicant state and keeps the network
461      * info updated (string version).
462      * @param stateName the string name of the new state
463      */
setSupplicantState(String stateName)464     private void setSupplicantState(String stateName) {
465         mWifiInfo.setSupplicantState(stateName);
466         updateNetworkInfo();
467         checkPollTimer();
468     }
469 
470     /**
471      * Helper method: sets the boolean indicating that the connection
472      * manager asked the network to be torn down (and so only the connection
473      * manager can set it up again).
474      * network info updated.
475      * @param flag {@code true} if explicitly disabled.
476      */
setTornDownByConnMgr(boolean flag)477     private void setTornDownByConnMgr(boolean flag) {
478         mTornDownByConnMgr = flag;
479         updateNetworkInfo();
480     }
481 
482     /**
483      * Return the IP addresses of the DNS servers available for the WLAN
484      * network interface.
485      * @return a list of DNS addresses, with no holes.
486      */
getNameServers()487     public String[] getNameServers() {
488         return getNameServerList(sDnsPropNames);
489     }
490 
491     /**
492      * Return the name of our WLAN network interface.
493      * @return the name of our interface.
494      */
getInterfaceName()495     public String getInterfaceName() {
496         return mInterfaceName;
497     }
498 
499     /**
500      * Return the system properties name associated with the tcp buffer sizes
501      * for this network.
502      */
getTcpBufferSizesPropName()503     public String getTcpBufferSizesPropName() {
504         return "net.tcp.buffersize.wifi";
505     }
506 
startMonitoring()507     public void startMonitoring() {
508         /*
509          * Get a handle on the WifiManager. This cannot be done in our
510          * constructor, because the Wifi service is not yet registered.
511          */
512         mWM = (WifiManager)mContext.getSystemService(Context.WIFI_SERVICE);
513     }
514 
startEventLoop()515     public void startEventLoop() {
516         mWifiMonitor.startMonitoring();
517     }
518 
519     /**
520      * Wi-Fi is considered available as long as we have a connection to the
521      * supplicant daemon and there is at least one enabled network. If a teardown
522      * was explicitly requested, then Wi-Fi can be restarted with a reconnect
523      * request, so it is considered available. If the driver has been stopped
524      * for any reason other than a teardown request, Wi-Fi is considered
525      * unavailable.
526      * @return {@code true} if Wi-Fi connections are possible
527      */
isAvailable()528     public synchronized boolean isAvailable() {
529         /*
530          * TODO: Need to also look at scan results to see whether we're
531          * in range of any access points. If we have scan results that
532          * are no more than N seconds old, use those, otherwise, initiate
533          * a scan and wait for the results. This only matters if we
534          * allow mobile to be the preferred network.
535          */
536         SupplicantState suppState = mWifiInfo.getSupplicantState();
537         return suppState != SupplicantState.UNINITIALIZED &&
538                 suppState != SupplicantState.INACTIVE &&
539                 (mTornDownByConnMgr || !isDriverStopped());
540     }
541 
542     /**
543      * {@inheritDoc}
544      * There are currently no defined Wi-Fi subtypes.
545      */
getNetworkSubtype()546     public int getNetworkSubtype() {
547         return 0;
548     }
549 
550     /**
551      * Helper method: updates the network info object to keep it in sync with
552      * the Wi-Fi state tracker.
553      */
updateNetworkInfo()554     private void updateNetworkInfo() {
555         mNetworkInfo.setIsAvailable(isAvailable());
556     }
557 
558     /**
559      * Report whether the Wi-Fi connection is fully configured for data.
560      * @return {@code true} if the {@link SupplicantState} is
561      * {@link android.net.wifi.SupplicantState#COMPLETED COMPLETED}.
562      */
isConnectionCompleted()563     public boolean isConnectionCompleted() {
564         return mWifiInfo.getSupplicantState() == SupplicantState.COMPLETED;
565     }
566 
567     /**
568      * Report whether the Wi-Fi connection has successfully acquired an IP address.
569      * @return {@code true} if the Wi-Fi connection has been assigned an IP address.
570      */
hasIpAddress()571     public boolean hasIpAddress() {
572         return mHaveIpAddress;
573     }
574 
575     /**
576      * Send the tracker a notification that a user-entered password key
577      * may be incorrect (i.e., caused authentication to fail).
578      */
notifyPasswordKeyMayBeIncorrect()579     void notifyPasswordKeyMayBeIncorrect() {
580         sendEmptyMessage(EVENT_PASSWORD_KEY_MAY_BE_INCORRECT);
581     }
582 
583     /**
584      * Send the tracker a notification that a connection to the supplicant
585      * daemon has been established.
586      */
notifySupplicantConnection()587     void notifySupplicantConnection() {
588         sendEmptyMessage(EVENT_SUPPLICANT_CONNECTION);
589     }
590 
591     /**
592      * Send the tracker a notification that the state of the supplicant
593      * has changed.
594      * @param networkId the configured network on which the state change occurred
595      * @param newState the new {@code SupplicantState}
596      */
notifyStateChange(int networkId, String BSSID, SupplicantState newState)597     void notifyStateChange(int networkId, String BSSID, SupplicantState newState) {
598         Message msg = Message.obtain(
599             this, EVENT_SUPPLICANT_STATE_CHANGED,
600             new SupplicantStateChangeResult(networkId, BSSID, newState));
601         msg.sendToTarget();
602     }
603 
604     /**
605      * Send the tracker a notification that the state of Wifi connectivity
606      * has changed.
607      * @param networkId the configured network on which the state change occurred
608      * @param newState the new network state
609      * @param BSSID when the new state is {@link DetailedState#CONNECTED
610      * NetworkInfo.DetailedState.CONNECTED},
611      * this is the MAC address of the access point. Otherwise, it
612      * is {@code null}.
613      */
notifyStateChange(DetailedState newState, String BSSID, int networkId)614     void notifyStateChange(DetailedState newState, String BSSID, int networkId) {
615         Message msg = Message.obtain(
616             this, EVENT_NETWORK_STATE_CHANGED,
617             new NetworkStateChangeResult(newState, BSSID, networkId));
618         msg.sendToTarget();
619     }
620 
621     /**
622      * Send the tracker a notification that a scan has completed, and results
623      * are available.
624      */
notifyScanResultsAvailable()625     void notifyScanResultsAvailable() {
626         // reset the supplicant's handling of scan results to "normal" mode
627         setScanResultHandling(SUPPL_SCAN_HANDLING_NORMAL);
628         sendEmptyMessage(EVENT_SCAN_RESULTS_AVAILABLE);
629     }
630 
631     /**
632      * Send the tracker a notification that we can no longer communicate with
633      * the supplicant daemon.
634      */
notifySupplicantLost()635     void notifySupplicantLost() {
636         sendEmptyMessage(EVENT_SUPPLICANT_DISCONNECT);
637     }
638 
639     /**
640      * Send the tracker a notification that the Wi-Fi driver has been stopped.
641      */
notifyDriverStopped()642     void notifyDriverStopped() {
643         // Send a driver stopped message to our handler
644         Message.obtain(this, EVENT_DRIVER_STATE_CHANGED, DRIVER_STOPPED, 0).sendToTarget();
645     }
646 
647     /**
648      * Send the tracker a notification that the Wi-Fi driver has been restarted after
649      * having been stopped.
650      */
notifyDriverStarted()651     void notifyDriverStarted() {
652         // Send a driver started message to our handler
653         Message.obtain(this, EVENT_DRIVER_STATE_CHANGED, DRIVER_STARTED, 0).sendToTarget();
654     }
655 
656     /**
657      * Send the tracker a notification that the Wi-Fi driver has hung and needs restarting.
658      */
notifyDriverHung()659     void notifyDriverHung() {
660         // Send a driver hanged message to our handler
661         Message.obtain(this, EVENT_DRIVER_STATE_CHANGED, DRIVER_HUNG, 0).sendToTarget();
662     }
663 
664     /**
665      * Set the interval timer for polling connection information
666      * that is not delivered asynchronously.
667      */
checkPollTimer()668     private synchronized void checkPollTimer() {
669         if (mEnableRssiPolling &&
670                 mWifiInfo.getSupplicantState() == SupplicantState.COMPLETED &&
671                 !hasMessages(EVENT_POLL_INTERVAL)) {
672             sendEmptyMessageDelayed(EVENT_POLL_INTERVAL, POLL_STATUS_INTERVAL_MSECS);
673         }
674     }
675 
676     /**
677      * TODO: mRunState is not synchronized in some places
678      * address this as part of re-architect.
679      *
680      * TODO: We are exposing an additional public synchronized call
681      * for a wakelock optimization in WifiService. Remove it
682      * when we handle the wakelock in ConnectivityService.
683      */
isDriverStopped()684     public synchronized boolean isDriverStopped() {
685         return mRunState == RUN_STATE_STOPPED || mRunState == RUN_STATE_STOPPING;
686     }
687 
updateBatteryWorkSourceLocked(WorkSource newSource)688     public void updateBatteryWorkSourceLocked(WorkSource newSource) {
689         try {
690             if (newSource != null) {
691                 mRunningWifiUids.set(newSource);
692             }
693             if (mRunState == RUN_STATE_RUNNING) {
694                 if (mReportedRunning) {
695                     // If the work source has changed since last time, need
696                     // to remove old work from battery stats.
697                     if (mLastRunningWifiUids.diff(mRunningWifiUids)) {
698                         mBatteryStats.noteWifiRunningChanged(mLastRunningWifiUids,
699                                 mRunningWifiUids);
700                         mLastRunningWifiUids.set(mRunningWifiUids);
701                     }
702                 } else {
703                     // Now being started, report it.
704                     mBatteryStats.noteWifiRunning(mRunningWifiUids);
705                     mLastRunningWifiUids.set(mRunningWifiUids);
706                     mReportedRunning = true;
707                 }
708             } else if (mRunState == RUN_STATE_STOPPED) {
709                 if (mReportedRunning) {
710                     // Last reported we were running, time to stop.
711                     mBatteryStats.noteWifiStopped(mLastRunningWifiUids);
712                     mLastRunningWifiUids.clear();
713                     mReportedRunning = false;
714                 }
715             } else {
716                 // State in transition -- nothing to update yet.
717             }
718         } catch (RemoteException ignore) {
719         }
720     }
721 
722     /**
723      * Set the run state to either "normal" or "scan-only".
724      * @param scanOnlyMode true if the new mode should be scan-only.
725      */
setScanOnlyMode(boolean scanOnlyMode)726     public synchronized void setScanOnlyMode(boolean scanOnlyMode) {
727         // do nothing unless scan-only mode is changing
728         if (mIsScanOnly != scanOnlyMode) {
729             int scanType = (scanOnlyMode ?
730                     SUPPL_SCAN_HANDLING_LIST_ONLY : SUPPL_SCAN_HANDLING_NORMAL);
731             if (LOCAL_LOGD) Log.v(TAG, "Scan-only mode changing to " + scanOnlyMode + " scanType=" + scanType);
732             if (setScanResultHandling(scanType)) {
733                 mIsScanOnly = scanOnlyMode;
734                 if (!isDriverStopped()) {
735                     if (scanOnlyMode) {
736                         disconnect();
737                     } else {
738                         reconnectCommand();
739                     }
740                 }
741             }
742         }
743     }
744 
745     /**
746      * Set suspend mode optimizations. These include:
747      * - packet filtering
748      * - turn off roaming
749      * - DTIM settings
750      *
751      * Uses reference counting to keep the suspend optimizations disabled
752      * as long as one entity wants optimizations disabled.
753      *
754      * For example, WifiLock can keep suspend optimizations disabled
755      * or the user setting (wifi never sleeps) can keep suspend optimizations
756      * disabled. As long as one entity wants it disabled, it should stay
757      * that way
758      *
759      * @param enabled true if optimizations need enabled, false otherwise
760      */
setSuspendModeOptimizations(boolean enabled)761     public synchronized void setSuspendModeOptimizations(boolean enabled) {
762 
763         /* It is good to plumb suspend optimization enable
764          * or disable even if ref count indicates already done
765          * since we could have a case of previous failure.
766          */
767         if (!enabled) {
768             mOptimizationsDisabledRefCount++;
769         } else {
770             mOptimizationsDisabledRefCount--;
771             if (mOptimizationsDisabledRefCount > 0) {
772                 return;
773             } else {
774                 /* Keep refcount from becoming negative */
775                 mOptimizationsDisabledRefCount = 0;
776             }
777         }
778 
779         if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
780             return;
781         }
782 
783         WifiNative.setSuspendOptimizationsCommand(enabled);
784     }
785 
786 
787     /**
788      * Set high performance mode of operation. This would mean
789      * use active power mode and disable suspend optimizations
790      * @param enabled true if enabled, false otherwise
791      */
setHighPerfMode(boolean enabled)792     public synchronized void setHighPerfMode(boolean enabled) {
793         if (mIsHighPerfEnabled != enabled) {
794             if (enabled) {
795                 setPowerMode(DRIVER_POWER_MODE_ACTIVE);
796                 setSuspendModeOptimizations(false);
797             } else {
798                 setPowerMode(DRIVER_POWER_MODE_AUTO);
799                 setSuspendModeOptimizations(true);
800             }
801             mIsHighPerfEnabled = enabled;
802             Log.d(TAG,"high performance mode: " + enabled);
803         }
804     }
805 
806 
checkIsBluetoothPlaying()807     private void checkIsBluetoothPlaying() {
808         boolean isBluetoothPlaying = false;
809         Set<BluetoothDevice> connected = mBluetoothA2dp.getConnectedSinks();
810 
811         for (BluetoothDevice device : connected) {
812             if (mBluetoothA2dp.getSinkState(device) == BluetoothA2dp.STATE_PLAYING) {
813                 isBluetoothPlaying = true;
814                 break;
815             }
816         }
817         setBluetoothScanMode(isBluetoothPlaying);
818     }
819 
enableRssiPolling(boolean enable)820     public void enableRssiPolling(boolean enable) {
821         if (mEnableRssiPolling != enable) {
822             mEnableRssiPolling = enable;
823             checkPollTimer();
824         }
825     }
826 
827     /**
828      * We release the wakelock in WifiService
829      * using a timer.
830      *
831      * TODO:
832      * Releasing wakelock using both timer and
833      * a call from ConnectivityService requires
834      * a rethink. We had problems where WifiService
835      * could keep a wakelock forever if we delete
836      * messages in the asynchronous call
837      * from ConnectivityService
838      */
839     @Override
releaseWakeLock()840     public void releaseWakeLock() {
841     }
842 
843     /**
844      * Tracks the WPA supplicant states to detect "loop" situations.
845      * @param newSupplicantState The new WPA supplicant state.
846      * @return {@code true} if the supplicant loop should be stopped
847      * and {@code false} if it should continue.
848      */
isSupplicantLooping(SupplicantState newSupplicantState)849     private boolean isSupplicantLooping(SupplicantState newSupplicantState) {
850         if (SupplicantState.ASSOCIATING.ordinal() <= newSupplicantState.ordinal()
851             && newSupplicantState.ordinal() < SupplicantState.COMPLETED.ordinal()) {
852             if (mSupplicantLoopState != newSupplicantState) {
853                 if (newSupplicantState.ordinal() < mSupplicantLoopState.ordinal()) {
854                     ++mNumSupplicantLoopIterations;
855                 }
856 
857                 mSupplicantLoopState = newSupplicantState;
858             }
859         } else if (newSupplicantState == SupplicantState.COMPLETED) {
860             resetSupplicantLoopState();
861         }
862 
863         return mNumSupplicantLoopIterations >= MAX_SUPPLICANT_LOOP_ITERATIONS;
864     }
865 
866     /**
867      * Resets the WPA supplicant loop state.
868      */
resetSupplicantLoopState()869     private void resetSupplicantLoopState() {
870         mNumSupplicantLoopIterations = 0;
871     }
872 
873     @Override
handleMessage(Message msg)874     public void handleMessage(Message msg) {
875         Intent intent;
876 
877         switch (msg.what) {
878             case EVENT_SUPPLICANT_CONNECTION:
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, true);
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 
wifiManagerDisableNetwork(int networkId)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 
configureInterface()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      */
handleDisconnectedState(DetailedState newState, boolean disableInterface)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      */
resetConnections(boolean disableInterface)1434     public void resetConnections(boolean disableInterface) {
1435         if (LOCAL_LOGD) Log.d(TAG, "Reset connections and stopping DHCP");
1436         mHaveIpAddress = false;
1437         mObtainingIpAddress = false;
1438         mWifiInfo.setIpAddress(0);
1439 
1440         /*
1441          * Reset connection depends on both the interface and the IP assigned,
1442          * so it should be done before any chance of the IP being lost.
1443          */
1444         NetworkUtils.resetConnections(mInterfaceName);
1445 
1446         // Stop DHCP
1447         mDhcpTarget.setCancelCallback(true);
1448         mDhcpTarget.removeMessages(EVENT_DHCP_START);
1449 
1450         if (!NetworkUtils.stopDhcp(mInterfaceName)) {
1451             Log.e(TAG, "Could not stop DHCP");
1452         }
1453 
1454         /**
1455          * Interface is re-enabled in the supplicant
1456          * when moving out of ASSOCIATING state
1457          */
1458         if(disableInterface) {
1459             if (LOCAL_LOGD) Log.d(TAG, "Disabling interface");
1460             NetworkUtils.disableInterface(mInterfaceName);
1461         }
1462     }
1463 
1464     /**
1465      * The supplicant is reporting that we are disconnected from the current
1466      * access point. Often, however, a disconnect will be followed very shortly
1467      * by a reconnect to the same access point. Therefore, we delay resetting
1468      * the connection's IP state for a bit.
1469      */
scheduleDisconnect()1470     private void scheduleDisconnect() {
1471         mDisconnectPending = true;
1472         if (!hasMessages(EVENT_DEFERRED_DISCONNECT)) {
1473             sendEmptyMessageDelayed(EVENT_DEFERRED_DISCONNECT, DISCONNECT_DELAY_MSECS);
1474         }
1475     }
1476 
cancelDisconnect()1477     private void cancelDisconnect() {
1478         mDisconnectPending = false;
1479         removeMessages(EVENT_DEFERRED_DISCONNECT);
1480     }
1481 
getDhcpInfo()1482     public DhcpInfo getDhcpInfo() {
1483         return mDhcpInfo;
1484     }
1485 
getScanResultsList()1486     public synchronized List<ScanResult> getScanResultsList() {
1487         return mScanResults;
1488     }
1489 
setScanResultsList(List<ScanResult> scanList)1490     public synchronized void setScanResultsList(List<ScanResult> scanList) {
1491         mScanResults = scanList;
1492     }
1493 
1494     /**
1495      * Get status information for the current connection, if any.
1496      * @return a {@link WifiInfo} object containing information about the current connection
1497      */
requestConnectionInfo()1498     public WifiInfo requestConnectionInfo() {
1499         requestConnectionStatus(mWifiInfo);
1500         requestPolledInfo(mWifiInfo, false);
1501         return mWifiInfo;
1502     }
1503 
requestConnectionStatus(WifiInfo info)1504     private void requestConnectionStatus(WifiInfo info) {
1505         String SSID = null;
1506         String BSSID = null;
1507         String suppState = null;
1508         int netId = -1;
1509         String reply = status();
1510         if (reply != null) {
1511             /*
1512              * Parse the reply from the supplicant to the status command, and update
1513              * local state accordingly. The reply is a series of lines of the form
1514              * "name=value".
1515              */
1516 
1517             String[] lines = reply.split("\n");
1518             for (String line : lines) {
1519                 String[] prop = line.split(" *= *");
1520                 if (prop.length < 2)
1521                     continue;
1522                 String name = prop[0];
1523                 String value = prop[1];
1524                 if (name.equalsIgnoreCase("id"))
1525                     netId = Integer.parseInt(value);
1526                 else if (name.equalsIgnoreCase("ssid"))
1527                     SSID = value;
1528                 else if (name.equalsIgnoreCase("bssid"))
1529                     BSSID = value;
1530                 else if (name.equalsIgnoreCase("wpa_state"))
1531                     suppState = value;
1532             }
1533         }
1534         info.setNetworkId(netId);
1535         info.setSSID(SSID);
1536         info.setBSSID(BSSID);
1537         /*
1538          * We only set the supplicant state if the previous state was
1539          * UNINITIALIZED. This should only happen when we first connect to
1540          * the supplicant. Once we're connected, we should always receive
1541          * an event upon any state change, but in this case, we want to
1542          * make sure any listeners are made aware of the state change.
1543          */
1544         if (mWifiInfo.getSupplicantState() == SupplicantState.UNINITIALIZED && suppState != null)
1545             setSupplicantState(suppState);
1546     }
1547 
1548     /**
1549      * Get the dynamic information that is not reported via events.
1550      * @param info the object into which the information should be captured.
1551      */
requestPolledInfo(WifiInfo info, boolean polling)1552     private synchronized void requestPolledInfo(WifiInfo info, boolean polling)
1553     {
1554         int newRssi = (polling ? getRssiApprox() : getRssi());
1555         if (newRssi != -1 && -200 < newRssi && newRssi < 256) { // screen out invalid values
1556             /* some implementations avoid negative values by adding 256
1557              * so we need to adjust for that here.
1558              */
1559             if (newRssi > 0) newRssi -= 256;
1560             info.setRssi(newRssi);
1561             /*
1562              * Rather then sending the raw RSSI out every time it
1563              * changes, we precalculate the signal level that would
1564              * be displayed in the status bar, and only send the
1565              * broadcast if that much more coarse-grained number
1566              * changes. This cuts down greatly on the number of
1567              * broadcasts, at the cost of not informing others
1568              * interested in RSSI of all the changes in signal
1569              * level.
1570              */
1571             // TODO: The second arg to the call below needs to be a symbol somewhere, but
1572             // it's actually the size of an array of icons that's private
1573             // to StatusBar Policy.
1574             int newSignalLevel = WifiManager.calculateSignalLevel(newRssi, 4);
1575             if (newSignalLevel != mLastSignalLevel) {
1576                 sendRssiChangeBroadcast(newRssi);
1577             }
1578             mLastSignalLevel = newSignalLevel;
1579         } else {
1580             info.setRssi(-200);
1581         }
1582         int newLinkSpeed = getLinkSpeed();
1583         if (newLinkSpeed != -1) {
1584             info.setLinkSpeed(newLinkSpeed);
1585         }
1586     }
1587 
sendRssiChangeBroadcast(final int newRssi)1588     private void sendRssiChangeBroadcast(final int newRssi) {
1589         if (ActivityManagerNative.isSystemReady()) {
1590             Intent intent = new Intent(WifiManager.RSSI_CHANGED_ACTION);
1591             intent.putExtra(WifiManager.EXTRA_NEW_RSSI, newRssi);
1592             mContext.sendBroadcast(intent);
1593         }
1594     }
1595 
sendNetworkStateChangeBroadcast(String bssid)1596     private void sendNetworkStateChangeBroadcast(String bssid) {
1597         Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
1598         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
1599                 | Intent.FLAG_RECEIVER_REPLACE_PENDING);
1600         intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, mNetworkInfo);
1601         if (bssid != null)
1602             intent.putExtra(WifiManager.EXTRA_BSSID, bssid);
1603         mContext.sendStickyBroadcast(intent);
1604     }
1605 
1606     /**
1607      * Disable Wi-Fi connectivity by stopping the driver.
1608      */
teardown()1609     public boolean teardown() {
1610         if (!mTornDownByConnMgr) {
1611             if (disconnectAndStop()) {
1612                 setTornDownByConnMgr(true);
1613                 return true;
1614             } else {
1615                 return false;
1616             }
1617         } else {
1618             return true;
1619         }
1620     }
1621 
1622     /**
1623      * Reenable Wi-Fi connectivity by restarting the driver.
1624      */
reconnect()1625     public boolean reconnect() {
1626         if (mTornDownByConnMgr) {
1627             if (restart()) {
1628                 setTornDownByConnMgr(false);
1629                 return true;
1630             } else {
1631                 return false;
1632             }
1633         } else {
1634             return true;
1635         }
1636     }
1637 
1638     /**
1639      * We want to stop the driver, but if we're connected to a network,
1640      * we first want to disconnect, so that the supplicant is always in
1641      * a known state (DISCONNECTED) when the driver is stopped.
1642      * @return {@code true} if the operation succeeds, which means that the
1643      * disconnect or stop command was initiated.
1644      */
disconnectAndStop()1645     public synchronized boolean disconnectAndStop() {
1646         boolean ret = true;;
1647         if (mRunState != RUN_STATE_STOPPING && mRunState != RUN_STATE_STOPPED) {
1648             // Take down any open network notifications
1649             setNotificationVisible(false, 0, false, 0);
1650 
1651             if (mWifiInfo.getSupplicantState() == SupplicantState.DORMANT) {
1652                 ret = stopDriver();
1653             } else {
1654                 ret = disconnect();
1655             }
1656             mRunState = RUN_STATE_STOPPING;
1657         }
1658         return ret;
1659     }
1660 
restart()1661     public synchronized boolean restart() {
1662         if (isDriverStopped()) {
1663             mRunState = RUN_STATE_STARTING;
1664             resetConnections(true);
1665             return startDriver();
1666         }
1667         return true;
1668     }
1669 
getWifiState()1670     public int getWifiState() {
1671         return mWifiState.get();
1672     }
1673 
setWifiState(int wifiState)1674     public void setWifiState(int wifiState) {
1675         mWifiState.set(wifiState);
1676     }
1677 
isAnyNetworkDisabled()1678     public boolean isAnyNetworkDisabled() {
1679         return mIsAnyNetworkDisabled.get();
1680     }
1681 
1682    /**
1683      * The WifiNative interface functions are listed below.
1684      * The only native call that is not synchronized on
1685      * WifiStateTracker is waitForEvent() which waits on a
1686      * seperate monitor channel.
1687      *
1688      * All supplicant commands need the wifi to be in an
1689      * enabled state. This can be done by checking the
1690      * mWifiState to be WIFI_STATE_ENABLED.
1691      *
1692      * All commands that can cause commands to driver
1693      * initiated need the driver state to be started.
1694      * This is done by checking isDriverStopped() to
1695      * be false.
1696      */
1697 
1698     /**
1699      * Load the driver and firmware
1700      *
1701      * @return {@code true} if the operation succeeds, {@code false} otherwise
1702      */
loadDriver()1703     public synchronized boolean loadDriver() {
1704         return WifiNative.loadDriver();
1705     }
1706 
1707     /**
1708      * Unload the driver and firmware
1709      *
1710      * @return {@code true} if the operation succeeds, {@code false} otherwise
1711      */
unloadDriver()1712     public synchronized boolean unloadDriver() {
1713         return WifiNative.unloadDriver();
1714     }
1715 
1716     /**
1717      * Check the supplicant config and
1718      * start the supplicant daemon
1719      *
1720      * @return {@code true} if the operation succeeds, {@code false} otherwise
1721      */
startSupplicant()1722     public synchronized boolean startSupplicant() {
1723         return WifiNative.startSupplicant();
1724     }
1725 
1726     /**
1727      * Stop the supplicant daemon
1728      *
1729      * @return {@code true} if the operation succeeds, {@code false} otherwise
1730      */
stopSupplicant()1731     public synchronized boolean stopSupplicant() {
1732         return WifiNative.stopSupplicant();
1733     }
1734 
1735     /**
1736      * Establishes two channels - control channel for commands
1737      * and monitor channel for notifying WifiMonitor
1738      *
1739      * @return {@code true} if the operation succeeds, {@code false} otherwise
1740      */
connectToSupplicant()1741     public synchronized boolean connectToSupplicant() {
1742         return WifiNative.connectToSupplicant();
1743     }
1744 
1745     /**
1746      * Close the control/monitor channels to supplicant
1747      */
closeSupplicantConnection()1748     public synchronized void closeSupplicantConnection() {
1749         WifiNative.closeSupplicantConnection();
1750     }
1751 
1752     /**
1753      * Check if the supplicant is alive
1754      *
1755      * @return {@code true} if the operation succeeds, {@code false} otherwise
1756      */
ping()1757     public synchronized boolean ping() {
1758         if (mWifiState.get() != WIFI_STATE_ENABLED) {
1759             return false;
1760         }
1761         return WifiNative.pingCommand();
1762     }
1763 
1764     /**
1765      * initiate an active or passive scan
1766      *
1767      * @param forceActive true if it is a active scan
1768      * @return {@code true} if the operation succeeds, {@code false} otherwise
1769      */
scan(boolean forceActive)1770     public synchronized boolean scan(boolean forceActive) {
1771         if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
1772             return false;
1773         }
1774         return WifiNative.scanCommand(forceActive);
1775     }
1776 
1777     /**
1778      * Specifies whether the supplicant or driver
1779      * take care of initiating scan and doing AP selection
1780      *
1781      * @param mode
1782      *    SUPPL_SCAN_HANDLING_NORMAL
1783      *    SUPPL_SCAN_HANDLING_LIST_ONLY
1784      * @return {@code true} if the operation succeeds, {@code false} otherwise
1785      */
setScanResultHandling(int mode)1786     public synchronized boolean setScanResultHandling(int mode) {
1787         if (mWifiState.get() != WIFI_STATE_ENABLED) {
1788             return false;
1789         }
1790         return WifiNative.setScanResultHandlingCommand(mode);
1791     }
1792 
1793     /**
1794      * Fetch the scan results from the supplicant
1795      *
1796      * @return example result string
1797      * 00:bb:cc:dd:cc:ee       2427    166     [WPA-EAP-TKIP][WPA2-EAP-CCMP]   Net1
1798      * 00:bb:cc:dd:cc:ff       2412    165     [WPA-EAP-TKIP][WPA2-EAP-CCMP]   Net2
1799      */
scanResults()1800     public synchronized String scanResults() {
1801         if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
1802             return null;
1803         }
1804         return WifiNative.scanResultsCommand();
1805     }
1806 
1807     /**
1808      * Set the scan mode - active or passive
1809      *
1810      * @return {@code true} if the operation succeeds, {@code false} otherwise
1811      */
setScanMode(boolean isScanModeActive)1812     public synchronized boolean setScanMode(boolean isScanModeActive) {
1813         if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
1814             return false;
1815         }
1816         if (mIsScanModeActive != isScanModeActive) {
1817             return WifiNative.setScanModeCommand(mIsScanModeActive = isScanModeActive);
1818         }
1819         return true;
1820     }
1821 
1822     /**
1823      * Disconnect from Access Point
1824      *
1825      * @return {@code true} if the operation succeeds, {@code false} otherwise
1826      */
disconnect()1827     public synchronized boolean disconnect() {
1828         if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
1829             return false;
1830         }
1831         return WifiNative.disconnectCommand();
1832     }
1833 
1834     /**
1835      * Initiate a reconnection to AP
1836      *
1837      * @return {@code true} if the operation succeeds, {@code false} otherwise
1838      */
reconnectCommand()1839     public synchronized boolean reconnectCommand() {
1840         if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
1841             return false;
1842         }
1843         return WifiNative.reconnectCommand();
1844     }
1845 
1846     /**
1847      * Add a network
1848      *
1849      * @return network id of the new network
1850      */
addNetwork()1851     public synchronized int addNetwork() {
1852         if (mWifiState.get() != WIFI_STATE_ENABLED) {
1853             return -1;
1854         }
1855         return WifiNative.addNetworkCommand();
1856     }
1857 
1858     /**
1859      * Delete a network
1860      *
1861      * @param networkId id of the network to be removed
1862      * @return {@code true} if the operation succeeds, {@code false} otherwise
1863      */
removeNetwork(int networkId)1864     public synchronized boolean removeNetwork(int networkId) {
1865         if (mWifiState.get() != WIFI_STATE_ENABLED) {
1866             return false;
1867         }
1868         return mDisconnectExpected = WifiNative.removeNetworkCommand(networkId);
1869     }
1870 
1871     /**
1872      * Enable a network
1873      *
1874      * @param netId network id of the network
1875      * @param disableOthers true, if all other networks have to be disabled
1876      * @return {@code true} if the operation succeeds, {@code false} otherwise
1877      */
enableNetwork(int netId, boolean disableOthers)1878     public synchronized boolean enableNetwork(int netId, boolean disableOthers) {
1879         if (mWifiState.get() != WIFI_STATE_ENABLED) {
1880             return false;
1881         }
1882         if (disableOthers) mIsAnyNetworkDisabled.set(true);
1883         return WifiNative.enableNetworkCommand(netId, disableOthers);
1884     }
1885 
1886     /**
1887      * Enable all networks
1888      *
1889      * @param networks list of configured networks
1890      */
enableAllNetworks(List<WifiConfiguration> networks)1891     public synchronized void enableAllNetworks(List<WifiConfiguration> networks) {
1892         if (mWifiState.get() != WIFI_STATE_ENABLED) {
1893             return;
1894         }
1895         mIsAnyNetworkDisabled.set(false);
1896         for (WifiConfiguration config : networks) {
1897             if (config.status == WifiConfiguration.Status.DISABLED) {
1898                 WifiNative.enableNetworkCommand(config.networkId, false);
1899             }
1900         }
1901     }
1902 
1903     /**
1904      * Disable a network
1905      *
1906      * @param netId network id of the network
1907      * @return {@code true} if the operation succeeds, {@code false} otherwise
1908      */
disableNetwork(int netId)1909     public synchronized boolean disableNetwork(int netId) {
1910         if (mWifiState.get() != WIFI_STATE_ENABLED) {
1911             return false;
1912         }
1913         mIsAnyNetworkDisabled.set(true);
1914         return WifiNative.disableNetworkCommand(netId);
1915     }
1916 
1917     /**
1918      * Initiate a re-association in supplicant
1919      *
1920      * @return {@code true} if the operation succeeds, {@code false} otherwise
1921      */
reassociate()1922     public synchronized boolean reassociate() {
1923         if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
1924             return false;
1925         }
1926         return WifiNative.reassociateCommand();
1927     }
1928 
1929     /**
1930      * Blacklist a BSSID. This will avoid the AP if there are
1931      * alternate APs to connect
1932      *
1933      * @param bssid BSSID of the network
1934      * @return {@code true} if the operation succeeds, {@code false} otherwise
1935      */
addToBlacklist(String bssid)1936     public synchronized boolean addToBlacklist(String bssid) {
1937         if (mWifiState.get() != WIFI_STATE_ENABLED) {
1938             return false;
1939         }
1940         return WifiNative.addToBlacklistCommand(bssid);
1941     }
1942 
1943     /**
1944      * Clear the blacklist list
1945      *
1946      * @return {@code true} if the operation succeeds, {@code false} otherwise
1947      */
clearBlacklist()1948     public synchronized boolean clearBlacklist() {
1949         if (mWifiState.get() != WIFI_STATE_ENABLED) {
1950             return false;
1951         }
1952         return WifiNative.clearBlacklistCommand();
1953     }
1954 
1955     /**
1956      * List all configured networks
1957      *
1958      * @return list of networks or null on failure
1959      */
listNetworks()1960     public synchronized String listNetworks() {
1961         if (mWifiState.get() != WIFI_STATE_ENABLED) {
1962             return null;
1963         }
1964         return WifiNative.listNetworksCommand();
1965     }
1966 
1967     /**
1968      * Get network setting by name
1969      *
1970      * @param netId network id of the network
1971      * @param name network variable key
1972      * @return value corresponding to key
1973      */
getNetworkVariable(int netId, String name)1974     public synchronized String getNetworkVariable(int netId, String name) {
1975         if (mWifiState.get() != WIFI_STATE_ENABLED) {
1976             return null;
1977         }
1978         return WifiNative.getNetworkVariableCommand(netId, name);
1979     }
1980 
1981     /**
1982      * Set network setting by name
1983      *
1984      * @param netId network id of the network
1985      * @param name network variable key
1986      * @param value network variable value
1987      * @return {@code true} if the operation succeeds, {@code false} otherwise
1988      */
setNetworkVariable(int netId, String name, String value)1989     public synchronized boolean setNetworkVariable(int netId, String name, String value) {
1990         if (mWifiState.get() != WIFI_STATE_ENABLED) {
1991             return false;
1992         }
1993         return WifiNative.setNetworkVariableCommand(netId, name, value);
1994     }
1995 
1996     /**
1997      * Get detailed status of the connection
1998      *
1999      * @return Example status result
2000      *  bssid=aa:bb:cc:dd:ee:ff
2001      *  ssid=TestNet
2002      *  id=3
2003      *  pairwise_cipher=NONE
2004      *  group_cipher=NONE
2005      *  key_mgmt=NONE
2006      *  wpa_state=COMPLETED
2007      *  ip_address=X.X.X.X
2008      */
status()2009     public synchronized String status() {
2010         if (mWifiState.get() != WIFI_STATE_ENABLED) {
2011             return null;
2012         }
2013         return WifiNative.statusCommand();
2014     }
2015 
2016     /**
2017      * Get RSSI to currently connected network
2018      *
2019      * @return RSSI value, -1 on failure
2020      */
getRssi()2021     public synchronized int getRssi() {
2022         if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
2023             return -1;
2024         }
2025         return WifiNative.getRssiApproxCommand();
2026     }
2027 
2028     /**
2029      * Get approx RSSI to currently connected network
2030      *
2031      * @return RSSI value, -1 on failure
2032      */
getRssiApprox()2033     public synchronized int getRssiApprox() {
2034         if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
2035             return -1;
2036         }
2037         return WifiNative.getRssiApproxCommand();
2038     }
2039 
2040     /**
2041      * Get link speed to currently connected network
2042      *
2043      * @return link speed, -1 on failure
2044      */
getLinkSpeed()2045     public synchronized int getLinkSpeed() {
2046         if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
2047             return -1;
2048         }
2049         return WifiNative.getLinkSpeedCommand();
2050     }
2051 
2052     /**
2053      * Start driver
2054      *
2055      * @return {@code true} if the operation succeeds, {@code false} otherwise
2056      */
startDriver()2057     public synchronized boolean startDriver() {
2058         if (mWifiState.get() != WIFI_STATE_ENABLED) {
2059             return false;
2060         }
2061         return WifiNative.startDriverCommand();
2062     }
2063 
2064     /**
2065      * Stop driver
2066      *
2067      * @return {@code true} if the operation succeeds, {@code false} otherwise
2068      */
stopDriver()2069     public synchronized boolean stopDriver() {
2070         /* Driver stop should not happen only when supplicant event
2071          * DRIVER_STOPPED has already been handled */
2072         if (mWifiState.get() != WIFI_STATE_ENABLED || mRunState == RUN_STATE_STOPPED) {
2073             return false;
2074         }
2075         return WifiNative.stopDriverCommand();
2076     }
2077 
2078     /**
2079      * Start packet filtering
2080      *
2081      * @return {@code true} if the operation succeeds, {@code false} otherwise
2082      */
startPacketFiltering()2083     public synchronized boolean startPacketFiltering() {
2084         if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
2085             return false;
2086         }
2087         return WifiNative.startPacketFiltering();
2088     }
2089 
2090     /**
2091      * Stop packet filtering
2092      *
2093      * @return {@code true} if the operation succeeds, {@code false} otherwise
2094      */
stopPacketFiltering()2095     public synchronized boolean stopPacketFiltering() {
2096         if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
2097             return false;
2098         }
2099         return WifiNative.stopPacketFiltering();
2100     }
2101 
2102     /**
2103      * Get power mode
2104      * @return power mode
2105      */
getPowerMode()2106     public synchronized int getPowerMode() {
2107         if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) {
2108             return -1;
2109         }
2110         return WifiNative.getPowerModeCommand();
2111     }
2112 
2113     /**
2114      * Set power mode
2115      * @param mode
2116      *     DRIVER_POWER_MODE_AUTO
2117      *     DRIVER_POWER_MODE_ACTIVE
2118      *
2119      * Uses reference counting to keep power mode active
2120      * as long as one entity wants power mode to be active.
2121      *
2122      * For example, WifiLock high perf mode can keep power mode active
2123      * or a DHCP session can keep it active. As long as one entity wants
2124      * it enabled, it should stay that way
2125      *
2126      */
setPowerMode(int mode)2127     private synchronized void setPowerMode(int mode) {
2128 
2129         /* It is good to plumb power mode change
2130          * even if ref count indicates already done
2131          * since we could have a case of previous failure.
2132          */
2133         switch(mode) {
2134             case DRIVER_POWER_MODE_ACTIVE:
2135                 mPowerModeRefCount++;
2136                 break;
2137             case DRIVER_POWER_MODE_AUTO:
2138                 mPowerModeRefCount--;
2139                 if (mPowerModeRefCount > 0) {
2140                     return;
2141                 } else {
2142                     /* Keep refcount from becoming negative */
2143                     mPowerModeRefCount = 0;
2144                 }
2145                 break;
2146         }
2147 
2148         if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
2149             return;
2150         }
2151 
2152         WifiNative.setPowerModeCommand(mode);
2153     }
2154 
2155     /**
2156      * Set the number of allowed radio frequency channels from the system
2157      * setting value, if any.
2158      * @return {@code true} if the operation succeeds, {@code false} otherwise, e.g.,
2159      * the number of channels is invalid.
2160      */
setNumAllowedChannels()2161     public synchronized boolean setNumAllowedChannels() {
2162         if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
2163             return false;
2164         }
2165         try {
2166             return setNumAllowedChannels(
2167                     Settings.Secure.getInt(mContext.getContentResolver(),
2168                     Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS));
2169         } catch (Settings.SettingNotFoundException e) {
2170             if (mNumAllowedChannels != 0) {
2171                 WifiNative.setNumAllowedChannelsCommand(mNumAllowedChannels);
2172             }
2173             // otherwise, use the driver default
2174         }
2175         return true;
2176     }
2177 
2178     /**
2179      * Set the number of radio frequency channels that are allowed to be used
2180      * in the current regulatory domain.
2181      * @param numChannels the number of allowed channels. Must be greater than 0
2182      * and less than or equal to 16.
2183      * @return {@code true} if the operation succeeds, {@code false} otherwise, e.g.,
2184      * {@code numChannels} is outside the valid range.
2185      */
setNumAllowedChannels(int numChannels)2186     public synchronized boolean setNumAllowedChannels(int numChannels) {
2187         if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
2188             return false;
2189         }
2190         mNumAllowedChannels = numChannels;
2191         return WifiNative.setNumAllowedChannelsCommand(numChannels);
2192     }
2193 
2194     /**
2195      * Get number of allowed channels
2196      *
2197      * @return channel count, -1 on failure
2198      */
getNumAllowedChannels()2199     public synchronized int getNumAllowedChannels() {
2200         if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
2201             return -1;
2202         }
2203         return WifiNative.getNumAllowedChannelsCommand();
2204     }
2205 
2206     /**
2207      * Set bluetooth coex mode:
2208      *
2209      * @param mode
2210      *  BLUETOOTH_COEXISTENCE_MODE_ENABLED
2211      *  BLUETOOTH_COEXISTENCE_MODE_DISABLED
2212      *  BLUETOOTH_COEXISTENCE_MODE_SENSE
2213      * @return {@code true} if the operation succeeds, {@code false} otherwise
2214      */
setBluetoothCoexistenceMode(int mode)2215     public synchronized boolean setBluetoothCoexistenceMode(int mode) {
2216         if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
2217             return false;
2218         }
2219         return WifiNative.setBluetoothCoexistenceModeCommand(mode);
2220     }
2221 
2222     /**
2223      * Enable or disable Bluetooth coexistence scan mode. When this mode is on,
2224      * some of the low-level scan parameters used by the driver are changed to
2225      * reduce interference with A2DP streaming.
2226      *
2227      * @param isBluetoothPlaying whether to enable or disable this mode
2228      */
setBluetoothScanMode(boolean isBluetoothPlaying)2229     public synchronized void setBluetoothScanMode(boolean isBluetoothPlaying) {
2230         if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
2231             return;
2232         }
2233         WifiNative.setBluetoothCoexistenceScanModeCommand(isBluetoothPlaying);
2234     }
2235 
2236     /**
2237      * Save configuration on supplicant
2238      *
2239      * @return {@code true} if the operation succeeds, {@code false} otherwise
2240      */
saveConfig()2241     public synchronized boolean saveConfig() {
2242         if (mWifiState.get() != WIFI_STATE_ENABLED) {
2243             return false;
2244         }
2245         return WifiNative.saveConfigCommand();
2246     }
2247 
2248     /**
2249      * Reload the configuration from file
2250      *
2251      * @return {@code true} if the operation succeeds, {@code false} otherwise
2252      */
reloadConfig()2253     public synchronized boolean reloadConfig() {
2254         if (mWifiState.get() != WIFI_STATE_ENABLED) {
2255             return false;
2256         }
2257         return WifiNative.reloadConfigCommand();
2258     }
2259 
setRadio(boolean turnOn)2260     public boolean setRadio(boolean turnOn) {
2261         return mWM.setWifiEnabled(turnOn);
2262     }
2263 
2264     /**
2265      * {@inheritDoc}
2266      * There are currently no Wi-Fi-specific features supported.
2267      * @param feature the name of the feature
2268      * @return {@code -1} indicating failure, always
2269      */
startUsingNetworkFeature(String feature, int callingPid, int callingUid)2270     public int startUsingNetworkFeature(String feature, int callingPid, int callingUid) {
2271         return -1;
2272     }
2273 
2274     /**
2275      * {@inheritDoc}
2276      * There are currently no Wi-Fi-specific features supported.
2277      * @param feature the name of the feature
2278      * @return {@code -1} indicating failure, always
2279      */
stopUsingNetworkFeature(String feature, int callingPid, int callingUid)2280     public int stopUsingNetworkFeature(String feature, int callingPid, int callingUid) {
2281         return -1;
2282     }
2283 
2284     @Override
interpretScanResultsAvailable()2285     public void interpretScanResultsAvailable() {
2286 
2287         // If we shouldn't place a notification on available networks, then
2288         // don't bother doing any of the following
2289         if (!mNotificationEnabled) return;
2290 
2291         NetworkInfo networkInfo = getNetworkInfo();
2292 
2293         State state = networkInfo.getState();
2294         if ((state == NetworkInfo.State.DISCONNECTED)
2295                 || (state == NetworkInfo.State.UNKNOWN)) {
2296 
2297             // Look for an open network
2298             List<ScanResult> scanResults = getScanResultsList();
2299             if (scanResults != null) {
2300                 int numOpenNetworks = 0;
2301                 for (int i = scanResults.size() - 1; i >= 0; i--) {
2302                     ScanResult scanResult = scanResults.get(i);
2303 
2304                     if (TextUtils.isEmpty(scanResult.capabilities)) {
2305                         numOpenNetworks++;
2306                     }
2307                 }
2308 
2309                 if (numOpenNetworks > 0) {
2310                     if (++mNumScansSinceNetworkStateChange >= NUM_SCANS_BEFORE_ACTUALLY_SCANNING) {
2311                         /*
2312                          * We've scanned continuously at least
2313                          * NUM_SCANS_BEFORE_NOTIFICATION times. The user
2314                          * probably does not have a remembered network in range,
2315                          * since otherwise supplicant would have tried to
2316                          * associate and thus resetting this counter.
2317                          */
2318                         setNotificationVisible(true, numOpenNetworks, false, 0);
2319                     }
2320                     return;
2321                 }
2322             }
2323         }
2324 
2325         // No open networks in range, remove the notification
2326         setNotificationVisible(false, 0, false, 0);
2327     }
2328 
2329     /**
2330      * Display or don't display a notification that there are open Wi-Fi networks.
2331      * @param visible {@code true} if notification should be visible, {@code false} otherwise
2332      * @param numNetworks the number networks seen
2333      * @param force {@code true} to force notification to be shown/not-shown,
2334      * even if it is already shown/not-shown.
2335      * @param delay time in milliseconds after which the notification should be made
2336      * visible or invisible.
2337      */
setNotificationVisible(boolean visible, int numNetworks, boolean force, int delay)2338     public void setNotificationVisible(boolean visible, int numNetworks, boolean force, int delay) {
2339 
2340         // Since we use auto cancel on the notification, when the
2341         // mNetworksAvailableNotificationShown is true, the notification may
2342         // have actually been canceled.  However, when it is false we know
2343         // for sure that it is not being shown (it will not be shown any other
2344         // place than here)
2345 
2346         // If it should be hidden and it is already hidden, then noop
2347         if (!visible && !mNotificationShown && !force) {
2348             return;
2349         }
2350 
2351         Message message;
2352         if (visible) {
2353 
2354             // Not enough time has passed to show the notification again
2355             if (System.currentTimeMillis() < mNotificationRepeatTime) {
2356                 return;
2357             }
2358 
2359             if (mNotification == null) {
2360                 // Cache the Notification mainly so we can remove the
2361                 // EVENT_NOTIFICATION_CHANGED message with this Notification from
2362                 // the queue later
2363                 mNotification = new Notification();
2364                 mNotification.when = 0;
2365                 mNotification.icon = ICON_NETWORKS_AVAILABLE;
2366                 mNotification.flags = Notification.FLAG_AUTO_CANCEL;
2367                 mNotification.contentIntent = PendingIntent.getActivity(mContext, 0,
2368                         new Intent(WifiManager.ACTION_PICK_WIFI_NETWORK), 0);
2369             }
2370 
2371             CharSequence title = mContext.getResources().getQuantityText(
2372                     com.android.internal.R.plurals.wifi_available, numNetworks);
2373             CharSequence details = mContext.getResources().getQuantityText(
2374                     com.android.internal.R.plurals.wifi_available_detailed, numNetworks);
2375             mNotification.tickerText = title;
2376             mNotification.setLatestEventInfo(mContext, title, details, mNotification.contentIntent);
2377 
2378             mNotificationRepeatTime = System.currentTimeMillis() + NOTIFICATION_REPEAT_DELAY_MS;
2379 
2380             message = mTarget.obtainMessage(EVENT_NOTIFICATION_CHANGED, 1,
2381                     ICON_NETWORKS_AVAILABLE, mNotification);
2382 
2383         } else {
2384 
2385             // Remove any pending messages to show the notification
2386             mTarget.removeMessages(EVENT_NOTIFICATION_CHANGED, mNotification);
2387 
2388             message = mTarget.obtainMessage(EVENT_NOTIFICATION_CHANGED, 0, ICON_NETWORKS_AVAILABLE);
2389         }
2390 
2391         mTarget.sendMessageDelayed(message, delay);
2392 
2393         mNotificationShown = visible;
2394     }
2395 
2396     /**
2397      * Clears variables related to tracking whether a notification has been
2398      * shown recently.
2399      * <p>
2400      * After calling this method, the timer that prevents notifications from
2401      * being shown too often will be cleared.
2402      */
resetNotificationTimer()2403     private void resetNotificationTimer() {
2404         mNotificationRepeatTime = 0;
2405         mNumScansSinceNetworkStateChange = 0;
2406     }
2407 
2408     @Override
toString()2409     public String toString() {
2410         StringBuffer sb = new StringBuffer();
2411         sb.append("interface ").append(mInterfaceName);
2412         sb.append(" runState=");
2413         if (mRunState >= 1 && mRunState <= mRunStateNames.length) {
2414             sb.append(mRunStateNames[mRunState-1]);
2415         } else {
2416             sb.append(mRunState);
2417         }
2418         sb.append(LS).append(mWifiInfo).append(LS);
2419         sb.append(mDhcpInfo).append(LS);
2420         sb.append("haveIpAddress=").append(mHaveIpAddress).
2421                 append(", obtainingIpAddress=").append(mObtainingIpAddress).
2422                 append(", scanModeActive=").append(mIsScanModeActive).append(LS).
2423                 append("lastSignalLevel=").append(mLastSignalLevel).
2424                 append(", explicitlyDisabled=").append(mTornDownByConnMgr);
2425         return sb.toString();
2426     }
2427 
2428     private class DhcpHandler extends Handler {
2429 
2430         private Handler mWifiStateTrackerHandler;
2431 
2432         /**
2433          * Whether to skip the DHCP result callback to the target. For example,
2434          * this could be set if the network we were requesting an IP for has
2435          * since been disconnected.
2436          * <p>
2437          * Note: There is still a chance where the client's intended DHCP
2438          * request not being canceled. For example, we are request for IP on
2439          * A, and he queues request for IP on B, and then cancels the request on
2440          * B while we're still requesting from A.
2441          */
2442         private boolean mCancelCallback;
2443 
2444         /**
2445          * Instance of the bluetooth headset helper. This needs to be created
2446          * early because there is a delay before it actually 'connects', as
2447          * noted by its javadoc. If we check before it is connected, it will be
2448          * in an error state and we will not disable coexistence.
2449          */
2450         private BluetoothHeadset mBluetoothHeadset;
2451 
DhcpHandler(Looper looper, Handler target)2452         public DhcpHandler(Looper looper, Handler target) {
2453             super(looper);
2454             mWifiStateTrackerHandler = target;
2455 
2456             mBluetoothHeadset = new BluetoothHeadset(mContext, null);
2457         }
2458 
handleMessage(Message msg)2459         public void handleMessage(Message msg) {
2460             int event;
2461 
2462             switch (msg.what) {
2463                 case EVENT_DHCP_START:
2464                 case EVENT_DHCP_RENEW:
2465                     boolean modifiedBluetoothCoexistenceMode = false;
2466                     int powerMode = DRIVER_POWER_MODE_AUTO;
2467 
2468                     if (shouldDisableCoexistenceMode()) {
2469                         /*
2470                          * There are problems setting the Wi-Fi driver's power
2471                          * mode to active when bluetooth coexistence mode is
2472                          * enabled or sense.
2473                          * <p>
2474                          * We set Wi-Fi to active mode when
2475                          * obtaining an IP address because we've found
2476                          * compatibility issues with some routers with low power
2477                          * mode.
2478                          * <p>
2479                          * In order for this active power mode to properly be set,
2480                          * we disable coexistence mode until we're done with
2481                          * obtaining an IP address.  One exception is if we
2482                          * are currently connected to a headset, since disabling
2483                          * coexistence would interrupt that connection.
2484                          */
2485                         modifiedBluetoothCoexistenceMode = true;
2486 
2487                         // Disable the coexistence mode
2488                         setBluetoothCoexistenceMode(
2489                                 WifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED);
2490                     }
2491 
2492                     powerMode = getPowerMode();
2493                     if (powerMode < 0) {
2494                         // Handle the case where supplicant driver does not support
2495                         // getPowerModeCommand.
2496                         powerMode = DRIVER_POWER_MODE_AUTO;
2497                     }
2498                     if (powerMode != DRIVER_POWER_MODE_ACTIVE) {
2499                         setPowerMode(DRIVER_POWER_MODE_ACTIVE);
2500                     }
2501 
2502                     synchronized (this) {
2503                         // A new request is being made, so assume we will callback
2504                         mCancelCallback = false;
2505                     }
2506 
2507                     if (msg.what == EVENT_DHCP_START) {
2508                         Log.d(TAG, "DHCP request started");
2509                         if (NetworkUtils.runDhcp(mInterfaceName, mDhcpInfo)) {
2510                             event = EVENT_INTERFACE_CONFIGURATION_SUCCEEDED;
2511                             Log.d(TAG, "DHCP succeeded with lease: " + mDhcpInfo.leaseDuration);
2512                             setDhcpRenewalAlarm(mDhcpInfo.leaseDuration);
2513                        } else {
2514                             event = EVENT_INTERFACE_CONFIGURATION_FAILED;
2515                             Log.e(TAG, "DHCP request failed: " + NetworkUtils.getDhcpError());
2516                         }
2517                         synchronized (this) {
2518                             if (!mCancelCallback) {
2519                                 mWifiStateTrackerHandler.sendEmptyMessage(event);
2520                             }
2521                         }
2522 
2523                     } else if (msg.what == EVENT_DHCP_RENEW) {
2524                         Log.d(TAG, "DHCP renewal started");
2525                         int oIp = mDhcpInfo.ipAddress;
2526                         int oGw = mDhcpInfo.gateway;
2527                         int oMsk = mDhcpInfo.netmask;
2528                         int oDns1 = mDhcpInfo.dns1;
2529                         int oDns2 = mDhcpInfo.dns2;
2530 
2531                         if (NetworkUtils.runDhcpRenew(mInterfaceName, mDhcpInfo)) {
2532                             Log.d(TAG, "DHCP renewal with lease: " + mDhcpInfo.leaseDuration);
2533 
2534                             boolean changed =
2535                                 (oIp   != mDhcpInfo.ipAddress ||
2536                                  oGw   != mDhcpInfo.gateway ||
2537                                  oMsk  != mDhcpInfo.netmask ||
2538                                  oDns1 != mDhcpInfo.dns1 ||
2539                                  oDns2 != mDhcpInfo.dns2);
2540 
2541                             if (changed) {
2542                                 Log.d(TAG, "IP config change on renewal");
2543                                 mWifiInfo.setIpAddress(mDhcpInfo.ipAddress);
2544                                 NetworkUtils.resetConnections(mInterfaceName);
2545                                 msg = mTarget.obtainMessage(EVENT_CONFIGURATION_CHANGED,
2546                                         mNetworkInfo);
2547                                 msg.sendToTarget();
2548                             }
2549 
2550                             setDhcpRenewalAlarm(mDhcpInfo.leaseDuration);
2551                         } else {
2552                             event = EVENT_INTERFACE_CONFIGURATION_FAILED;
2553                             Log.d(TAG, "DHCP renewal failed: " + NetworkUtils.getDhcpError());
2554 
2555                             synchronized (this) {
2556                                 if (!mCancelCallback) {
2557                                     mWifiStateTrackerHandler.sendEmptyMessage(event);
2558                                 }
2559                             }
2560                         }
2561                     }
2562 
2563                     if (powerMode != DRIVER_POWER_MODE_ACTIVE) {
2564                         setPowerMode(powerMode);
2565                     }
2566 
2567                     if (modifiedBluetoothCoexistenceMode) {
2568                         // Set the coexistence mode back to its default value
2569                         setBluetoothCoexistenceMode(
2570                                 WifiNative.BLUETOOTH_COEXISTENCE_MODE_SENSE);
2571                     }
2572 
2573                     break;
2574             }
2575         }
2576 
setCancelCallback(boolean cancelCallback)2577         public synchronized void setCancelCallback(boolean cancelCallback) {
2578             mCancelCallback = cancelCallback;
2579             if (cancelCallback) {
2580                 mAlarmManager.cancel(mDhcpRenewalIntent);
2581             }
2582         }
2583 
2584         /**
2585          * Whether to disable coexistence mode while obtaining IP address. This
2586          * logic will return true only if the current bluetooth
2587          * headset/handsfree state is disconnected. This means if it is in an
2588          * error state, we will NOT disable coexistence mode to err on the side
2589          * of safety.
2590          *
2591          * @return Whether to disable coexistence mode.
2592          */
shouldDisableCoexistenceMode()2593         private boolean shouldDisableCoexistenceMode() {
2594             int state = mBluetoothHeadset.getState(mBluetoothHeadset.getCurrentHeadset());
2595             return state == BluetoothHeadset.STATE_DISCONNECTED;
2596         }
2597 
setDhcpRenewalAlarm(long leaseDuration)2598         private void setDhcpRenewalAlarm(long leaseDuration) {
2599             //Do it a bit earlier than half the lease duration time
2600             //to beat the native DHCP client and avoid extra packets
2601             //48% for one hour lease time = 29 minutes
2602             if (leaseDuration < MIN_RENEWAL_TIME_SECS) {
2603                 leaseDuration = MIN_RENEWAL_TIME_SECS;
2604             }
2605             mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
2606                     SystemClock.elapsedRealtime() +
2607                     leaseDuration * 480, //in milliseconds
2608                     mDhcpRenewalIntent);
2609         }
2610 
2611     }
2612 
checkUseStaticIp()2613     private void checkUseStaticIp() {
2614         mUseStaticIp = false;
2615         final ContentResolver cr = mContext.getContentResolver();
2616         try {
2617             if (Settings.System.getInt(cr, Settings.System.WIFI_USE_STATIC_IP) == 0) {
2618                 return;
2619             }
2620         } catch (Settings.SettingNotFoundException e) {
2621             return;
2622         }
2623 
2624         try {
2625             String addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_IP);
2626             if (addr != null) {
2627                 mDhcpInfo.ipAddress = stringToIpAddr(addr);
2628             } else {
2629                 return;
2630             }
2631             addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_GATEWAY);
2632             if (addr != null) {
2633                 mDhcpInfo.gateway = stringToIpAddr(addr);
2634             } else {
2635                 return;
2636             }
2637             addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_NETMASK);
2638             if (addr != null) {
2639                 mDhcpInfo.netmask = stringToIpAddr(addr);
2640             } else {
2641                 return;
2642             }
2643             addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_DNS1);
2644             if (addr != null) {
2645                 mDhcpInfo.dns1 = stringToIpAddr(addr);
2646             } else {
2647                 return;
2648             }
2649             addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_DNS2);
2650             if (addr != null) {
2651                 mDhcpInfo.dns2 = stringToIpAddr(addr);
2652             } else {
2653                 mDhcpInfo.dns2 = 0;
2654             }
2655         } catch (UnknownHostException e) {
2656             return;
2657         }
2658         mUseStaticIp = true;
2659     }
2660 
stringToIpAddr(String addrString)2661     private static int stringToIpAddr(String addrString) throws UnknownHostException {
2662         try {
2663             String[] parts = addrString.split("\\.");
2664             if (parts.length != 4) {
2665                 throw new UnknownHostException(addrString);
2666             }
2667 
2668             int a = Integer.parseInt(parts[0])      ;
2669             int b = Integer.parseInt(parts[1]) <<  8;
2670             int c = Integer.parseInt(parts[2]) << 16;
2671             int d = Integer.parseInt(parts[3]) << 24;
2672 
2673             return a | b | c | d;
2674         } catch (NumberFormatException ex) {
2675             throw new UnknownHostException(addrString);
2676         }
2677     }
2678 
getMaxDhcpRetries()2679     private int getMaxDhcpRetries() {
2680         return Settings.Secure.getInt(mContext.getContentResolver(),
2681                                       Settings.Secure.WIFI_MAX_DHCP_RETRY_COUNT,
2682                                       DEFAULT_MAX_DHCP_RETRIES);
2683     }
2684 
2685     private class SettingsObserver extends ContentObserver {
SettingsObserver(Handler handler)2686         public SettingsObserver(Handler handler) {
2687             super(handler);
2688             ContentResolver cr = mContext.getContentResolver();
2689             cr.registerContentObserver(Settings.System.getUriFor(
2690                 Settings.System.WIFI_USE_STATIC_IP), false, this);
2691             cr.registerContentObserver(Settings.System.getUriFor(
2692                 Settings.System.WIFI_STATIC_IP), false, this);
2693             cr.registerContentObserver(Settings.System.getUriFor(
2694                 Settings.System.WIFI_STATIC_GATEWAY), false, this);
2695             cr.registerContentObserver(Settings.System.getUriFor(
2696                 Settings.System.WIFI_STATIC_NETMASK), false, this);
2697             cr.registerContentObserver(Settings.System.getUriFor(
2698                 Settings.System.WIFI_STATIC_DNS1), false, this);
2699             cr.registerContentObserver(Settings.System.getUriFor(
2700                 Settings.System.WIFI_STATIC_DNS2), false, this);
2701         }
2702 
onChange(boolean selfChange)2703         public void onChange(boolean selfChange) {
2704             super.onChange(selfChange);
2705 
2706             boolean wasStaticIp = mUseStaticIp;
2707             int oIp, oGw, oMsk, oDns1, oDns2;
2708             oIp = oGw = oMsk = oDns1 = oDns2 = 0;
2709             if (wasStaticIp) {
2710                 oIp = mDhcpInfo.ipAddress;
2711                 oGw = mDhcpInfo.gateway;
2712                 oMsk = mDhcpInfo.netmask;
2713                 oDns1 = mDhcpInfo.dns1;
2714                 oDns2 = mDhcpInfo.dns2;
2715             }
2716             checkUseStaticIp();
2717 
2718             if (mWifiInfo.getSupplicantState() == SupplicantState.UNINITIALIZED) {
2719                 return;
2720             }
2721 
2722             boolean changed =
2723                 (wasStaticIp != mUseStaticIp) ||
2724                     (wasStaticIp && (
2725                         oIp   != mDhcpInfo.ipAddress ||
2726                         oGw   != mDhcpInfo.gateway ||
2727                         oMsk  != mDhcpInfo.netmask ||
2728                         oDns1 != mDhcpInfo.dns1 ||
2729                         oDns2 != mDhcpInfo.dns2));
2730 
2731             if (changed) {
2732                 resetConnections(true);
2733                 configureInterface();
2734                 if (mUseStaticIp) {
2735                     Message msg = mTarget.obtainMessage(EVENT_CONFIGURATION_CHANGED, mNetworkInfo);
2736                     msg.sendToTarget();
2737                 }
2738             }
2739         }
2740     }
2741 
2742     private class NotificationEnabledSettingObserver extends ContentObserver {
2743 
NotificationEnabledSettingObserver(Handler handler)2744         public NotificationEnabledSettingObserver(Handler handler) {
2745             super(handler);
2746         }
2747 
register()2748         public void register() {
2749             ContentResolver cr = mContext.getContentResolver();
2750             cr.registerContentObserver(Settings.Secure.getUriFor(
2751                 Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON), true, this);
2752             mNotificationEnabled = getValue();
2753         }
2754 
2755         @Override
onChange(boolean selfChange)2756         public void onChange(boolean selfChange) {
2757             super.onChange(selfChange);
2758 
2759             mNotificationEnabled = getValue();
2760             if (!mNotificationEnabled) {
2761                 // Remove any notification that may be showing
2762                 setNotificationVisible(false, 0, true, 0);
2763             }
2764 
2765             resetNotificationTimer();
2766         }
2767 
getValue()2768         private boolean getValue() {
2769             return Settings.Secure.getInt(mContext.getContentResolver(),
2770                     Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 1) == 1;
2771         }
2772     }
2773 }
2774