2 * Copyright (C) 2008 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package android.net.wifi;
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;
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;
63 import java.net.UnknownHostException;
64 import java.util.ArrayList;
65 import java.util.List;
67 import java.util.concurrent.atomic.AtomicInteger;
68 import java.util.concurrent.atomic.AtomicBoolean;
71 * Track the state of Wifi connectivity. All event handling is done here,
72 * and all changes in connectivity state are initiated here.
76 public class WifiStateTracker extends NetworkStateTracker {
78 private static final boolean LOCAL_LOGD = Config.LOGD || false;
80 private static final String TAG = "WifiStateTracker";
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;
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;
103 * The driver is started or stopped. The object will be the state: true for
104 * started, false for stopped.
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;
111 * The driver state indication.
113 private static final int DRIVER_STARTED = 0;
114 private static final int DRIVER_STOPPED = 1;
115 private static final int DRIVER_HUNG = 2;
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).
122 private static final int POLL_STATUS_INTERVAL_MSECS = 3000;
125 * The max number of the WPA supplicant loop iterations before we
126 * decide that the loop should be terminated:
128 private static final int MAX_SUPPLICANT_LOOP_ITERATIONS = 4;
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.
143 private static final int DISCONNECT_DELAY_MSECS = 5000;
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.
149 private static final int RECONNECT_DELAY_MSECS = 2000;
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
157 private static final int KICKSTART_SCANNING_DELAY_MSECS = 15000;
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.
164 * See {@link Settings.Secure#WIFI_MAX_DHCP_RETRY_COUNT}. This is the default
165 * value if a Settings value is not present.
167 private static final int DEFAULT_MAX_DHCP_RETRIES = 9;
169 //Minimum dhcp lease duration for renewal
170 private static final int MIN_RENEWAL_TIME_SECS = 5 * 60; //5 minutes
172 private static final int DRIVER_POWER_MODE_AUTO = 0;
173 private static final int DRIVER_POWER_MODE_ACTIVE = 1;
176 * The current WPA supplicant loop state (used to detect looping behavior):
178 private SupplicantState mSupplicantLoopState = SupplicantState.DISCONNECTED;
181 * The current number of WPA supplicant loop iterations:
183 private int mNumSupplicantLoopIterations = 0;
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.
191 private int mNumSupplicantStateChanges = 0;
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
199 private boolean mPasswordKeyMayBeIncorrect = false;
201 public static final int SUPPL_SCAN_HANDLING_NORMAL = 1;
202 public static final int SUPPL_SCAN_HANDLING_LIST_ONLY = 2;
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;
212 * A DISCONNECT event has been received, but processing it
215 private boolean mDisconnectPending;
217 * An operation has been performed as a result of which we expect the next event
218 * will be a DISCONNECT.
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;
230 private AlarmManager mAlarmManager;
231 private PendingIntent mDhcpRenewalIntent;
232 private PowerManager.WakeLock mDhcpRenewWakeLock;
233 private static final String WAKELOCK_TAG = "*wifi*";
235 private static final int DHCP_RENEW = 0;
236 private static final String ACTION_DHCP_RENEW = "android.net.wifi.DHCP_RENEW";
239 /* Tracks if any network in the configuration is disabled */
240 private AtomicBoolean mIsAnyNetworkDisabled = new AtomicBoolean(false);
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;
246 // Variables relating to the 'available networks' notification
249 * The icon to show in the 'available networks' notification. This will also
250 * be the ID of the Notification given to the NotificationManager.
252 private static final int ICON_NETWORKS_AVAILABLE =
253 com.android.internal.R.drawable.stat_notify_wifi_in_range;
255 * When a notification is shown, we wait this amount before possibly showing it again.
257 private final long NOTIFICATION_REPEAT_DELAY_MS;
259 * Whether the user has set the setting to show the 'available networks' notification.
261 private boolean mNotificationEnabled;
263 * Observes the user setting to keep {@link #mNotificationEnabled} in sync.
265 private NotificationEnabledSettingObserver mNotificationEnabledSettingObserver;
267 * The {@link System#currentTimeMillis()} must be at least this value for us
268 * to show the notification again.
270 private long mNotificationRepeatTime;
272 * The Notification object given to the NotificationManager.
274 private Notification mNotification;
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.
281 private boolean mNotificationShown;
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.
287 private static final int NUM_SCANS_BEFORE_ACTUALLY_SCANNING = 3;
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.
294 private int mNumScansSinceNetworkStateChange;
296 * Observes the static IP address settings.
298 private SettingsObserver mSettingsObserver;
300 private boolean mIsScanModeActive;
301 private boolean mEnableRssiPolling;
302 private boolean mIsHighPerfEnabled;
303 private int mPowerModeRefCount = 0;
304 private int mOptimizationsDisabledRefCount = 0;
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}
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.
317 private final AtomicInteger mWifiState = new AtomicInteger(WIFI_STATE_UNKNOWN);
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;
325 private static final String mRunStateNames[] = {
331 private int mRunState;
333 private final IBatteryStats mBatteryStats;
335 private boolean mIsScanOnly;
337 private BluetoothA2dp mBluetoothA2dp;
339 private String mInterfaceName;
340 private static String LS = System.getProperty("line.separator");
342 private static String[] sDnsPropNames;
345 * Keep track of whether we last told the battery stats we had started.
347 private boolean mReportedRunning = false;
350 * Most recently set source of starting WIFI.
352 private final WorkSource mRunningWifiUids = new WorkSource();
355 * The last reported UIDs that were responsible for starting WIFI.
357 private final WorkSource mLastRunningWifiUids = new WorkSource();
360 * A structure for supplying information about a supplicant state
361 * change in the STATE_CHANGE event message that comes from the
365 private static class SupplicantStateChangeResult {
366 SupplicantStateChangeResult(int networkId, String BSSID, SupplicantState state) {
369 this.networkId = networkId;
373 SupplicantState state;
377 * A structure for supplying information about a connection in
378 * the CONNECTED event message that comes from the WifiMonitor
381 private static class NetworkStateChangeResult {
382 NetworkStateChangeResult(DetailedState state, String BSSID, int networkId) {
385 this.networkId = networkId;
392 public WifiStateTracker(Context context, Handler target) {
393 super(context, target, ConnectivityManager.TYPE_WIFI, 0, "WIFI", "");
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;
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);
410 mContext.registerReceiver(
411 new BroadcastReceiver() {
413 public void onReceive(Context context, Intent intent) {
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);
422 },new IntentFilter(ACTION_DHCP_RENEW));
424 PowerManager powerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
425 mDhcpRenewWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG);
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();
433 mSettingsObserver = new SettingsObserver(new Handler());
435 mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo"));
440 * Helper method: sets the supplicant state and keeps the network
442 * @param state the new state
444 private void setSupplicantState(SupplicantState state) {
445 mWifiInfo.setSupplicantState(state);
450 public SupplicantState getSupplicantState() {
451 return mWifiInfo.getSupplicantState();
455 * Helper method: sets the supplicant state and keeps the network
456 * info updated (string version).
457 * @param stateName the string name of the new state
459 private void setSupplicantState(String stateName) {
460 mWifiInfo.setSupplicantState(stateName);
466 * Helper method: sets the boolean indicating that the connection
467 * manager asked the network to be torn down (and so only the connection
468 * manager can set it up again).
469 * network info updated.
470 * @param flag {@code true} if explicitly disabled.
472 private void setTornDownByConnMgr(boolean flag) {
473 mTornDownByConnMgr = flag;
478 * Return the IP addresses of the DNS servers available for the WLAN
480 * @return a list of DNS addresses, with no holes.
482 public String[] getNameServers() {
483 return getNameServerList(sDnsPropNames);
487 * Return the name of our WLAN network interface.
488 * @return the name of our interface.
490 public String getInterfaceName() {
491 return mInterfaceName;
495 * Return the system properties name associated with the tcp buffer sizes
498 public String getTcpBufferSizesPropName() {
499 return "net.tcp.buffersize.wifi";
502 public void startMonitoring() {
504 * Get a handle on the WifiManager. This cannot be done in our
505 * constructor, because the Wifi service is not yet registered.
507 mWM = (WifiManager)mContext.getSystemService(Context.WIFI_SERVICE);
510 public void startEventLoop() {
511 mWifiMonitor.startMonitoring();
515 * Wi-Fi is considered available as long as we have a connection to the
516 * supplicant daemon and there is at least one enabled network. If a teardown
517 * was explicitly requested, then Wi-Fi can be restarted with a reconnect
518 * request, so it is considered available. If the driver has been stopped
519 * for any reason other than a teardown request, Wi-Fi is considered
521 * @return {@code true} if Wi-Fi connections are possible
523 public synchronized boolean isAvailable() {
525 * TODO: Need to also look at scan results to see whether we're
526 * in range of any access points. If we have scan results that
527 * are no more than N seconds old, use those, otherwise, initiate
528 * a scan and wait for the results. This only matters if we
529 * allow mobile to be the preferred network.
531 SupplicantState suppState = mWifiInfo.getSupplicantState();
532 return suppState != SupplicantState.UNINITIALIZED &&
533 suppState != SupplicantState.INACTIVE &&
534 (mTornDownByConnMgr || !isDriverStopped());
539 * There are currently no defined Wi-Fi subtypes.
541 public int getNetworkSubtype() {
546 * Helper method: updates the network info object to keep it in sync with
547 * the Wi-Fi state tracker.
549 private void updateNetworkInfo() {
550 mNetworkInfo.setIsAvailable(isAvailable());
554 * Report whether the Wi-Fi connection is fully configured for data.
555 * @return {@code true} if the {@link SupplicantState} is
556 * {@link android.net.wifi.SupplicantState#COMPLETED COMPLETED}.
558 public boolean isConnectionCompleted() {
559 return mWifiInfo.getSupplicantState() == SupplicantState.COMPLETED;
563 * Report whether the Wi-Fi connection has successfully acquired an IP address.
564 * @return {@code true} if the Wi-Fi connection has been assigned an IP address.
566 public boolean hasIpAddress() {
567 return mHaveIpAddress;
571 * Send the tracker a notification that a user-entered password key
572 * may be incorrect (i.e., caused authentication to fail).
574 void notifyPasswordKeyMayBeIncorrect() {
575 sendEmptyMessage(EVENT_PASSWORD_KEY_MAY_BE_INCORRECT);
579 * Send the tracker a notification that a connection to the supplicant
580 * daemon has been established.
582 void notifySupplicantConnection() {
583 sendEmptyMessage(EVENT_SUPPLICANT_CONNECTION);
587 * Send the tracker a notification that the state of the supplicant
589 * @param networkId the configured network on which the state change occurred
590 * @param newState the new {@code SupplicantState}
592 void notifyStateChange(int networkId, String BSSID, SupplicantState newState) {
593 Message msg = Message.obtain(
594 this, EVENT_SUPPLICANT_STATE_CHANGED,
595 new SupplicantStateChangeResult(networkId, BSSID, newState));
600 * Send the tracker a notification that the state of Wifi connectivity
602 * @param networkId the configured network on which the state change occurred
603 * @param newState the new network state
604 * @param BSSID when the new state is {@link DetailedState#CONNECTED
605 * NetworkInfo.DetailedState.CONNECTED},
606 * this is the MAC address of the access point. Otherwise, it
609 void notifyStateChange(DetailedState newState, String BSSID, int networkId) {
610 Message msg = Message.obtain(
611 this, EVENT_NETWORK_STATE_CHANGED,
612 new NetworkStateChangeResult(newState, BSSID, networkId));
617 * Send the tracker a notification that a scan has completed, and results
620 void notifyScanResultsAvailable() {
621 // reset the supplicant's handling of scan results to "normal" mode
622 setScanResultHandling(SUPPL_SCAN_HANDLING_NORMAL);
623 sendEmptyMessage(EVENT_SCAN_RESULTS_AVAILABLE);
627 * Send the tracker a notification that we can no longer communicate with
628 * the supplicant daemon.
630 void notifySupplicantLost() {
631 sendEmptyMessage(EVENT_SUPPLICANT_DISCONNECT);
635 * Send the tracker a notification that the Wi-Fi driver has been stopped.
637 void notifyDriverStopped() {
638 // Send a driver stopped message to our handler
639 Message.obtain(this, EVENT_DRIVER_STATE_CHANGED, DRIVER_STOPPED, 0).sendToTarget();
643 * Send the tracker a notification that the Wi-Fi driver has been restarted after
644 * having been stopped.
646 void notifyDriverStarted() {
647 // Send a driver started message to our handler
648 Message.obtain(this, EVENT_DRIVER_STATE_CHANGED, DRIVER_STARTED, 0).sendToTarget();
652 * Send the tracker a notification that the Wi-Fi driver has hung and needs restarting.
654 void notifyDriverHung() {
655 // Send a driver hanged message to our handler
656 Message.obtain(this, EVENT_DRIVER_STATE_CHANGED, DRIVER_HUNG, 0).sendToTarget();
660 * Set the interval timer for polling connection information
661 * that is not delivered asynchronously.
663 private synchronized void checkPollTimer() {
664 if (mEnableRssiPolling &&
665 mWifiInfo.getSupplicantState() == SupplicantState.COMPLETED &&
666 !hasMessages(EVENT_POLL_INTERVAL)) {
667 sendEmptyMessageDelayed(EVENT_POLL_INTERVAL, POLL_STATUS_INTERVAL_MSECS);
672 * TODO: mRunState is not synchronized in some places
673 * address this as part of re-architect.
675 * TODO: We are exposing an additional public synchronized call
676 * for a wakelock optimization in WifiService. Remove it
677 * when we handle the wakelock in ConnectivityService.
679 public synchronized boolean isDriverStopped() {
680 return mRunState == RUN_STATE_STOPPED || mRunState == RUN_STATE_STOPPING;
683 public void updateBatteryWorkSourceLocked(WorkSource newSource) {
685 if (newSource != null) {
686 mRunningWifiUids.set(newSource);
688 if (mRunState == RUN_STATE_RUNNING) {
689 if (mReportedRunning) {
690 // If the work source has changed since last time, need
691 // to remove old work from battery stats.
692 if (mLastRunningWifiUids.diff(mRunningWifiUids)) {
693 mBatteryStats.noteWifiRunningChanged(mLastRunningWifiUids,
695 mLastRunningWifiUids.set(mRunningWifiUids);
698 // Now being started, report it.
699 mBatteryStats.noteWifiRunning(mRunningWifiUids);
700 mLastRunningWifiUids.set(mRunningWifiUids);
701 mReportedRunning = true;
703 } else if (mRunState == RUN_STATE_STOPPED) {
704 if (mReportedRunning) {
705 // Last reported we were running, time to stop.
706 mBatteryStats.noteWifiStopped(mLastRunningWifiUids);
707 mLastRunningWifiUids.clear();
708 mReportedRunning = false;
711 // State in transition -- nothing to update yet.
713 } catch (RemoteException ignore) {
718 * Set the run state to either "normal" or "scan-only".
719 * @param scanOnlyMode true if the new mode should be scan-only.
721 public synchronized void setScanOnlyMode(boolean scanOnlyMode) {
722 // do nothing unless scan-only mode is changing
723 if (mIsScanOnly != scanOnlyMode) {
724 int scanType = (scanOnlyMode ?
725 SUPPL_SCAN_HANDLING_LIST_ONLY : SUPPL_SCAN_HANDLING_NORMAL);
726 if (LOCAL_LOGD) Log.v(TAG, "Scan-only mode changing to " + scanOnlyMode + " scanType=" + scanType);
727 if (setScanResultHandling(scanType)) {
728 mIsScanOnly = scanOnlyMode;
729 if (!isDriverStopped()) {
741 * Set suspend mode optimizations. These include:
746 * Uses reference counting to keep the suspend optimizations disabled
747 * as long as one entity wants optimizations disabled.
749 * For example, WifiLock can keep suspend optimizations disabled
750 * or the user setting (wifi never sleeps) can keep suspend optimizations
751 * disabled. As long as one entity wants it disabled, it should stay
754 * @param enabled true if optimizations need enabled, false otherwise
756 public synchronized void setSuspendModeOptimizations(boolean enabled) {
758 /* It is good to plumb suspend optimization enable
759 * or disable even if ref count indicates already done
760 * since we could have a case of previous failure.
763 mOptimizationsDisabledRefCount++;
765 mOptimizationsDisabledRefCount--;
766 if (mOptimizationsDisabledRefCount > 0) {
769 /* Keep refcount from becoming negative */
770 mOptimizationsDisabledRefCount = 0;
774 if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
778 WifiNative.setSuspendOptimizationsCommand(enabled);
783 * Set high performance mode of operation. This would mean
784 * use active power mode and disable suspend optimizations
785 * @param enabled true if enabled, false otherwise
787 public synchronized void setHighPerfMode(boolean enabled) {
788 if (mIsHighPerfEnabled != enabled) {
790 setPowerMode(DRIVER_POWER_MODE_ACTIVE);
791 setSuspendModeOptimizations(false);
793 setPowerMode(DRIVER_POWER_MODE_AUTO);
794 setSuspendModeOptimizations(true);
796 mIsHighPerfEnabled = enabled;
797 Log.d(TAG,"high performance mode: " + enabled);
802 private void checkIsBluetoothPlaying() {
803 boolean isBluetoothPlaying = false;
804 Set<BluetoothDevice> connected = mBluetoothA2dp.getConnectedSinks();
806 for (BluetoothDevice device : connected) {
807 if (mBluetoothA2dp.getSinkState(device) == BluetoothA2dp.STATE_PLAYING) {
808 isBluetoothPlaying = true;
812 setBluetoothScanMode(isBluetoothPlaying);
815 public void enableRssiPolling(boolean enable) {
816 if (mEnableRssiPolling != enable) {
817 mEnableRssiPolling = enable;
823 * We release the wakelock in WifiService
827 * Releasing wakelock using both timer and
828 * a call from ConnectivityService requires
829 * a rethink. We had problems where WifiService
830 * could keep a wakelock forever if we delete
831 * messages in the asynchronous call
832 * from ConnectivityService
835 public void releaseWakeLock() {
839 * Tracks the WPA supplicant states to detect "loop" situations.
840 * @param newSupplicantState The new WPA supplicant state.
841 * @return {@code true} if the supplicant loop should be stopped
842 * and {@code false} if it should continue.
844 private boolean isSupplicantLooping(SupplicantState newSupplicantState) {
845 if (SupplicantState.ASSOCIATING.ordinal() <= newSupplicantState.ordinal()
846 && newSupplicantState.ordinal() < SupplicantState.COMPLETED.ordinal()) {
847 if (mSupplicantLoopState != newSupplicantState) {
848 if (newSupplicantState.ordinal() < mSupplicantLoopState.ordinal()) {
849 ++mNumSupplicantLoopIterations;
852 mSupplicantLoopState = newSupplicantState;
854 } else if (newSupplicantState == SupplicantState.COMPLETED) {
855 resetSupplicantLoopState();
858 return mNumSupplicantLoopIterations >= MAX_SUPPLICANT_LOOP_ITERATIONS;
862 * Resets the WPA supplicant loop state.
864 private void resetSupplicantLoopState() {
865 mNumSupplicantLoopIterations = 0;
869 public void handleMessage(Message msg) {
873 case EVENT_SUPPLICANT_CONNECTION:
874 mInterfaceName = SystemProperties.get("wlan.interface", "wlan0");
875 sDnsPropNames = new String[] {
876 "dhcp." + mInterfaceName + ".dns1",
877 "dhcp." + mInterfaceName + ".dns2"
879 mRunState = RUN_STATE_RUNNING;
881 synchronized (this) {
882 updateBatteryWorkSourceLocked(null);
883 macaddr = WifiNative.getMacAddressCommand();
885 if (macaddr != null) {
886 mWifiInfo.setMacAddress(macaddr);
890 /* Reset notification state on new connection */
891 resetNotificationTimer();
893 * DHCP requests are blocking, so run them in a separate thread.
895 HandlerThread dhcpThread = new HandlerThread("DHCP Handler Thread");
897 mDhcpTarget = new DhcpHandler(dhcpThread.getLooper(), this);
898 mIsScanModeActive = true;
899 mIsHighPerfEnabled = false;
900 mOptimizationsDisabledRefCount = 0;
901 mPowerModeRefCount = 0;
902 mTornDownByConnMgr = false;
905 mIsAnyNetworkDisabled.set(false);
906 requestConnectionInfo();
907 SupplicantState supplState = mWifiInfo.getSupplicantState();
909 if (LOCAL_LOGD) Log.v(TAG, "Connection to supplicant established, state=" +
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);
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.
921 if (supplState == SupplicantState.COMPLETED) {
922 mLastBssid = mWifiInfo.getBSSID();
923 mLastSsid = mWifiInfo.getSSID();
924 configureInterface();
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);
931 if (supplState == SupplicantState.COMPLETED && mHaveIpAddress) {
932 setDetailedState(DetailedState.CONNECTED);
934 setDetailedState(WifiInfo.getDetailedStateOf(supplState));
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.
941 mWM.initializeMulticastFiltering();
943 if (mBluetoothA2dp == null) {
944 mBluetoothA2dp = new BluetoothA2dp(mContext);
946 checkIsBluetoothPlaying();
948 // initialize this after the supplicant is alive
949 setNumAllowedChannels();
952 case EVENT_SUPPLICANT_DISCONNECT:
953 mRunState = RUN_STATE_STOPPED;
954 synchronized (this) {
955 updateBatteryWorkSourceLocked(null);
957 boolean died = mWifiState.get() != WIFI_STATE_DISABLED &&
958 mWifiState.get() != WIFI_STATE_DISABLING;
960 if (LOCAL_LOGD) Log.v(TAG, "Supplicant died unexpectedly");
962 if (LOCAL_LOGD) Log.v(TAG, "Connection to supplicant lost");
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();
972 resetConnections(true);
974 // When supplicant dies, kill the DHCP thread
975 mDhcpTarget.getLooper().quit();
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);
983 setDetailedState(DetailedState.DISCONNECTED);
984 setSupplicantState(SupplicantState.UNINITIALIZED);
985 mHaveIpAddress = false;
986 mObtainingIpAddress = false;
988 mWM.setWifiEnabled(false);
992 case EVENT_MAYBE_START_SCAN_POST_DISCONNECT:
993 // Only do this if we haven't gotten a new supplicant status since the timer
995 if (mNumSupplicantStateChanges == msg.arg1) {
996 scan(false); // do a passive scan
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();
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);
1013 if (LOCAL_LOGD) Log.v(TAG, "Changing supplicant state: "
1015 " ==> " + newState);
1017 int networkId = supplicantStateResult.networkId;
1020 * The SupplicantState BSSID value is valid in ASSOCIATING state only.
1021 * The NetworkState BSSID value comes upon a successful connection.
1023 if (supplicantStateResult.state == SupplicantState.ASSOCIATING) {
1024 mLastBssid = supplicantStateResult.BSSID;
1027 * If we get disconnect or inactive we need to start our
1028 * watchdog timer to start a scan
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);
1038 * Did we get to DISCONNECTED state due to an
1039 * authentication (password) failure?
1041 boolean failedToAuthenticate = false;
1042 if (newState == SupplicantState.DISCONNECTED) {
1043 failedToAuthenticate = mPasswordKeyMayBeIncorrect;
1045 mPasswordKeyMayBeIncorrect = false;
1048 * Keep track of the supplicant state and check if we should
1049 * disable the network
1051 boolean disabledNetwork = false;
1052 if (isSupplicantLooping(newState)) {
1055 "Stop WPA supplicant loop and disable network");
1057 disabledNetwork = wifiManagerDisableNetwork(networkId);
1060 if (disabledNetwork) {
1062 * Reset the loop state if we disabled the network
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;
1074 newDetailedState = DetailedState.FAILED;
1076 handleDisconnectedState(newDetailedState, true);
1078 * We should never let the supplicant stay in DORMANT state
1079 * as long as we are in connect mode and driver is started
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.
1085 * After multiple failures, the network gets disabled and the
1086 * supplicant should reach an INACTIVE state.
1089 if (mRunState == RUN_STATE_RUNNING && !mIsScanOnly) {
1090 sendMessageDelayed(reconnectMsg, RECONNECT_DELAY_MSECS);
1091 } else if (mRunState == RUN_STATE_STOPPING) {
1093 } else if (mRunState == RUN_STATE_STARTING && !mIsScanOnly) {
1096 } else if (newState == SupplicantState.DISCONNECTED) {
1097 mHaveIpAddress = false;
1098 if (isDriverStopped() || mDisconnectExpected) {
1099 handleDisconnectedState(DetailedState.DISCONNECTED, true);
1101 scheduleDisconnect();
1103 } else if (newState != SupplicantState.COMPLETED && !mDisconnectPending) {
1105 * Ignore events that don't change the connectivity state,
1106 * such as WPA rekeying operations.
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));
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);
1126 WifiManager.EXTRA_SUPPLICANT_ERROR,
1127 WifiManager.ERROR_AUTHENTICATING);
1129 mContext.sendStickyBroadcast(intent);
1133 case EVENT_NETWORK_STATE_CHANGED:
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.
1145 NetworkStateChangeResult result =
1146 (NetworkStateChangeResult) msg.obj;
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);
1154 if (LOCAL_LOGD) Log.v(TAG, "New network state is " + result.state);
1156 * If we're in scan-only mode, don't advance the state machine, and
1157 * don't report the state change to clients.
1160 if (LOCAL_LOGD) Log.v(TAG, "Dropping event in scan-only mode");
1163 if (result.state != DetailedState.SCANNING) {
1165 * Reset the scan count since there was a network state
1166 * change. This could be from supplicant trying to associate
1169 mNumScansSinceNetworkStateChange = 0;
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.
1179 if (result.state == DetailedState.DISCONNECTED) {
1180 if (mWifiInfo.getSupplicantState() != SupplicantState.DORMANT) {
1181 scheduleDisconnect();
1185 requestConnectionStatus(mWifiInfo);
1186 if (!(result.state == DetailedState.CONNECTED &&
1187 (!mHaveIpAddress || mDisconnectPending))) {
1188 setDetailedState(result.state);
1191 if (result.state == DetailedState.CONNECTED) {
1193 * Remove the 'available networks' notification when we
1194 * successfully connect to a network.
1196 setNotificationVisible(false, 0, false, 0);
1197 boolean wasDisconnectPending = mDisconnectPending;
1200 * The connection is fully configured as far as link-level
1201 * connectivity is concerned, but we may still need to obtain
1204 if (wasDisconnectPending) {
1205 DetailedState saveState = getNetworkInfo().getDetailedState();
1206 handleDisconnectedState(DetailedState.DISCONNECTED, false);
1207 setDetailedStateInternal(saveState);
1210 configureInterface();
1211 mLastBssid = result.BSSID;
1212 mLastSsid = mWifiInfo.getSSID();
1213 mLastNetworkId = result.networkId;
1214 if (mHaveIpAddress) {
1215 setDetailedState(DetailedState.CONNECTED);
1217 setDetailedState(DetailedState.OBTAINING_IPADDR);
1220 sendNetworkStateChangeBroadcast(mWifiInfo.getBSSID());
1223 case EVENT_SCAN_RESULTS_AVAILABLE:
1224 if (ActivityManagerNative.isSystemReady()) {
1225 mContext.sendBroadcast(new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
1227 sendScanResultsAvailable();
1229 * On receiving the first scan results after connecting to
1230 * the supplicant, switch scan mode over to passive.
1235 case EVENT_POLL_INTERVAL:
1236 if (mWifiInfo.getSupplicantState() != SupplicantState.UNINITIALIZED) {
1237 requestPolledInfo(mWifiInfo, true);
1242 case EVENT_DEFERRED_DISCONNECT:
1243 if (mWifiInfo.getSupplicantState() != SupplicantState.UNINITIALIZED) {
1244 handleDisconnectedState(DetailedState.DISCONNECTED, !SupplicantState.isValidState(mWifiInfo.getSupplicantState()));
1248 case EVENT_DEFERRED_RECONNECT:
1250 * mLastBssid can be null when there is a reconnect
1251 * request on the first BSSID we connect to
1253 String BSSID = (msg.obj != null) ? msg.obj.toString() : null;
1255 * If we've exceeded the maximum number of retries for reconnecting
1256 * to a given network, disable the network
1258 if (mWifiInfo.getSupplicantState() != SupplicantState.UNINITIALIZED) {
1259 if (++mReconnectCount > getMaxDhcpRetries()) {
1261 Log.d(TAG, "Failed reconnect count: " +
1262 mReconnectCount + " Disabling " + BSSID);
1264 mWM.disableNetwork(mLastNetworkId);
1270 case EVENT_INTERFACE_CONFIGURATION_SUCCEEDED:
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.
1276 if (mWifiInfo.getSupplicantState() == SupplicantState.UNINITIALIZED) {
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());
1288 msg = mTarget.obtainMessage(EVENT_CONFIGURATION_CHANGED, mNetworkInfo);
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);
1297 // We've connected successfully, so allow the notification again in the future
1298 resetNotificationTimer();
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;
1314 case EVENT_DRIVER_STATE_CHANGED:
1315 // Wi-Fi driver state changed:
1319 EventLog.writeEvent(EVENTLOG_DRIVER_STATE_CHANGED, msg.arg1);
1322 case DRIVER_STARTED:
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.
1328 setNumAllowedChannels();
1329 synchronized (this) {
1330 macaddr = WifiNative.getMacAddressCommand();
1331 if (macaddr != null) {
1332 mWifiInfo.setMacAddress(macaddr);
1334 mRunState = RUN_STATE_RUNNING;
1338 // In some situations, supplicant needs to be kickstarted to
1339 // start the background scanning
1344 case DRIVER_STOPPED:
1345 mRunState = RUN_STATE_STOPPED;
1348 Log.e(TAG, "Wifi Driver reports HUNG - reloading.");
1350 * restart the driver - toggle off and on
1352 mWM.setWifiEnabled(false);
1353 mWM.setWifiEnabled(true);
1356 synchronized (this) {
1357 updateBatteryWorkSourceLocked(null);
1361 case EVENT_PASSWORD_KEY_MAY_BE_INCORRECT:
1362 mPasswordKeyMayBeIncorrect = true;
1367 private boolean wifiManagerDisableNetwork(int networkId) {
1368 boolean disabledNetwork = false;
1369 if (0 <= networkId) {
1370 disabledNetwork = mWM.disableNetwork(networkId);
1372 if (disabledNetwork) {
1373 Log.v(TAG, "Disabled network: " + networkId);
1378 if (!disabledNetwork) {
1379 Log.e(TAG, "Failed to disable network:" +
1380 " invalid network id: " + networkId);
1383 return disabledNetwork;
1386 private void configureInterface() {
1388 mLastSignalLevel = -1;
1389 if (!mUseStaticIp) {
1390 if (!mHaveIpAddress && !mObtainingIpAddress) {
1391 mObtainingIpAddress = true;
1392 mDhcpTarget.sendEmptyMessage(EVENT_DHCP_START);
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");
1401 mHaveIpAddress = false;
1402 event = EVENT_INTERFACE_CONFIGURATION_FAILED;
1403 if (LOCAL_LOGD) Log.v(TAG, "Static IP configuration failed");
1405 sendEmptyMessage(event);
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
1416 private void handleDisconnectedState(DetailedState newState, boolean disableInterface) {
1417 if (mDisconnectPending) {
1420 mDisconnectExpected = false;
1421 resetConnections(disableInterface);
1422 setDetailedState(newState);
1423 sendNetworkStateChangeBroadcast(mLastBssid);
1424 mWifiInfo.setBSSID(null);
1427 mDisconnectPending = false;
1431 * Resets the Wi-Fi Connections by clearing any state, resetting any sockets
1432 * using the interface, stopping DHCP, and disabling the interface.
1434 public void resetConnections(boolean disableInterface) {
1435 if (LOCAL_LOGD) Log.d(TAG, "Reset connections and stopping DHCP");
1436 if (mInterfaceName == null)
1438 mHaveIpAddress = false;
1439 mObtainingIpAddress = false;
1440 mWifiInfo.setIpAddress(0);
1443 * Reset connection depends on both the interface and the IP assigned,
1444 * so it should be done before any chance of the IP being lost.
1446 NetworkUtils.resetConnections(mInterfaceName);
1449 mDhcpTarget.setCancelCallback(true);
1450 mDhcpTarget.removeMessages(EVENT_DHCP_START);
1452 if (!NetworkUtils.stopDhcp(mInterfaceName)) {
1453 Log.e(TAG, "Could not stop DHCP");
1457 * Interface is re-enabled in the supplicant
1458 * when moving out of ASSOCIATING state
1460 if(disableInterface) {
1461 if (LOCAL_LOGD) Log.d(TAG, "Disabling interface");
1462 NetworkUtils.disableInterface(mInterfaceName);
1467 * The supplicant is reporting that we are disconnected from the current
1468 * access point. Often, however, a disconnect will be followed very shortly
1469 * by a reconnect to the same access point. Therefore, we delay resetting
1470 * the connection's IP state for a bit.
1472 private void scheduleDisconnect() {
1473 mDisconnectPending = true;
1474 if (!hasMessages(EVENT_DEFERRED_DISCONNECT)) {
1475 sendEmptyMessageDelayed(EVENT_DEFERRED_DISCONNECT, DISCONNECT_DELAY_MSECS);
1479 private void cancelDisconnect() {
1480 mDisconnectPending = false;
1481 removeMessages(EVENT_DEFERRED_DISCONNECT);
1484 public DhcpInfo getDhcpInfo() {
1488 public synchronized List<ScanResult> getScanResultsList() {
1489 return mScanResults;
1492 public synchronized void setScanResultsList(List<ScanResult> scanList) {
1493 mScanResults = scanList;
1497 * Get status information for the current connection, if any.
1498 * @return a {@link WifiInfo} object containing information about the current connection
1500 public WifiInfo requestConnectionInfo() {
1501 requestConnectionStatus(mWifiInfo);
1502 requestPolledInfo(mWifiInfo, false);
1506 private void requestConnectionStatus(WifiInfo info) {
1508 String BSSID = null;
1509 String suppState = null;
1511 String reply = status();
1512 if (reply != null) {
1514 * Parse the reply from the supplicant to the status command, and update
1515 * local state accordingly. The reply is a series of lines of the form
1519 String[] lines = reply.split("\n");
1520 for (String line : lines) {
1521 String[] prop = line.split(" *= *");
1522 if (prop.length < 2)
1524 String name = prop[0];
1525 String value = prop[1];
1526 if (name.equalsIgnoreCase("id"))
1527 netId = Integer.parseInt(value);
1528 else if (name.equalsIgnoreCase("ssid"))
1530 else if (name.equalsIgnoreCase("bssid"))
1532 else if (name.equalsIgnoreCase("wpa_state"))
1536 info.setNetworkId(netId);
1538 info.setBSSID(BSSID);
1540 * We only set the supplicant state if the previous state was
1541 * UNINITIALIZED. This should only happen when we first connect to
1542 * the supplicant. Once we're connected, we should always receive
1543 * an event upon any state change, but in this case, we want to
1544 * make sure any listeners are made aware of the state change.
1546 if (mWifiInfo.getSupplicantState() == SupplicantState.UNINITIALIZED && suppState != null)
1547 setSupplicantState(suppState);
1551 * Get the dynamic information that is not reported via events.
1552 * @param info the object into which the information should be captured.
1554 private synchronized void requestPolledInfo(WifiInfo info, boolean polling)
1556 int newRssi = (polling ? getRssiApprox() : getRssi());
1557 if (newRssi != -1 && -200 < newRssi && newRssi < 256) { // screen out invalid values
1558 /* some implementations avoid negative values by adding 256
1559 * so we need to adjust for that here.
1561 if (newRssi > 0) newRssi -= 256;
1562 info.setRssi(newRssi);
1564 * Rather then sending the raw RSSI out every time it
1565 * changes, we precalculate the signal level that would
1566 * be displayed in the status bar, and only send the
1567 * broadcast if that much more coarse-grained number
1568 * changes. This cuts down greatly on the number of
1569 * broadcasts, at the cost of not informing others
1570 * interested in RSSI of all the changes in signal
1573 // TODO: The second arg to the call below needs to be a symbol somewhere, but
1574 // it's actually the size of an array of icons that's private
1575 // to StatusBar Policy.
1576 int newSignalLevel = WifiManager.calculateSignalLevel(newRssi, 4);
1577 if (newSignalLevel != mLastSignalLevel) {
1578 sendRssiChangeBroadcast(newRssi);
1580 mLastSignalLevel = newSignalLevel;
1584 int newLinkSpeed = getLinkSpeed();
1585 if (newLinkSpeed != -1) {
1586 info.setLinkSpeed(newLinkSpeed);
1590 private void sendRssiChangeBroadcast(final int newRssi) {
1591 if (ActivityManagerNative.isSystemReady()) {
1592 Intent intent = new Intent(WifiManager.RSSI_CHANGED_ACTION);
1593 intent.putExtra(WifiManager.EXTRA_NEW_RSSI, newRssi);
1594 mContext.sendBroadcast(intent);
1598 private void sendNetworkStateChangeBroadcast(String bssid) {
1599 Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
1600 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
1601 | Intent.FLAG_RECEIVER_REPLACE_PENDING);
1602 intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, mNetworkInfo);
1604 intent.putExtra(WifiManager.EXTRA_BSSID, bssid);
1605 mContext.sendStickyBroadcast(intent);
1609 * Disable Wi-Fi connectivity by stopping the driver.
1611 public boolean teardown() {
1612 if (!mTornDownByConnMgr) {
1613 if (disconnectAndStop()) {
1614 setTornDownByConnMgr(true);
1625 * Reenable Wi-Fi connectivity by restarting the driver.
1627 public boolean reconnect() {
1628 if (mTornDownByConnMgr) {
1630 setTornDownByConnMgr(false);
1641 * We want to stop the driver, but if we're connected to a network,
1642 * we first want to disconnect, so that the supplicant is always in
1643 * a known state (DISCONNECTED) when the driver is stopped.
1644 * @return {@code true} if the operation succeeds, which means that the
1645 * disconnect or stop command was initiated.
1647 public synchronized boolean disconnectAndStop() {
1648 boolean ret = true;;
1649 if (mRunState != RUN_STATE_STOPPING && mRunState != RUN_STATE_STOPPED) {
1650 // Take down any open network notifications
1651 setNotificationVisible(false, 0, false, 0);
1653 if (mWifiInfo.getSupplicantState() == SupplicantState.DORMANT) {
1658 mRunState = RUN_STATE_STOPPING;
1663 public synchronized boolean restart() {
1664 if (isDriverStopped()) {
1665 mRunState = RUN_STATE_STARTING;
1666 resetConnections(true);
1667 return startDriver();
1672 public int getWifiState() {
1673 return mWifiState.get();
1676 public void setWifiState(int wifiState) {
1677 mWifiState.set(wifiState);
1680 public boolean isAnyNetworkDisabled() {
1681 return mIsAnyNetworkDisabled.get();
1685 * The WifiNative interface functions are listed below.
1686 * The only native call that is not synchronized on
1687 * WifiStateTracker is waitForEvent() which waits on a
1688 * seperate monitor channel.
1690 * All supplicant commands need the wifi to be in an
1691 * enabled state. This can be done by checking the
1692 * mWifiState to be WIFI_STATE_ENABLED.
1694 * All commands that can cause commands to driver
1695 * initiated need the driver state to be started.
1696 * This is done by checking isDriverStopped() to
1701 * Load the driver and firmware
1703 * @return {@code true} if the operation succeeds, {@code false} otherwise
1705 public synchronized boolean loadDriver() {
1706 return WifiNative.loadDriver();
1710 * Unload the driver and firmware
1712 * @return {@code true} if the operation succeeds, {@code false} otherwise
1714 public synchronized boolean unloadDriver() {
1715 return WifiNative.unloadDriver();
1719 * Check the supplicant config and
1720 * start the supplicant daemon
1722 * @return {@code true} if the operation succeeds, {@code false} otherwise
1724 public synchronized boolean startSupplicant() {
1725 return WifiNative.startSupplicant();
1729 * Stop the supplicant daemon
1731 * @return {@code true} if the operation succeeds, {@code false} otherwise
1733 public synchronized boolean stopSupplicant() {
1734 return WifiNative.stopSupplicant();
1738 * Establishes two channels - control channel for commands
1739 * and monitor channel for notifying WifiMonitor
1741 * @return {@code true} if the operation succeeds, {@code false} otherwise
1743 public synchronized boolean connectToSupplicant() {
1744 return WifiNative.connectToSupplicant();
1748 * Close the control/monitor channels to supplicant
1750 public synchronized void closeSupplicantConnection() {
1751 WifiNative.closeSupplicantConnection();
1755 * Check if the supplicant is alive
1757 * @return {@code true} if the operation succeeds, {@code false} otherwise
1759 public synchronized boolean ping() {
1760 if (mWifiState.get() != WIFI_STATE_ENABLED) {
1763 return WifiNative.pingCommand();
1767 * initiate an active or passive scan
1769 * @param forceActive true if it is a active scan
1770 * @return {@code true} if the operation succeeds, {@code false} otherwise
1772 public synchronized boolean scan(boolean forceActive) {
1773 if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
1776 return WifiNative.scanCommand(forceActive);
1780 * Specifies whether the supplicant or driver
1781 * take care of initiating scan and doing AP selection
1784 * SUPPL_SCAN_HANDLING_NORMAL
1785 * SUPPL_SCAN_HANDLING_LIST_ONLY
1786 * @return {@code true} if the operation succeeds, {@code false} otherwise
1788 public synchronized boolean setScanResultHandling(int mode) {
1789 if (mWifiState.get() != WIFI_STATE_ENABLED) {
1792 return WifiNative.setScanResultHandlingCommand(mode);
1796 * Fetch the scan results from the supplicant
1798 * @return example result string
1799 * 00:bb:cc:dd:cc:ee 2427 166 [WPA-EAP-TKIP][WPA2-EAP-CCMP] Net1
1800 * 00:bb:cc:dd:cc:ff 2412 165 [WPA-EAP-TKIP][WPA2-EAP-CCMP] Net2
1802 public synchronized String scanResults() {
1803 if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
1806 return WifiNative.scanResultsCommand();
1810 * Set the scan mode - active or passive
1812 * @return {@code true} if the operation succeeds, {@code false} otherwise
1814 public synchronized boolean setScanMode(boolean isScanModeActive) {
1815 if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
1818 if (mIsScanModeActive != isScanModeActive) {
1819 return WifiNative.setScanModeCommand(mIsScanModeActive = isScanModeActive);
1825 * Disconnect from Access Point
1827 * @return {@code true} if the operation succeeds, {@code false} otherwise
1829 public synchronized boolean disconnect() {
1830 if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
1833 return WifiNative.disconnectCommand();
1837 * Initiate a reconnection to AP
1839 * @return {@code true} if the operation succeeds, {@code false} otherwise
1841 public synchronized boolean reconnectCommand() {
1842 if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
1845 return WifiNative.reconnectCommand();
1851 * @return network id of the new network
1853 public synchronized int addNetwork() {
1854 if (mWifiState.get() != WIFI_STATE_ENABLED) {
1857 return WifiNative.addNetworkCommand();
1863 * @param networkId id of the network to be removed
1864 * @return {@code true} if the operation succeeds, {@code false} otherwise
1866 public synchronized boolean removeNetwork(int networkId) {
1867 if (mWifiState.get() != WIFI_STATE_ENABLED) {
1870 return mDisconnectExpected = WifiNative.removeNetworkCommand(networkId);
1876 * @param netId network id of the network
1877 * @param disableOthers true, if all other networks have to be disabled
1878 * @return {@code true} if the operation succeeds, {@code false} otherwise
1880 public synchronized boolean enableNetwork(int netId, boolean disableOthers) {
1881 if (mWifiState.get() != WIFI_STATE_ENABLED) {
1884 if (disableOthers) mIsAnyNetworkDisabled.set(true);
1885 return WifiNative.enableNetworkCommand(netId, disableOthers);
1889 * Enable all networks
1891 * @param networks list of configured networks
1893 public synchronized void enableAllNetworks(List<WifiConfiguration> networks) {
1894 if (mWifiState.get() != WIFI_STATE_ENABLED) {
1897 mIsAnyNetworkDisabled.set(false);
1898 for (WifiConfiguration config : networks) {
1899 if (config.status == WifiConfiguration.Status.DISABLED) {
1900 WifiNative.enableNetworkCommand(config.networkId, false);
1908 * @param netId network id of the network
1909 * @return {@code true} if the operation succeeds, {@code false} otherwise
1911 public synchronized boolean disableNetwork(int netId) {
1912 if (mWifiState.get() != WIFI_STATE_ENABLED) {
1915 mIsAnyNetworkDisabled.set(true);
1916 return WifiNative.disableNetworkCommand(netId);
1920 * Initiate a re-association in supplicant
1922 * @return {@code true} if the operation succeeds, {@code false} otherwise
1924 public synchronized boolean reassociate() {
1925 if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
1928 return WifiNative.reassociateCommand();
1932 * Blacklist a BSSID. This will avoid the AP if there are
1933 * alternate APs to connect
1935 * @param bssid BSSID of the network
1936 * @return {@code true} if the operation succeeds, {@code false} otherwise
1938 public synchronized boolean addToBlacklist(String bssid) {
1939 if (mWifiState.get() != WIFI_STATE_ENABLED) {
1942 return WifiNative.addToBlacklistCommand(bssid);
1946 * Clear the blacklist list
1948 * @return {@code true} if the operation succeeds, {@code false} otherwise
1950 public synchronized boolean clearBlacklist() {
1951 if (mWifiState.get() != WIFI_STATE_ENABLED) {
1954 return WifiNative.clearBlacklistCommand();
1958 * List all configured networks
1960 * @return list of networks or null on failure
1962 public synchronized String listNetworks() {
1963 if (mWifiState.get() != WIFI_STATE_ENABLED) {
1966 return WifiNative.listNetworksCommand();
1970 * Get network setting by name
1972 * @param netId network id of the network
1973 * @param name network variable key
1974 * @return value corresponding to key
1976 public synchronized String getNetworkVariable(int netId, String name) {
1977 if (mWifiState.get() != WIFI_STATE_ENABLED) {
1980 return WifiNative.getNetworkVariableCommand(netId, name);
1984 * Set network setting by name
1986 * @param netId network id of the network
1987 * @param name network variable key
1988 * @param value network variable value
1989 * @return {@code true} if the operation succeeds, {@code false} otherwise
1991 public synchronized boolean setNetworkVariable(int netId, String name, String value) {
1992 if (mWifiState.get() != WIFI_STATE_ENABLED) {
1995 return WifiNative.setNetworkVariableCommand(netId, name, value);
1999 * Get detailed status of the connection
2001 * @return Example status result
2002 * bssid=aa:bb:cc:dd:ee:ff
2005 * pairwise_cipher=NONE
2008 * wpa_state=COMPLETED
2009 * ip_address=X.X.X.X
2011 public synchronized String status() {
2012 if (mWifiState.get() != WIFI_STATE_ENABLED) {
2015 return WifiNative.statusCommand();
2019 * Get RSSI to currently connected network
2021 * @return RSSI value, -1 on failure
2023 public synchronized int getRssi() {
2024 if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
2027 return WifiNative.getRssiApproxCommand();
2031 * Get approx RSSI to currently connected network
2033 * @return RSSI value, -1 on failure
2035 public synchronized int getRssiApprox() {
2036 if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
2039 return WifiNative.getRssiApproxCommand();
2043 * Get link speed to currently connected network
2045 * @return link speed, -1 on failure
2047 public synchronized int getLinkSpeed() {
2048 if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
2051 return WifiNative.getLinkSpeedCommand();
2057 * @return {@code true} if the operation succeeds, {@code false} otherwise
2059 public synchronized boolean startDriver() {
2060 if (mWifiState.get() != WIFI_STATE_ENABLED) {
2063 return WifiNative.startDriverCommand();
2069 * @return {@code true} if the operation succeeds, {@code false} otherwise
2071 public synchronized boolean stopDriver() {
2072 /* Driver stop should not happen only when supplicant event
2073 * DRIVER_STOPPED has already been handled */
2074 if (mWifiState.get() != WIFI_STATE_ENABLED || mRunState == RUN_STATE_STOPPED) {
2077 return WifiNative.stopDriverCommand();
2081 * Start packet filtering
2083 * @return {@code true} if the operation succeeds, {@code false} otherwise
2085 public synchronized boolean startPacketFiltering() {
2086 if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
2089 return WifiNative.startPacketFiltering();
2093 * Stop packet filtering
2095 * @return {@code true} if the operation succeeds, {@code false} otherwise
2097 public synchronized boolean stopPacketFiltering() {
2098 if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
2101 return WifiNative.stopPacketFiltering();
2106 * @return power mode
2108 public synchronized int getPowerMode() {
2109 if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) {
2112 return WifiNative.getPowerModeCommand();
2118 * DRIVER_POWER_MODE_AUTO
2119 * DRIVER_POWER_MODE_ACTIVE
2121 * Uses reference counting to keep power mode active
2122 * as long as one entity wants power mode to be active.
2124 * For example, WifiLock high perf mode can keep power mode active
2125 * or a DHCP session can keep it active. As long as one entity wants
2126 * it enabled, it should stay that way
2129 private synchronized void setPowerMode(int mode) {
2131 /* It is good to plumb power mode change
2132 * even if ref count indicates already done
2133 * since we could have a case of previous failure.
2136 case DRIVER_POWER_MODE_ACTIVE:
2137 mPowerModeRefCount++;
2139 case DRIVER_POWER_MODE_AUTO:
2140 mPowerModeRefCount--;
2141 if (mPowerModeRefCount > 0) {
2144 /* Keep refcount from becoming negative */
2145 mPowerModeRefCount = 0;
2150 if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
2154 WifiNative.setPowerModeCommand(mode);
2158 * Set the number of allowed radio frequency channels from the system
2159 * setting value, if any.
2160 * @return {@code true} if the operation succeeds, {@code false} otherwise, e.g.,
2161 * the number of channels is invalid.
2163 public synchronized boolean setNumAllowedChannels() {
2164 if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
2168 return setNumAllowedChannels(
2169 Settings.Secure.getInt(mContext.getContentResolver(),
2170 Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS));
2171 } catch (Settings.SettingNotFoundException e) {
2172 if (mNumAllowedChannels != 0) {
2173 WifiNative.setNumAllowedChannelsCommand(mNumAllowedChannels);
2175 // otherwise, use the driver default
2181 * Set the number of radio frequency channels that are allowed to be used
2182 * in the current regulatory domain.
2183 * @param numChannels the number of allowed channels. Must be greater than 0
2184 * and less than or equal to 16.
2185 * @return {@code true} if the operation succeeds, {@code false} otherwise, e.g.,
2186 * {@code numChannels} is outside the valid range.
2188 public synchronized boolean setNumAllowedChannels(int numChannels) {
2189 if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
2192 mNumAllowedChannels = numChannels;
2193 return WifiNative.setNumAllowedChannelsCommand(numChannels);
2197 * Get number of allowed channels
2199 * @return channel count, -1 on failure
2201 public synchronized int getNumAllowedChannels() {
2202 if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
2205 return WifiNative.getNumAllowedChannelsCommand();
2209 * Set bluetooth coex mode:
2212 * BLUETOOTH_COEXISTENCE_MODE_ENABLED
2213 * BLUETOOTH_COEXISTENCE_MODE_DISABLED
2214 * BLUETOOTH_COEXISTENCE_MODE_SENSE
2215 * @return {@code true} if the operation succeeds, {@code false} otherwise
2217 public synchronized boolean setBluetoothCoexistenceMode(int mode) {
2218 if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
2221 return WifiNative.setBluetoothCoexistenceModeCommand(mode);
2225 * Enable or disable Bluetooth coexistence scan mode. When this mode is on,
2226 * some of the low-level scan parameters used by the driver are changed to
2227 * reduce interference with A2DP streaming.
2229 * @param isBluetoothPlaying whether to enable or disable this mode
2231 public synchronized void setBluetoothScanMode(boolean isBluetoothPlaying) {
2232 if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
2235 WifiNative.setBluetoothCoexistenceScanModeCommand(isBluetoothPlaying);
2239 * Save configuration on supplicant
2241 * @return {@code true} if the operation succeeds, {@code false} otherwise
2243 public synchronized boolean saveConfig() {
2244 if (mWifiState.get() != WIFI_STATE_ENABLED) {
2247 return WifiNative.saveConfigCommand();
2251 * Reload the configuration from file
2253 * @return {@code true} if the operation succeeds, {@code false} otherwise
2255 public synchronized boolean reloadConfig() {
2256 if (mWifiState.get() != WIFI_STATE_ENABLED) {
2259 return WifiNative.reloadConfigCommand();
2262 public boolean setRadio(boolean turnOn) {
2263 return mWM.setWifiEnabled(turnOn);
2268 * There are currently no Wi-Fi-specific features supported.
2269 * @param feature the name of the feature
2270 * @return {@code -1} indicating failure, always
2272 public int startUsingNetworkFeature(String feature, int callingPid, int callingUid) {
2278 * There are currently no Wi-Fi-specific features supported.
2279 * @param feature the name of the feature
2280 * @return {@code -1} indicating failure, always
2282 public int stopUsingNetworkFeature(String feature, int callingPid, int callingUid) {
2287 public void interpretScanResultsAvailable() {
2289 // If we shouldn't place a notification on available networks, then
2290 // don't bother doing any of the following
2291 if (!mNotificationEnabled) return;
2293 NetworkInfo networkInfo = getNetworkInfo();
2295 State state = networkInfo.getState();
2296 if ((state == NetworkInfo.State.DISCONNECTED)
2297 || (state == NetworkInfo.State.UNKNOWN)) {
2299 // Look for an open network
2300 List<ScanResult> scanResults = getScanResultsList();
2301 if (scanResults != null) {
2302 int numOpenNetworks = 0;
2303 for (int i = scanResults.size() - 1; i >= 0; i--) {
2304 ScanResult scanResult = scanResults.get(i);
2306 if (TextUtils.isEmpty(scanResult.capabilities)) {
2311 if (numOpenNetworks > 0) {
2312 if (++mNumScansSinceNetworkStateChange >= NUM_SCANS_BEFORE_ACTUALLY_SCANNING) {
2314 * We've scanned continuously at least
2315 * NUM_SCANS_BEFORE_NOTIFICATION times. The user
2316 * probably does not have a remembered network in range,
2317 * since otherwise supplicant would have tried to
2318 * associate and thus resetting this counter.
2320 setNotificationVisible(true, numOpenNetworks, false, 0);
2327 // No open networks in range, remove the notification
2328 setNotificationVisible(false, 0, false, 0);
2332 * Display or don't display a notification that there are open Wi-Fi networks.
2333 * @param visible {@code true} if notification should be visible, {@code false} otherwise
2334 * @param numNetworks the number networks seen
2335 * @param force {@code true} to force notification to be shown/not-shown,
2336 * even if it is already shown/not-shown.
2337 * @param delay time in milliseconds after which the notification should be made
2338 * visible or invisible.
2340 public void setNotificationVisible(boolean visible, int numNetworks, boolean force, int delay) {
2342 // Since we use auto cancel on the notification, when the
2343 // mNetworksAvailableNotificationShown is true, the notification may
2344 // have actually been canceled. However, when it is false we know
2345 // for sure that it is not being shown (it will not be shown any other
2348 // If it should be hidden and it is already hidden, then noop
2349 if (!visible && !mNotificationShown && !force) {
2356 // Not enough time has passed to show the notification again
2357 if (System.currentTimeMillis() < mNotificationRepeatTime) {
2361 if (mNotification == null) {
2362 // Cache the Notification mainly so we can remove the
2363 // EVENT_NOTIFICATION_CHANGED message with this Notification from
2365 mNotification = new Notification();
2366 mNotification.when = 0;
2367 mNotification.icon = ICON_NETWORKS_AVAILABLE;
2368 mNotification.flags = Notification.FLAG_AUTO_CANCEL;
2369 mNotification.contentIntent = PendingIntent.getActivity(mContext, 0,
2370 new Intent(WifiManager.ACTION_PICK_WIFI_NETWORK), 0);
2373 CharSequence title = mContext.getResources().getQuantityText(
2374 com.android.internal.R.plurals.wifi_available, numNetworks);
2375 CharSequence details = mContext.getResources().getQuantityText(
2376 com.android.internal.R.plurals.wifi_available_detailed, numNetworks);
2377 mNotification.tickerText = title;
2378 mNotification.setLatestEventInfo(mContext, title, details, mNotification.contentIntent);
2380 mNotificationRepeatTime = System.currentTimeMillis() + NOTIFICATION_REPEAT_DELAY_MS;
2382 message = mTarget.obtainMessage(EVENT_NOTIFICATION_CHANGED, 1,
2383 ICON_NETWORKS_AVAILABLE, mNotification);
2387 // Remove any pending messages to show the notification
2388 mTarget.removeMessages(EVENT_NOTIFICATION_CHANGED, mNotification);
2390 message = mTarget.obtainMessage(EVENT_NOTIFICATION_CHANGED, 0, ICON_NETWORKS_AVAILABLE);
2393 mTarget.sendMessageDelayed(message, delay);
2395 mNotificationShown = visible;
2399 * Clears variables related to tracking whether a notification has been
2402 * After calling this method, the timer that prevents notifications from
2403 * being shown too often will be cleared.
2405 private void resetNotificationTimer() {
2406 mNotificationRepeatTime = 0;
2407 mNumScansSinceNetworkStateChange = 0;
2411 public String toString() {
2412 StringBuffer sb = new StringBuffer();
2413 sb.append("interface ").append(mInterfaceName);
2414 sb.append(" runState=");
2415 if (mRunState >= 1 && mRunState <= mRunStateNames.length) {
2416 sb.append(mRunStateNames[mRunState-1]);
2418 sb.append(mRunState);
2420 sb.append(LS).append(mWifiInfo).append(LS);
2421 sb.append(mDhcpInfo).append(LS);
2422 sb.append("haveIpAddress=").append(mHaveIpAddress).
2423 append(", obtainingIpAddress=").append(mObtainingIpAddress).
2424 append(", scanModeActive=").append(mIsScanModeActive).append(LS).
2425 append("lastSignalLevel=").append(mLastSignalLevel).
2426 append(", explicitlyDisabled=").append(mTornDownByConnMgr);
2427 return sb.toString();
2430 private class DhcpHandler extends Handler {
2432 private Handler mWifiStateTrackerHandler;
2435 * Whether to skip the DHCP result callback to the target. For example,
2436 * this could be set if the network we were requesting an IP for has
2437 * since been disconnected.
2439 * Note: There is still a chance where the client's intended DHCP
2440 * request not being canceled. For example, we are request for IP on
2441 * A, and he queues request for IP on B, and then cancels the request on
2442 * B while we're still requesting from A.
2444 private boolean mCancelCallback;
2447 * Instance of the bluetooth headset helper. This needs to be created
2448 * early because there is a delay before it actually 'connects', as
2449 * noted by its javadoc. If we check before it is connected, it will be
2450 * in an error state and we will not disable coexistence.
2452 private BluetoothHeadset mBluetoothHeadset;
2454 public DhcpHandler(Looper looper, Handler target) {
2456 mWifiStateTrackerHandler = target;
2458 mBluetoothHeadset = new BluetoothHeadset(mContext, null);
2461 public void handleMessage(Message msg) {
2465 case EVENT_DHCP_START:
2466 case EVENT_DHCP_RENEW:
2467 boolean modifiedBluetoothCoexistenceMode = false;
2468 int powerMode = DRIVER_POWER_MODE_AUTO;
2470 if (shouldDisableCoexistenceMode()) {
2472 * There are problems setting the Wi-Fi driver's power
2473 * mode to active when bluetooth coexistence mode is
2476 * We set Wi-Fi to active mode when
2477 * obtaining an IP address because we've found
2478 * compatibility issues with some routers with low power
2481 * In order for this active power mode to properly be set,
2482 * we disable coexistence mode until we're done with
2483 * obtaining an IP address. One exception is if we
2484 * are currently connected to a headset, since disabling
2485 * coexistence would interrupt that connection.
2487 modifiedBluetoothCoexistenceMode = true;
2489 // Disable the coexistence mode
2490 setBluetoothCoexistenceMode(
2491 WifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED);
2494 powerMode = getPowerMode();
2495 if (powerMode < 0) {
2496 // Handle the case where supplicant driver does not support
2497 // getPowerModeCommand.
2498 powerMode = DRIVER_POWER_MODE_AUTO;
2500 if (powerMode != DRIVER_POWER_MODE_ACTIVE) {
2501 setPowerMode(DRIVER_POWER_MODE_ACTIVE);
2504 synchronized (this) {
2505 // A new request is being made, so assume we will callback
2506 mCancelCallback = false;
2509 if (msg.what == EVENT_DHCP_START) {
2510 Log.d(TAG, "DHCP request started");
2511 if (NetworkUtils.runDhcp(mInterfaceName, mDhcpInfo)) {
2512 event = EVENT_INTERFACE_CONFIGURATION_SUCCEEDED;
2513 Log.d(TAG, "DHCP succeeded with lease: " + mDhcpInfo.leaseDuration);
2514 setDhcpRenewalAlarm(mDhcpInfo.leaseDuration);
2516 event = EVENT_INTERFACE_CONFIGURATION_FAILED;
2517 Log.e(TAG, "DHCP request failed: " + NetworkUtils.getDhcpError());
2519 synchronized (this) {
2520 if (!mCancelCallback) {
2521 mWifiStateTrackerHandler.sendEmptyMessage(event);
2525 } else if (msg.what == EVENT_DHCP_RENEW) {
2526 Log.d(TAG, "DHCP renewal started");
2527 int oIp = mDhcpInfo.ipAddress;
2528 int oGw = mDhcpInfo.gateway;
2529 int oMsk = mDhcpInfo.netmask;
2530 int oDns1 = mDhcpInfo.dns1;
2531 int oDns2 = mDhcpInfo.dns2;
2533 if (NetworkUtils.runDhcpRenew(mInterfaceName, mDhcpInfo)) {
2534 Log.d(TAG, "DHCP renewal with lease: " + mDhcpInfo.leaseDuration);
2537 (oIp != mDhcpInfo.ipAddress ||
2538 oGw != mDhcpInfo.gateway ||
2539 oMsk != mDhcpInfo.netmask ||
2540 oDns1 != mDhcpInfo.dns1 ||
2541 oDns2 != mDhcpInfo.dns2);
2544 Log.d(TAG, "IP config change on renewal");
2545 mWifiInfo.setIpAddress(mDhcpInfo.ipAddress);
2546 NetworkUtils.resetConnections(mInterfaceName);
2547 msg = mTarget.obtainMessage(EVENT_CONFIGURATION_CHANGED,
2552 setDhcpRenewalAlarm(mDhcpInfo.leaseDuration);
2554 event = EVENT_INTERFACE_CONFIGURATION_FAILED;
2555 Log.d(TAG, "DHCP renewal failed: " + NetworkUtils.getDhcpError());
2557 synchronized (this) {
2558 if (!mCancelCallback) {
2559 mWifiStateTrackerHandler.sendEmptyMessage(event);
2565 if (powerMode != DRIVER_POWER_MODE_ACTIVE) {
2566 setPowerMode(powerMode);
2569 if (modifiedBluetoothCoexistenceMode) {
2570 // Set the coexistence mode back to its default value
2571 setBluetoothCoexistenceMode(
2572 WifiNative.BLUETOOTH_COEXISTENCE_MODE_SENSE);
2579 public synchronized void setCancelCallback(boolean cancelCallback) {
2580 mCancelCallback = cancelCallback;
2581 if (cancelCallback) {
2582 mAlarmManager.cancel(mDhcpRenewalIntent);
2587 * Whether to disable coexistence mode while obtaining IP address. This
2588 * logic will return true only if the current bluetooth
2589 * headset/handsfree state is disconnected. This means if it is in an
2590 * error state, we will NOT disable coexistence mode to err on the side
2593 * @return Whether to disable coexistence mode.
2595 private boolean shouldDisableCoexistenceMode() {
2596 int state = mBluetoothHeadset.getState(mBluetoothHeadset.getCurrentHeadset());
2597 return state == BluetoothHeadset.STATE_DISCONNECTED;
2600 private void setDhcpRenewalAlarm(long leaseDuration) {
2601 //Do it a bit earlier than half the lease duration time
2602 //to beat the native DHCP client and avoid extra packets
2603 //48% for one hour lease time = 29 minutes
2604 if (leaseDuration < MIN_RENEWAL_TIME_SECS) {
2605 leaseDuration = MIN_RENEWAL_TIME_SECS;
2607 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
2608 SystemClock.elapsedRealtime() +
2609 leaseDuration * 480, //in milliseconds
2610 mDhcpRenewalIntent);
2615 private void checkUseStaticIp() {
2616 mUseStaticIp = false;
2617 final ContentResolver cr = mContext.getContentResolver();
2619 if (Settings.System.getInt(cr, Settings.System.WIFI_USE_STATIC_IP) == 0) {
2622 } catch (Settings.SettingNotFoundException e) {
2627 String addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_IP);
2629 mDhcpInfo.ipAddress = stringToIpAddr(addr);
2633 addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_GATEWAY);
2635 mDhcpInfo.gateway = stringToIpAddr(addr);
2639 addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_NETMASK);
2641 mDhcpInfo.netmask = stringToIpAddr(addr);
2645 addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_DNS1);
2647 mDhcpInfo.dns1 = stringToIpAddr(addr);
2651 addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_DNS2);
2653 mDhcpInfo.dns2 = stringToIpAddr(addr);
2657 } catch (UnknownHostException e) {
2660 mUseStaticIp = true;
2663 private static int stringToIpAddr(String addrString) throws UnknownHostException {
2665 String[] parts = addrString.split("\\.");
2666 if (parts.length != 4) {
2667 throw new UnknownHostException(addrString);
2670 int a = Integer.parseInt(parts[0]) ;
2671 int b = Integer.parseInt(parts[1]) << 8;
2672 int c = Integer.parseInt(parts[2]) << 16;
2673 int d = Integer.parseInt(parts[3]) << 24;
2675 return a | b | c | d;
2676 } catch (NumberFormatException ex) {
2677 throw new UnknownHostException(addrString);
2681 private int getMaxDhcpRetries() {
2682 return Settings.Secure.getInt(mContext.getContentResolver(),
2683 Settings.Secure.WIFI_MAX_DHCP_RETRY_COUNT,
2684 DEFAULT_MAX_DHCP_RETRIES);
2687 private class SettingsObserver extends ContentObserver {
2688 public SettingsObserver(Handler handler) {
2690 ContentResolver cr = mContext.getContentResolver();
2691 cr.registerContentObserver(Settings.System.getUriFor(
2692 Settings.System.WIFI_USE_STATIC_IP), false, this);
2693 cr.registerContentObserver(Settings.System.getUriFor(
2694 Settings.System.WIFI_STATIC_IP), false, this);
2695 cr.registerContentObserver(Settings.System.getUriFor(
2696 Settings.System.WIFI_STATIC_GATEWAY), false, this);
2697 cr.registerContentObserver(Settings.System.getUriFor(
2698 Settings.System.WIFI_STATIC_NETMASK), false, this);
2699 cr.registerContentObserver(Settings.System.getUriFor(
2700 Settings.System.WIFI_STATIC_DNS1), false, this);
2701 cr.registerContentObserver(Settings.System.getUriFor(
2702 Settings.System.WIFI_STATIC_DNS2), false, this);
2705 public void onChange(boolean selfChange) {
2706 super.onChange(selfChange);
2708 boolean wasStaticIp = mUseStaticIp;
2709 int oIp, oGw, oMsk, oDns1, oDns2;
2710 oIp = oGw = oMsk = oDns1 = oDns2 = 0;
2712 oIp = mDhcpInfo.ipAddress;
2713 oGw = mDhcpInfo.gateway;
2714 oMsk = mDhcpInfo.netmask;
2715 oDns1 = mDhcpInfo.dns1;
2716 oDns2 = mDhcpInfo.dns2;
2720 if (mWifiInfo.getSupplicantState() == SupplicantState.UNINITIALIZED) {
2725 (wasStaticIp != mUseStaticIp) ||
2727 oIp != mDhcpInfo.ipAddress ||
2728 oGw != mDhcpInfo.gateway ||
2729 oMsk != mDhcpInfo.netmask ||
2730 oDns1 != mDhcpInfo.dns1 ||
2731 oDns2 != mDhcpInfo.dns2));
2734 resetConnections(true);
2735 configureInterface();
2737 Message msg = mTarget.obtainMessage(EVENT_CONFIGURATION_CHANGED, mNetworkInfo);
2744 private class NotificationEnabledSettingObserver extends ContentObserver {
2746 public NotificationEnabledSettingObserver(Handler handler) {
2750 public void register() {
2751 ContentResolver cr = mContext.getContentResolver();
2752 cr.registerContentObserver(Settings.Secure.getUriFor(
2753 Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON), true, this);
2754 mNotificationEnabled = getValue();
2758 public void onChange(boolean selfChange) {
2759 super.onChange(selfChange);
2761 mNotificationEnabled = getValue();
2762 if (!mNotificationEnabled) {
2763 // Remove any notification that may be showing
2764 setNotificationVisible(false, 0, true, 0);
2767 resetNotificationTimer();
2770 private boolean getValue() {
2771 return Settings.Secure.getInt(mContext.getContentResolver(),
2772 Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 1) == 1;