2 * Copyright (C) 2010 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;
27 * Deprecate WIFI_STATE_UNKNOWN
29 import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED;
30 import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLING;
31 import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
32 import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLING;
33 import static android.net.wifi.WifiManager.WIFI_AP_STATE_FAILED;
35 import android.app.AlarmManager;
36 import android.app.PendingIntent;
37 import android.app.backup.IBackupManager;
38 import android.bluetooth.BluetoothAdapter;
39 import android.content.BroadcastReceiver;
40 import android.content.Context;
41 import android.content.Intent;
42 import android.content.IntentFilter;
43 import android.content.pm.PackageManager;
44 import android.database.ContentObserver;
45 import android.net.ConnectivityManager;
46 import android.net.DhcpResults;
47 import android.net.DhcpStateMachine;
48 import android.net.InterfaceConfiguration;
49 import android.net.LinkAddress;
50 import android.net.LinkProperties;
51 import android.net.NetworkInfo;
52 import android.net.NetworkInfo.DetailedState;
53 import android.net.NetworkUtils;
54 import android.net.RouteInfo;
55 import android.net.wifi.WpsResult.Status;
56 import android.net.wifi.p2p.WifiP2pManager;
57 import android.net.wifi.p2p.WifiP2pService;
58 import android.os.BatteryStats;
59 import android.os.Binder;
60 import android.os.Bundle;
61 import android.os.IBinder;
62 import android.os.INetworkManagementService;
63 import android.os.Message;
64 import android.os.Messenger;
65 import android.os.PowerManager;
66 import android.os.Process;
67 import android.os.RemoteException;
68 import android.os.ServiceManager;
69 import android.os.SystemClock;
70 import android.os.SystemProperties;
71 import android.os.UserHandle;
72 import android.os.WorkSource;
73 import android.provider.Settings;
74 import android.util.Log;
75 import android.util.LruCache;
76 import android.text.TextUtils;
78 import com.android.internal.R;
79 import com.android.internal.app.IBatteryStats;
80 import com.android.internal.util.AsyncChannel;
81 import com.android.internal.util.Protocol;
82 import com.android.internal.util.State;
83 import com.android.internal.util.StateMachine;
85 import com.android.server.net.BaseNetworkObserver;
87 import java.io.FileDescriptor;
88 import java.io.PrintWriter;
89 import java.net.InetAddress;
90 import java.net.Inet6Address;
91 import java.util.ArrayList;
92 import java.util.List;
93 import java.util.Locale;
94 import java.util.concurrent.atomic.AtomicInteger;
95 import java.util.concurrent.atomic.AtomicBoolean;
96 import java.util.Iterator;
97 import java.util.regex.Pattern;
100 * Track the state of Wifi connectivity. All event handling is done here,
101 * and all changes in connectivity state are initiated here.
103 * Wi-Fi now supports three modes of operation: Client, SoftAp and p2p
104 * In the current implementation, we support concurrent wifi p2p and wifi operation.
105 * The WifiStateMachine handles SoftAp and Client operations while WifiP2pService
106 * handles p2p operation.
110 public class WifiStateMachine extends StateMachine {
112 private static final String NETWORKTYPE = "WIFI";
113 private static final boolean DBG = false;
115 private WifiMonitor mWifiMonitor;
116 private WifiNative mWifiNative;
117 private WifiConfigStore mWifiConfigStore;
118 private INetworkManagementService mNwService;
119 private ConnectivityManager mCm;
121 private final boolean mP2pSupported;
122 private final AtomicBoolean mP2pConnected = new AtomicBoolean(false);
123 private boolean mTemporarilyDisconnectWifi = false;
124 private final String mPrimaryDeviceType;
126 /* Scan results handling */
127 private List<ScanResult> mScanResults = new ArrayList<ScanResult>();
128 private static final Pattern scanResultPattern = Pattern.compile("\t+");
129 private static final int SCAN_RESULT_CACHE_SIZE = 80;
130 private final LruCache<String, ScanResult> mScanResultCache;
132 /* Batch scan results */
133 private final List<BatchedScanResult> mBatchedScanResults =
134 new ArrayList<BatchedScanResult>();
135 private int mBatchedScanOwnerUid = UNKNOWN_SCAN_SOURCE;
136 private int mExpectedBatchedScans = 0;
137 private long mBatchedScanMinPollTime = 0;
139 /* Chipset supports background scan */
140 private final boolean mBackgroundScanSupported;
142 private String mInterfaceName;
143 /* Tethering interface could be separate from wlan interface */
144 private String mTetherInterfaceName;
146 private int mLastSignalLevel = -1;
147 private String mLastBssid;
148 private int mLastNetworkId;
149 private boolean mEnableRssiPolling = false;
150 private boolean mEnableBackgroundScan = false;
151 private int mRssiPollToken = 0;
152 private int mReconnectCount = 0;
153 /* 3 operational states for STA operation: CONNECT_MODE, SCAN_ONLY_MODE, SCAN_ONLY_WIFI_OFF_MODE
154 * In CONNECT_MODE, the STA can scan and connect to an access point
155 * In SCAN_ONLY_MODE, the STA can only scan for access points
156 * In SCAN_ONLY_WIFI_OFF_MODE, the STA can only scan for access points with wifi toggle being off
158 private int mOperationalMode = CONNECT_MODE;
159 private boolean mScanResultIsPending = false;
160 private WorkSource mScanWorkSource = null;
161 private static final int UNKNOWN_SCAN_SOURCE = -1;
162 /* Tracks if state machine has received any screen state change broadcast yet.
163 * We can miss one of these at boot.
165 private AtomicBoolean mScreenBroadcastReceived = new AtomicBoolean(false);
167 private boolean mBluetoothConnectionActive = false;
169 private PowerManager.WakeLock mSuspendWakeLock;
172 * Interval in milliseconds between polling for RSSI
173 * and linkspeed information
175 private static final int POLL_RSSI_INTERVAL_MSECS = 3000;
178 * Delay between supplicant restarts upon failure to establish connection
180 private static final int SUPPLICANT_RESTART_INTERVAL_MSECS = 5000;
183 * Number of times we attempt to restart supplicant
185 private static final int SUPPLICANT_RESTART_TRIES = 5;
187 private int mSupplicantRestartCount = 0;
188 /* Tracks sequence number on stop failure message */
189 private int mSupplicantStopFailureToken = 0;
192 * Tether state change notification time out
194 private static final int TETHER_NOTIFICATION_TIME_OUT_MSECS = 5000;
196 /* Tracks sequence number on a tether notification time out */
197 private int mTetherToken = 0;
200 * Driver start time out.
202 private static final int DRIVER_START_TIME_OUT_MSECS = 10000;
204 /* Tracks sequence number on a driver time out */
205 private int mDriverStartToken = 0;
208 * The link properties of the wifi interface.
209 * Do not modify this directly; use updateLinkProperties instead.
211 private LinkProperties mLinkProperties;
214 * Subset of link properties coming from netlink.
215 * Currently includes IPv4 and IPv6 addresses. In the future will also include IPv6 DNS servers
216 * and domains obtained from router advertisements (RFC 6106).
218 private final LinkProperties mNetlinkLinkProperties;
220 /* Tracks sequence number on a periodic scan message */
221 private int mPeriodicScanToken = 0;
223 // Wakelock held during wifi start/stop and driver load/unload
224 private PowerManager.WakeLock mWakeLock;
226 private Context mContext;
228 private final Object mDhcpResultsLock = new Object();
229 private DhcpResults mDhcpResults;
230 private WifiInfo mWifiInfo;
231 private NetworkInfo mNetworkInfo;
232 private SupplicantStateTracker mSupplicantStateTracker;
233 private DhcpStateMachine mDhcpStateMachine;
234 private boolean mDhcpActive = false;
236 // Delay in switching to null country code (non-null has no delay)
237 private final int COUNTRY_CODE_DELAY_MS = 15000;
238 private final AtomicInteger mCountryCodeSequence = new AtomicInteger();
240 private class InterfaceObserver extends BaseNetworkObserver {
241 private WifiStateMachine mWifiStateMachine;
243 InterfaceObserver(WifiStateMachine wifiStateMachine) {
245 mWifiStateMachine = wifiStateMachine;
249 public void addressUpdated(String address, String iface, int flags, int scope) {
250 if (mWifiStateMachine.mInterfaceName.equals(iface)) {
252 log("addressUpdated: " + address + " on " + iface +
253 " flags " + flags + " scope " + scope);
255 mWifiStateMachine.sendMessage(CMD_IP_ADDRESS_UPDATED, new LinkAddress(address));
260 public void addressRemoved(String address, String iface, int flags, int scope) {
261 if (mWifiStateMachine.mInterfaceName.equals(iface)) {
263 log("addressRemoved: " + address + " on " + iface +
264 " flags " + flags + " scope " + scope);
266 mWifiStateMachine.sendMessage(CMD_IP_ADDRESS_REMOVED, new LinkAddress(address));
271 private InterfaceObserver mInterfaceObserver;
273 private AlarmManager mAlarmManager;
274 private PendingIntent mScanIntent;
275 private PendingIntent mDriverStopIntent;
276 private PendingIntent mBatchedScanIntervalIntent;
278 /* Tracks current frequency mode */
279 private AtomicInteger mFrequencyBand = new AtomicInteger(WifiManager.WIFI_FREQUENCY_BAND_AUTO);
281 /* Tracks if we are filtering Multicast v4 packets. Default is to filter. */
282 private AtomicBoolean mFilteringMulticastV4Packets = new AtomicBoolean(true);
284 // Channel for sending replies.
285 private AsyncChannel mReplyChannel = new AsyncChannel();
287 private WifiP2pManager mWifiP2pManager;
288 //Used to initiate a connection with WifiP2pService
289 private AsyncChannel mWifiP2pChannel;
290 private AsyncChannel mWifiApConfigChannel;
292 /* The base for wifi message types */
293 static final int BASE = Protocol.BASE_WIFI;
294 /* Start the supplicant */
295 static final int CMD_START_SUPPLICANT = BASE + 11;
296 /* Stop the supplicant */
297 static final int CMD_STOP_SUPPLICANT = BASE + 12;
298 /* Start the driver */
299 static final int CMD_START_DRIVER = BASE + 13;
300 /* Stop the driver */
301 static final int CMD_STOP_DRIVER = BASE + 14;
302 /* Indicates Static IP succeeded */
303 static final int CMD_STATIC_IP_SUCCESS = BASE + 15;
304 /* Indicates Static IP failed */
305 static final int CMD_STATIC_IP_FAILURE = BASE + 16;
306 /* Indicates supplicant stop failed */
307 static final int CMD_STOP_SUPPLICANT_FAILED = BASE + 17;
308 /* Delayed stop to avoid shutting down driver too quick*/
309 static final int CMD_DELAYED_STOP_DRIVER = BASE + 18;
310 /* A delayed message sent to start driver when it fail to come up */
311 static final int CMD_DRIVER_START_TIMED_OUT = BASE + 19;
312 /* Ready to switch to network as default */
313 static final int CMD_CAPTIVE_CHECK_COMPLETE = BASE + 20;
315 /* Start the soft access point */
316 static final int CMD_START_AP = BASE + 21;
317 /* Indicates soft ap start succeeded */
318 static final int CMD_START_AP_SUCCESS = BASE + 22;
319 /* Indicates soft ap start failed */
320 static final int CMD_START_AP_FAILURE = BASE + 23;
321 /* Stop the soft access point */
322 static final int CMD_STOP_AP = BASE + 24;
323 /* Set the soft access point configuration */
324 static final int CMD_SET_AP_CONFIG = BASE + 25;
325 /* Soft access point configuration set completed */
326 static final int CMD_SET_AP_CONFIG_COMPLETED = BASE + 26;
327 /* Request the soft access point configuration */
328 static final int CMD_REQUEST_AP_CONFIG = BASE + 27;
329 /* Response to access point configuration request */
330 static final int CMD_RESPONSE_AP_CONFIG = BASE + 28;
331 /* Invoked when getting a tether state change notification */
332 static final int CMD_TETHER_STATE_CHANGE = BASE + 29;
333 /* A delayed message sent to indicate tether state change failed to arrive */
334 static final int CMD_TETHER_NOTIFICATION_TIMED_OUT = BASE + 30;
336 static final int CMD_BLUETOOTH_ADAPTER_STATE_CHANGE = BASE + 31;
338 /* Supplicant commands */
339 /* Is supplicant alive ? */
340 static final int CMD_PING_SUPPLICANT = BASE + 51;
341 /* Add/update a network configuration */
342 static final int CMD_ADD_OR_UPDATE_NETWORK = BASE + 52;
343 /* Delete a network */
344 static final int CMD_REMOVE_NETWORK = BASE + 53;
345 /* Enable a network. The device will attempt a connection to the given network. */
346 static final int CMD_ENABLE_NETWORK = BASE + 54;
347 /* Enable all networks */
348 static final int CMD_ENABLE_ALL_NETWORKS = BASE + 55;
349 /* Blacklist network. De-prioritizes the given BSSID for connection. */
350 static final int CMD_BLACKLIST_NETWORK = BASE + 56;
351 /* Clear the blacklist network list */
352 static final int CMD_CLEAR_BLACKLIST = BASE + 57;
353 /* Save configuration */
354 static final int CMD_SAVE_CONFIG = BASE + 58;
355 /* Get configured networks*/
356 static final int CMD_GET_CONFIGURED_NETWORKS = BASE + 59;
358 /* Supplicant commands after driver start*/
359 /* Initiate a scan */
360 static final int CMD_START_SCAN = BASE + 71;
361 /* Set operational mode. CONNECT, SCAN ONLY, SCAN_ONLY with Wi-Fi off mode */
362 static final int CMD_SET_OPERATIONAL_MODE = BASE + 72;
363 /* Disconnect from a network */
364 static final int CMD_DISCONNECT = BASE + 73;
365 /* Reconnect to a network */
366 static final int CMD_RECONNECT = BASE + 74;
367 /* Reassociate to a network */
368 static final int CMD_REASSOCIATE = BASE + 75;
369 /* Controls suspend mode optimizations
371 * When high perf mode is enabled, suspend mode optimizations are disabled
373 * When high perf mode is disabled, suspend mode optimizations are enabled
375 * Suspend mode optimizations include:
378 * - DTIM wake up settings
380 static final int CMD_SET_HIGH_PERF_MODE = BASE + 77;
381 /* Set the country code */
382 static final int CMD_SET_COUNTRY_CODE = BASE + 80;
383 /* Enables RSSI poll */
384 static final int CMD_ENABLE_RSSI_POLL = BASE + 82;
386 static final int CMD_RSSI_POLL = BASE + 83;
387 /* Set up packet filtering */
388 static final int CMD_START_PACKET_FILTERING = BASE + 84;
389 /* Clear packet filter */
390 static final int CMD_STOP_PACKET_FILTERING = BASE + 85;
391 /* Enable suspend mode optimizations in the driver */
392 static final int CMD_SET_SUSPEND_OPT_ENABLED = BASE + 86;
393 /* When there are no saved networks, we do a periodic scan to notify user of
395 static final int CMD_NO_NETWORKS_PERIODIC_SCAN = BASE + 88;
397 /* arg1 values to CMD_STOP_PACKET_FILTERING and CMD_START_PACKET_FILTERING */
398 static final int MULTICAST_V6 = 1;
399 static final int MULTICAST_V4 = 0;
401 /* Set the frequency band */
402 static final int CMD_SET_FREQUENCY_BAND = BASE + 90;
403 /* Enable background scan for configured networks */
404 static final int CMD_ENABLE_BACKGROUND_SCAN = BASE + 91;
405 /* Enable TDLS on a specific MAC address */
406 static final int CMD_ENABLE_TDLS = BASE + 92;
408 /* Commands from/to the SupplicantStateTracker */
409 /* Reset the supplicant state tracker */
410 static final int CMD_RESET_SUPPLICANT_STATE = BASE + 111;
413 /* We are ok with no response here since we wont do much with it anyway */
414 public static final int CMD_ENABLE_P2P = BASE + 131;
415 /* In order to shut down supplicant cleanly, we wait till p2p has
417 public static final int CMD_DISABLE_P2P_REQ = BASE + 132;
418 public static final int CMD_DISABLE_P2P_RSP = BASE + 133;
420 public static final int CMD_BOOT_COMPLETED = BASE + 134;
422 /* change the batch scan settings.
423 * arg1 = responsible UID
424 * arg2 = csph (channel scans per hour)
425 * obj = bundle with the new settings and the optional worksource
427 public static final int CMD_SET_BATCHED_SCAN = BASE + 135;
428 public static final int CMD_START_NEXT_BATCHED_SCAN = BASE + 136;
429 public static final int CMD_POLL_BATCHED_SCAN = BASE + 137;
431 /* Link configuration (IP address, DNS, ...) changes */
432 /* An new IP address was added to our interface, or an existing IP address was updated */
433 static final int CMD_IP_ADDRESS_UPDATED = BASE + 140;
434 /* An IP address was removed from our interface */
435 static final int CMD_IP_ADDRESS_REMOVED = BASE + 141;
436 /* Reload all networks and reconnect */
437 static final int CMD_RELOAD_TLS_AND_RECONNECT = BASE + 142;
439 /* Wifi state machine modes of operation */
440 /* CONNECT_MODE - connect to any 'known' AP when it becomes available */
441 public static final int CONNECT_MODE = 1;
442 /* SCAN_ONLY_MODE - don't connect to any APs; scan, but only while apps hold lock */
443 public static final int SCAN_ONLY_MODE = 2;
444 /* SCAN_ONLY_WITH_WIFI_OFF - scan, but don't connect to any APs */
445 public static final int SCAN_ONLY_WITH_WIFI_OFF_MODE = 3;
447 private static final int SUCCESS = 1;
448 private static final int FAILURE = -1;
451 * The maximum number of times we will retry a connection to an access point
452 * for which we have failed in acquiring an IP address from DHCP. A value of
453 * N means that we will make N+1 connection attempts in all.
455 * See {@link Settings.Secure#WIFI_MAX_DHCP_RETRY_COUNT}. This is the default
456 * value if a Settings value is not present.
458 private static final int DEFAULT_MAX_DHCP_RETRIES = 9;
460 /* Tracks if suspend optimizations need to be disabled by DHCP,
461 * screen or due to high perf mode.
462 * When any of them needs to disable it, we keep the suspend optimizations
465 private int mSuspendOptNeedsDisabled = 0;
467 private static final int SUSPEND_DUE_TO_DHCP = 1;
468 private static final int SUSPEND_DUE_TO_HIGH_PERF = 1<<1;
469 private static final int SUSPEND_DUE_TO_SCREEN = 1<<2;
471 /* Tracks if user has enabled suspend optimizations through settings */
472 private AtomicBoolean mUserWantsSuspendOpt = new AtomicBoolean(true);
475 * Default framework scan interval in milliseconds. This is used in the scenario in which
476 * wifi chipset does not support background scanning to set up a
477 * periodic wake up scan so that the device can connect to a new access
478 * point on the move. {@link Settings.Global#WIFI_FRAMEWORK_SCAN_INTERVAL_MS} can
481 private final int mDefaultFrameworkScanIntervalMs;
484 * Supplicant scan interval in milliseconds.
485 * Comes from {@link Settings.Global#WIFI_SUPPLICANT_SCAN_INTERVAL_MS} or
486 * from the default config if the setting is not set
488 private long mSupplicantScanIntervalMs;
491 * Minimum time interval between enabling all networks.
492 * A device can end up repeatedly connecting to a bad network on screen on/off toggle
493 * due to enabling every time. We add a threshold to avoid this.
495 private static final int MIN_INTERVAL_ENABLE_ALL_NETWORKS_MS = 10 * 60 * 1000; /* 10 minutes */
496 private long mLastEnableAllNetworksTime;
499 * Starting and shutting down driver too quick causes problems leading to driver
500 * being in a bad state. Delay driver stop.
502 private final int mDriverStopDelayMs;
503 private int mDelayedStopCounter;
504 private boolean mInDelayedStop = false;
506 // sometimes telephony gives us this data before boot is complete and we can't store it
507 // until after, so the write is deferred
508 private volatile String mPersistedCountryCode;
510 // Supplicant doesn't like setting the same country code multiple times (it may drop
511 // currently connected network), so we save the country code here to avoid redundency
512 private String mLastSetCountryCode;
514 private static final int MIN_RSSI = -200;
515 private static final int MAX_RSSI = 256;
517 /* Default parent state */
518 private State mDefaultState = new DefaultState();
519 /* Temporary initial state */
520 private State mInitialState = new InitialState();
521 /* Driver loaded, waiting for supplicant to start */
522 private State mSupplicantStartingState = new SupplicantStartingState();
523 /* Driver loaded and supplicant ready */
524 private State mSupplicantStartedState = new SupplicantStartedState();
525 /* Waiting for supplicant to stop and monitor to exit */
526 private State mSupplicantStoppingState = new SupplicantStoppingState();
527 /* Driver start issued, waiting for completed event */
528 private State mDriverStartingState = new DriverStartingState();
530 private State mDriverStartedState = new DriverStartedState();
531 /* Wait until p2p is disabled
532 * This is a special state which is entered right after we exit out of DriverStartedState
533 * before transitioning to another state.
535 private State mWaitForP2pDisableState = new WaitForP2pDisableState();
536 /* Driver stopping */
537 private State mDriverStoppingState = new DriverStoppingState();
539 private State mDriverStoppedState = new DriverStoppedState();
540 /* Scan for networks, no connection will be established */
541 private State mScanModeState = new ScanModeState();
542 /* Connecting to an access point */
543 private State mConnectModeState = new ConnectModeState();
544 /* Connected at 802.11 (L2) level */
545 private State mL2ConnectedState = new L2ConnectedState();
546 /* fetching IP after connection to access point (assoc+auth complete) */
547 private State mObtainingIpState = new ObtainingIpState();
548 /* Waiting for link quality verification to be complete */
549 private State mVerifyingLinkState = new VerifyingLinkState();
550 /* Waiting for captive portal check to be complete */
551 private State mCaptivePortalCheckState = new CaptivePortalCheckState();
552 /* Connected with IP addr */
553 private State mConnectedState = new ConnectedState();
554 /* disconnect issued, waiting for network disconnect confirmation */
555 private State mDisconnectingState = new DisconnectingState();
556 /* Network is not connected, supplicant assoc+auth is not complete */
557 private State mDisconnectedState = new DisconnectedState();
558 /* Waiting for WPS to be completed*/
559 private State mWpsRunningState = new WpsRunningState();
561 /* Soft ap is starting up */
562 private State mSoftApStartingState = new SoftApStartingState();
563 /* Soft ap is running */
564 private State mSoftApStartedState = new SoftApStartedState();
565 /* Soft ap is running and we are waiting for tether notification */
566 private State mTetheringState = new TetheringState();
567 /* Soft ap is running and we are tethered through connectivity service */
568 private State mTetheredState = new TetheredState();
569 /* Waiting for untether confirmation before stopping soft Ap */
570 private State mUntetheringState = new UntetheringState();
572 private class TetherStateChange {
573 ArrayList<String> available;
574 ArrayList<String> active;
575 TetherStateChange(ArrayList<String> av, ArrayList<String> ac) {
583 * One of {@link WifiManager#WIFI_STATE_DISABLED},
584 * {@link WifiManager#WIFI_STATE_DISABLING},
585 * {@link WifiManager#WIFI_STATE_ENABLED},
586 * {@link WifiManager#WIFI_STATE_ENABLING},
587 * {@link WifiManager#WIFI_STATE_UNKNOWN}
590 private final AtomicInteger mWifiState = new AtomicInteger(WIFI_STATE_DISABLED);
593 * One of {@link WifiManager#WIFI_AP_STATE_DISABLED},
594 * {@link WifiManager#WIFI_AP_STATE_DISABLING},
595 * {@link WifiManager#WIFI_AP_STATE_ENABLED},
596 * {@link WifiManager#WIFI_AP_STATE_ENABLING},
597 * {@link WifiManager#WIFI_AP_STATE_FAILED}
600 private final AtomicInteger mWifiApState = new AtomicInteger(WIFI_AP_STATE_DISABLED);
602 private static final int SCAN_REQUEST = 0;
603 private static final String ACTION_START_SCAN =
604 "com.android.server.WifiManager.action.START_SCAN";
606 private static final String DELAYED_STOP_COUNTER = "DelayedStopCounter";
607 private static final int DRIVER_STOP_REQUEST = 0;
608 private static final String ACTION_DELAYED_DRIVER_STOP =
609 "com.android.server.WifiManager.action.DELAYED_DRIVER_STOP";
611 private static final String ACTION_REFRESH_BATCHED_SCAN =
612 "com.android.server.WifiManager.action.REFRESH_BATCHED_SCAN";
614 * Keep track of whether WIFI is running.
616 private boolean mIsRunning = false;
619 * Keep track of whether we last told the battery stats we had started.
621 private boolean mReportedRunning = false;
624 * Most recently set source of starting WIFI.
626 private final WorkSource mRunningWifiUids = new WorkSource();
629 * The last reported UIDs that were responsible for starting WIFI.
631 private final WorkSource mLastRunningWifiUids = new WorkSource();
633 private final IBatteryStats mBatteryStats;
635 private BatchedScanSettings mBatchedScanSettings = null;
638 * Track the worksource/cost of the current settings and track what's been noted
639 * to the battery stats, so we can mark the end of the previous when changing.
641 private WorkSource mBatchedScanWorkSource = null;
642 private int mBatchedScanCsph = 0;
643 private WorkSource mNotedBatchedScanWorkSource = null;
644 private int mNotedBatchedScanCsph = 0;
647 public WifiStateMachine(Context context, String wlanInterface) {
648 super("WifiStateMachine");
650 mInterfaceName = wlanInterface;
652 mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, NETWORKTYPE, "");
653 mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
654 BatteryStats.SERVICE_NAME));
656 IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
657 mNwService = INetworkManagementService.Stub.asInterface(b);
659 mP2pSupported = mContext.getPackageManager().hasSystemFeature(
660 PackageManager.FEATURE_WIFI_DIRECT);
662 mWifiNative = new WifiNative(mInterfaceName);
663 mWifiConfigStore = new WifiConfigStore(context, mWifiNative);
664 mWifiMonitor = new WifiMonitor(this, mWifiNative);
665 mWifiInfo = new WifiInfo();
666 mSupplicantStateTracker = new SupplicantStateTracker(context, this, mWifiConfigStore,
668 mLinkProperties = new LinkProperties();
669 mNetlinkLinkProperties = new LinkProperties();
671 mWifiP2pManager = (WifiP2pManager) mContext.getSystemService(Context.WIFI_P2P_SERVICE);
673 mNetworkInfo.setIsAvailable(false);
675 mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
676 mLastSignalLevel = -1;
678 mInterfaceObserver = new InterfaceObserver(this);
680 mNwService.registerObserver(mInterfaceObserver);
681 } catch (RemoteException e) {
682 loge("Couldn't register interface observer: " + e.toString());
685 mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
686 Intent scanIntent = new Intent(ACTION_START_SCAN, null);
687 mScanIntent = PendingIntent.getBroadcast(mContext, SCAN_REQUEST, scanIntent, 0);
689 Intent batchedIntent = new Intent(ACTION_REFRESH_BATCHED_SCAN, null);
690 mBatchedScanIntervalIntent = PendingIntent.getBroadcast(mContext, 0, batchedIntent, 0);
692 mDefaultFrameworkScanIntervalMs = mContext.getResources().getInteger(
693 R.integer.config_wifi_framework_scan_interval);
695 mDriverStopDelayMs = mContext.getResources().getInteger(
696 R.integer.config_wifi_driver_stop_delay);
698 mBackgroundScanSupported = mContext.getResources().getBoolean(
699 R.bool.config_wifi_background_scan_support);
701 mPrimaryDeviceType = mContext.getResources().getString(
702 R.string.config_wifi_p2p_device_type);
704 mUserWantsSuspendOpt.set(Settings.Global.getInt(mContext.getContentResolver(),
705 Settings.Global.WIFI_SUSPEND_OPTIMIZATIONS_ENABLED, 1) == 1);
707 mContext.registerReceiver(
708 new BroadcastReceiver() {
710 public void onReceive(Context context, Intent intent) {
711 ArrayList<String> available = intent.getStringArrayListExtra(
712 ConnectivityManager.EXTRA_AVAILABLE_TETHER);
713 ArrayList<String> active = intent.getStringArrayListExtra(
714 ConnectivityManager.EXTRA_ACTIVE_TETHER);
715 sendMessage(CMD_TETHER_STATE_CHANGE, new TetherStateChange(available, active));
717 },new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED));
719 mContext.registerReceiver(
720 new BroadcastReceiver() {
722 public void onReceive(Context context, Intent intent) {
723 final WorkSource workSource = null;
724 startScan(UNKNOWN_SCAN_SOURCE, workSource);
727 new IntentFilter(ACTION_START_SCAN));
729 IntentFilter filter = new IntentFilter();
730 filter.addAction(Intent.ACTION_SCREEN_ON);
731 filter.addAction(Intent.ACTION_SCREEN_OFF);
732 filter.addAction(ACTION_REFRESH_BATCHED_SCAN);
733 mContext.registerReceiver(
734 new BroadcastReceiver() {
736 public void onReceive(Context context, Intent intent) {
737 String action = intent.getAction();
739 if (action.equals(Intent.ACTION_SCREEN_ON)) {
740 handleScreenStateChanged(true);
741 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
742 handleScreenStateChanged(false);
743 } else if (action.equals(ACTION_REFRESH_BATCHED_SCAN)) {
744 startNextBatchedScanAsync();
749 mContext.registerReceiver(
750 new BroadcastReceiver() {
752 public void onReceive(Context context, Intent intent) {
753 int counter = intent.getIntExtra(DELAYED_STOP_COUNTER, 0);
754 sendMessage(CMD_DELAYED_STOP_DRIVER, counter, 0);
757 new IntentFilter(ACTION_DELAYED_DRIVER_STOP));
759 mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor(
760 Settings.Global.WIFI_SUSPEND_OPTIMIZATIONS_ENABLED), false,
761 new ContentObserver(getHandler()) {
763 public void onChange(boolean selfChange) {
764 mUserWantsSuspendOpt.set(Settings.Global.getInt(mContext.getContentResolver(),
765 Settings.Global.WIFI_SUSPEND_OPTIMIZATIONS_ENABLED, 1) == 1);
769 mContext.registerReceiver(
770 new BroadcastReceiver() {
772 public void onReceive(Context context, Intent intent) {
773 sendMessage(CMD_BOOT_COMPLETED);
776 new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
778 mScanResultCache = new LruCache<String, ScanResult>(SCAN_RESULT_CACHE_SIZE);
780 PowerManager powerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
781 mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, getName());
783 mSuspendWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "WifiSuspend");
784 mSuspendWakeLock.setReferenceCounted(false);
786 addState(mDefaultState);
787 addState(mInitialState, mDefaultState);
788 addState(mSupplicantStartingState, mDefaultState);
789 addState(mSupplicantStartedState, mDefaultState);
790 addState(mDriverStartingState, mSupplicantStartedState);
791 addState(mDriverStartedState, mSupplicantStartedState);
792 addState(mScanModeState, mDriverStartedState);
793 addState(mConnectModeState, mDriverStartedState);
794 addState(mL2ConnectedState, mConnectModeState);
795 addState(mObtainingIpState, mL2ConnectedState);
796 addState(mVerifyingLinkState, mL2ConnectedState);
797 addState(mCaptivePortalCheckState, mL2ConnectedState);
798 addState(mConnectedState, mL2ConnectedState);
799 addState(mDisconnectingState, mConnectModeState);
800 addState(mDisconnectedState, mConnectModeState);
801 addState(mWpsRunningState, mConnectModeState);
802 addState(mWaitForP2pDisableState, mSupplicantStartedState);
803 addState(mDriverStoppingState, mSupplicantStartedState);
804 addState(mDriverStoppedState, mSupplicantStartedState);
805 addState(mSupplicantStoppingState, mDefaultState);
806 addState(mSoftApStartingState, mDefaultState);
807 addState(mSoftApStartedState, mDefaultState);
808 addState(mTetheringState, mSoftApStartedState);
809 addState(mTetheredState, mSoftApStartedState);
810 addState(mUntetheringState, mSoftApStartedState);
812 setInitialState(mInitialState);
815 setLogOnlyTransitions(false);
816 if (DBG) setDbg(true);
818 //start the state machine
821 final Intent intent = new Intent(WifiManager.WIFI_SCAN_AVAILABLE);
822 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
823 intent.putExtra(WifiManager.EXTRA_SCAN_AVAILABLE, WIFI_STATE_DISABLED);
824 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
827 /*********************************************************
828 * Methods exposed for public use
829 ********************************************************/
831 public Messenger getMessenger() {
832 return new Messenger(getHandler());
837 public boolean syncPingSupplicant(AsyncChannel channel) {
838 Message resultMsg = channel.sendMessageSynchronously(CMD_PING_SUPPLICANT);
839 boolean result = (resultMsg.arg1 != FAILURE);
845 * Initiate a wifi scan. If workSource is not null, blame is given to it,
846 * otherwise blame is given to callingUid.
848 * @param callingUid The uid initiating the wifi scan. Blame will be given
849 * here unless workSource is specified.
850 * @param workSource If not null, blame is given to workSource.
852 public void startScan(int callingUid, WorkSource workSource) {
853 sendMessage(CMD_START_SCAN, callingUid, 0, workSource);
857 * start or stop batched scanning using the given settings
859 private static final String BATCHED_SETTING = "batched_settings";
860 private static final String BATCHED_WORKSOURCE = "batched_worksource";
861 public void setBatchedScanSettings(BatchedScanSettings settings, int callingUid, int csph,
862 WorkSource workSource) {
863 Bundle bundle = new Bundle();
864 bundle.putParcelable(BATCHED_SETTING, settings);
865 bundle.putParcelable(BATCHED_WORKSOURCE, workSource);
866 sendMessage(CMD_SET_BATCHED_SCAN, callingUid, csph, bundle);
869 public List<BatchedScanResult> syncGetBatchedScanResultsList() {
870 synchronized (mBatchedScanResults) {
871 List<BatchedScanResult> batchedScanList =
872 new ArrayList<BatchedScanResult>(mBatchedScanResults.size());
873 for(BatchedScanResult result: mBatchedScanResults) {
874 batchedScanList.add(new BatchedScanResult(result));
876 return batchedScanList;
880 public void requestBatchedScanPoll() {
881 sendMessage(CMD_POLL_BATCHED_SCAN);
884 private void startBatchedScan() {
885 if (mBatchedScanSettings == null) return;
888 if (DBG) log("not starting Batched Scans due to DHCP");
892 // first grab any existing data
893 retrieveBatchedScanData();
895 mAlarmManager.cancel(mBatchedScanIntervalIntent);
897 String scansExpected = mWifiNative.setBatchedScanSettings(mBatchedScanSettings);
899 mExpectedBatchedScans = Integer.parseInt(scansExpected);
900 setNextBatchedAlarm(mExpectedBatchedScans);
901 if (mExpectedBatchedScans > 0) noteBatchedScanStart();
902 } catch (NumberFormatException e) {
904 loge("Exception parsing WifiNative.setBatchedScanSettings response " + e);
908 // called from BroadcastListener
909 private void startNextBatchedScanAsync() {
910 sendMessage(CMD_START_NEXT_BATCHED_SCAN);
913 private void startNextBatchedScan() {
914 // first grab any existing data
915 retrieveBatchedScanData();
917 setNextBatchedAlarm(mExpectedBatchedScans);
920 private void handleBatchedScanPollRequest() {
922 log("handleBatchedScanPoll Request - mBatchedScanMinPollTime=" +
923 mBatchedScanMinPollTime + " , mBatchedScanSettings=" +
924 mBatchedScanSettings);
926 // if there is no appropriate PollTime that's because we either aren't
927 // batching or we've already set a time for a poll request
928 if (mBatchedScanMinPollTime == 0) return;
929 if (mBatchedScanSettings == null) return;
931 long now = System.currentTimeMillis();
933 if (now > mBatchedScanMinPollTime) {
934 // do the poll and reset our timers
935 startNextBatchedScan();
937 mAlarmManager.setExact(AlarmManager.RTC_WAKEUP, mBatchedScanMinPollTime,
938 mBatchedScanIntervalIntent);
939 mBatchedScanMinPollTime = 0;
943 // return true if new/different
944 private boolean recordBatchedScanSettings(int responsibleUid, int csph, Bundle bundle) {
945 BatchedScanSettings settings = bundle.getParcelable(BATCHED_SETTING);
946 WorkSource responsibleWorkSource = bundle.getParcelable(BATCHED_WORKSOURCE);
949 log("set batched scan to " + settings + " for uid=" + responsibleUid +
950 ", worksource=" + responsibleWorkSource);
952 if (settings != null) {
953 if (settings.equals(mBatchedScanSettings)) return false;
955 if (mBatchedScanSettings == null) return false;
957 mBatchedScanSettings = settings;
958 if (responsibleWorkSource == null) responsibleWorkSource = new WorkSource(responsibleUid);
959 mBatchedScanWorkSource = responsibleWorkSource;
960 mBatchedScanCsph = csph;
964 private void stopBatchedScan() {
965 mAlarmManager.cancel(mBatchedScanIntervalIntent);
966 retrieveBatchedScanData();
967 mWifiNative.setBatchedScanSettings(null);
968 noteBatchedScanStop();
971 private void setNextBatchedAlarm(int scansExpected) {
973 if (mBatchedScanSettings == null || scansExpected < 1) return;
975 mBatchedScanMinPollTime = System.currentTimeMillis() +
976 mBatchedScanSettings.scanIntervalSec * 1000;
978 if (mBatchedScanSettings.maxScansPerBatch < scansExpected) {
979 scansExpected = mBatchedScanSettings.maxScansPerBatch;
982 int secToFull = mBatchedScanSettings.scanIntervalSec;
983 secToFull *= scansExpected;
985 int debugPeriod = SystemProperties.getInt("wifi.batchedScan.pollPeriod", 0);
986 if (debugPeriod > 0) secToFull = debugPeriod;
988 // set the alarm to do the next poll. We set it a little short as we'd rather
989 // wake up wearly than miss a scan due to buffer overflow
990 mAlarmManager.setExact(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()
991 + ((secToFull - (mBatchedScanSettings.scanIntervalSec / 2)) * 1000),
992 mBatchedScanIntervalIntent);
996 * Start reading new scan data
1001 * "trunc\n" (optional)
1004 * "freq=...\n" (in Mhz)
1006 * "dist=...\n" (in cm)
1007 * "distsd=...\n" (standard deviation, in cm)
1022 private final static boolean DEBUG_PARSE = false;
1023 private void retrieveBatchedScanData() {
1024 String rawData = mWifiNative.getBatchedScanResults();
1025 if (DEBUG_PARSE) log("rawData = " + rawData);
1026 mBatchedScanMinPollTime = 0;
1027 if (rawData == null || rawData.equalsIgnoreCase("OK")) {
1028 loge("Unexpected BatchedScanResults :" + rawData);
1033 final String END_OF_BATCHES = "----";
1034 final String SCANCOUNT = "scancount=";
1035 final String TRUNCATED = "trunc";
1036 final String AGE = "age=";
1037 final String DIST = "dist=";
1038 final String DISTSD = "distSd=";
1040 String splitData[] = rawData.split("\n");
1042 if (splitData[n].startsWith(SCANCOUNT)) {
1044 scanCount = Integer.parseInt(splitData[n++].substring(SCANCOUNT.length()));
1045 } catch (NumberFormatException e) {
1046 loge("scancount parseInt Exception from " + splitData[n]);
1048 } else log("scancount not found");
1049 if (scanCount == 0) {
1050 loge("scanCount==0 - aborting");
1054 final Intent intent = new Intent(WifiManager.BATCHED_SCAN_RESULTS_AVAILABLE_ACTION);
1055 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1057 synchronized (mBatchedScanResults) {
1058 mBatchedScanResults.clear();
1059 BatchedScanResult batchedScanResult = new BatchedScanResult();
1061 String bssid = null;
1062 WifiSsid wifiSsid = null;
1067 dist = distSd = ScanResult.UNSPECIFIED;
1068 final long now = SystemClock.elapsedRealtime();
1069 final int bssidStrLen = BSSID_STR.length();
1072 while (n < splitData.length) {
1073 if (DEBUG_PARSE) logd("parsing " + splitData[n]);
1074 if (splitData[n].equals(END_OF_BATCHES)) {
1075 if (n+1 != splitData.length) {
1076 loge("didn't consume " + (splitData.length-n));
1078 if (mBatchedScanResults.size() > 0) {
1079 mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
1081 logd("retrieveBatchedScanResults X");
1084 if ((splitData[n].equals(END_STR)) || splitData[n].equals(DELIMITER_STR)) {
1085 if (bssid != null) {
1086 batchedScanResult.scanResults.add(new ScanResult(
1087 wifiSsid, bssid, "", level, freq, tsf, dist, distSd));
1093 dist = distSd = ScanResult.UNSPECIFIED;
1095 if (splitData[n].equals(END_STR)) {
1096 if (batchedScanResult.scanResults.size() != 0) {
1097 mBatchedScanResults.add(batchedScanResult);
1098 batchedScanResult = new BatchedScanResult();
1100 logd("Found empty batch");
1103 } else if (splitData[n].equals(TRUNCATED)) {
1104 batchedScanResult.truncated = true;
1105 } else if (splitData[n].startsWith(BSSID_STR)) {
1106 bssid = new String(splitData[n].getBytes(), bssidStrLen,
1107 splitData[n].length() - bssidStrLen);
1108 } else if (splitData[n].startsWith(FREQ_STR)) {
1110 freq = Integer.parseInt(splitData[n].substring(FREQ_STR.length()));
1111 } catch (NumberFormatException e) {
1112 loge("Invalid freqency: " + splitData[n]);
1115 } else if (splitData[n].startsWith(AGE)) {
1117 tsf = now - Long.parseLong(splitData[n].substring(AGE.length()));
1118 tsf *= 1000; // convert mS -> uS
1119 } catch (NumberFormatException e) {
1120 loge("Invalid timestamp: " + splitData[n]);
1123 } else if (splitData[n].startsWith(SSID_STR)) {
1124 wifiSsid = WifiSsid.createFromAsciiEncoded(
1125 splitData[n].substring(SSID_STR.length()));
1126 } else if (splitData[n].startsWith(LEVEL_STR)) {
1128 level = Integer.parseInt(splitData[n].substring(LEVEL_STR.length()));
1129 if (level > 0) level -= 256;
1130 } catch (NumberFormatException e) {
1131 loge("Invalid level: " + splitData[n]);
1134 } else if (splitData[n].startsWith(DIST)) {
1136 dist = Integer.parseInt(splitData[n].substring(DIST.length()));
1137 } catch (NumberFormatException e) {
1138 loge("Invalid distance: " + splitData[n]);
1139 dist = ScanResult.UNSPECIFIED;
1141 } else if (splitData[n].startsWith(DISTSD)) {
1143 distSd = Integer.parseInt(splitData[n].substring(DISTSD.length()));
1144 } catch (NumberFormatException e) {
1145 loge("Invalid distanceSd: " + splitData[n]);
1146 distSd = ScanResult.UNSPECIFIED;
1149 loge("Unable to parse batched scan result line: " + splitData[n]);
1153 rawData = mWifiNative.getBatchedScanResults();
1154 if (DEBUG_PARSE) log("reading more data:\n" + rawData);
1155 if (rawData == null) {
1156 loge("Unexpected null BatchedScanResults");
1159 splitData = rawData.split("\n");
1160 if (splitData.length == 0 || splitData[0].equals("ok")) {
1161 loge("batch scan results just ended!");
1162 if (mBatchedScanResults.size() > 0) {
1163 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
1172 // If workSource is not null, blame is given to it, otherwise blame is given to callingUid.
1173 private void noteScanStart(int callingUid, WorkSource workSource) {
1174 if (mScanWorkSource == null && (callingUid != UNKNOWN_SCAN_SOURCE || workSource != null)) {
1175 mScanWorkSource = workSource != null ? workSource : new WorkSource(callingUid);
1177 mBatteryStats.noteWifiScanStartedFromSource(mScanWorkSource);
1178 } catch (RemoteException e) {
1184 private void noteScanEnd() {
1185 if (mScanWorkSource != null) {
1187 mBatteryStats.noteWifiScanStoppedFromSource(mScanWorkSource);
1188 } catch (RemoteException e) {
1191 mScanWorkSource = null;
1196 private void noteBatchedScanStart() {
1197 // note the end of a previous scan set
1198 if (mNotedBatchedScanWorkSource != null &&
1199 (mNotedBatchedScanWorkSource.equals(mBatchedScanWorkSource) == false ||
1200 mNotedBatchedScanCsph != mBatchedScanCsph)) {
1202 mBatteryStats.noteWifiBatchedScanStoppedFromSource(mNotedBatchedScanWorkSource);
1203 } catch (RemoteException e) {
1206 mNotedBatchedScanWorkSource = null;
1207 mNotedBatchedScanCsph = 0;
1210 // note the start of the new
1212 mBatteryStats.noteWifiBatchedScanStartedFromSource(mBatchedScanWorkSource,
1214 mNotedBatchedScanWorkSource = mBatchedScanWorkSource;
1215 mNotedBatchedScanCsph = mBatchedScanCsph;
1216 } catch (RemoteException e) {
1221 private void noteBatchedScanStop() {
1222 if (mNotedBatchedScanWorkSource != null) {
1224 mBatteryStats.noteWifiBatchedScanStoppedFromSource(mNotedBatchedScanWorkSource);
1225 } catch (RemoteException e) {
1228 mNotedBatchedScanWorkSource = null;
1229 mNotedBatchedScanCsph = 0;
1234 private void startScanNative(int type) {
1235 mWifiNative.scan(type);
1236 mScanResultIsPending = true;
1242 public void setSupplicantRunning(boolean enable) {
1244 sendMessage(CMD_START_SUPPLICANT);
1246 sendMessage(CMD_STOP_SUPPLICANT);
1253 public void setHostApRunning(WifiConfiguration wifiConfig, boolean enable) {
1255 sendMessage(CMD_START_AP, wifiConfig);
1257 sendMessage(CMD_STOP_AP);
1261 public void setWifiApConfiguration(WifiConfiguration config) {
1262 mWifiApConfigChannel.sendMessage(CMD_SET_AP_CONFIG, config);
1265 public WifiConfiguration syncGetWifiApConfiguration() {
1266 Message resultMsg = mWifiApConfigChannel.sendMessageSynchronously(CMD_REQUEST_AP_CONFIG);
1267 WifiConfiguration ret = (WifiConfiguration) resultMsg.obj;
1268 resultMsg.recycle();
1275 public int syncGetWifiState() {
1276 return mWifiState.get();
1282 public String syncGetWifiStateByName() {
1283 switch (mWifiState.get()) {
1284 case WIFI_STATE_DISABLING:
1286 case WIFI_STATE_DISABLED:
1288 case WIFI_STATE_ENABLING:
1290 case WIFI_STATE_ENABLED:
1292 case WIFI_STATE_UNKNOWN:
1293 return "unknown state";
1295 return "[invalid state]";
1302 public int syncGetWifiApState() {
1303 return mWifiApState.get();
1309 public String syncGetWifiApStateByName() {
1310 switch (mWifiApState.get()) {
1311 case WIFI_AP_STATE_DISABLING:
1313 case WIFI_AP_STATE_DISABLED:
1315 case WIFI_AP_STATE_ENABLING:
1317 case WIFI_AP_STATE_ENABLED:
1319 case WIFI_AP_STATE_FAILED:
1322 return "[invalid state]";
1327 * Get status information for the current connection, if any.
1328 * @return a {@link WifiInfo} object containing information about the current connection
1331 public WifiInfo syncRequestConnectionInfo() {
1335 public DhcpResults syncGetDhcpResults() {
1336 synchronized (mDhcpResultsLock) {
1337 return new DhcpResults(mDhcpResults);
1344 public void setDriverStart(boolean enable) {
1346 sendMessage(CMD_START_DRIVER);
1348 sendMessage(CMD_STOP_DRIVER);
1352 public void captivePortalCheckComplete() {
1353 sendMessage(CMD_CAPTIVE_CHECK_COMPLETE);
1359 public void setOperationalMode(int mode) {
1360 if (DBG) log("setting operational mode to " + String.valueOf(mode));
1361 sendMessage(CMD_SET_OPERATIONAL_MODE, mode, 0);
1367 public List<ScanResult> syncGetScanResultsList() {
1368 synchronized (mScanResultCache) {
1369 List<ScanResult> scanList = new ArrayList<ScanResult>();
1370 for(ScanResult result: mScanResults) {
1371 scanList.add(new ScanResult(result));
1378 * Disconnect from Access Point
1380 public void disconnectCommand() {
1381 sendMessage(CMD_DISCONNECT);
1385 * Initiate a reconnection to AP
1387 public void reconnectCommand() {
1388 sendMessage(CMD_RECONNECT);
1392 * Initiate a re-association to AP
1394 public void reassociateCommand() {
1395 sendMessage(CMD_REASSOCIATE);
1399 * Reload networks and then reconnect; helps load correct data for TLS networks
1402 public void reloadTlsNetworksAndReconnect() {
1403 sendMessage(CMD_RELOAD_TLS_AND_RECONNECT);
1407 * Add a network synchronously
1409 * @return network id of the new network
1411 public int syncAddOrUpdateNetwork(AsyncChannel channel, WifiConfiguration config) {
1412 Message resultMsg = channel.sendMessageSynchronously(CMD_ADD_OR_UPDATE_NETWORK, config);
1413 int result = resultMsg.arg1;
1414 resultMsg.recycle();
1418 public List<WifiConfiguration> syncGetConfiguredNetworks(AsyncChannel channel) {
1419 Message resultMsg = channel.sendMessageSynchronously(CMD_GET_CONFIGURED_NETWORKS);
1420 List<WifiConfiguration> result = (List<WifiConfiguration>) resultMsg.obj;
1421 resultMsg.recycle();
1428 * @param networkId id of the network to be removed
1430 public boolean syncRemoveNetwork(AsyncChannel channel, int networkId) {
1431 Message resultMsg = channel.sendMessageSynchronously(CMD_REMOVE_NETWORK, networkId);
1432 boolean result = (resultMsg.arg1 != FAILURE);
1433 resultMsg.recycle();
1440 * @param netId network id of the network
1441 * @param disableOthers true, if all other networks have to be disabled
1442 * @return {@code true} if the operation succeeds, {@code false} otherwise
1444 public boolean syncEnableNetwork(AsyncChannel channel, int netId, boolean disableOthers) {
1445 Message resultMsg = channel.sendMessageSynchronously(CMD_ENABLE_NETWORK, netId,
1446 disableOthers ? 1 : 0);
1447 boolean result = (resultMsg.arg1 != FAILURE);
1448 resultMsg.recycle();
1455 * @param netId network id of the network
1456 * @return {@code true} if the operation succeeds, {@code false} otherwise
1458 public boolean syncDisableNetwork(AsyncChannel channel, int netId) {
1459 Message resultMsg = channel.sendMessageSynchronously(WifiManager.DISABLE_NETWORK, netId);
1460 boolean result = (resultMsg.arg1 != WifiManager.DISABLE_NETWORK_FAILED);
1461 resultMsg.recycle();
1466 * Blacklist a BSSID. This will avoid the AP if there are
1467 * alternate APs to connect
1469 * @param bssid BSSID of the network
1471 public void addToBlacklist(String bssid) {
1472 sendMessage(CMD_BLACKLIST_NETWORK, bssid);
1476 * Clear the blacklist list
1479 public void clearBlacklist() {
1480 sendMessage(CMD_CLEAR_BLACKLIST);
1483 public void enableRssiPolling(boolean enabled) {
1484 sendMessage(CMD_ENABLE_RSSI_POLL, enabled ? 1 : 0, 0);
1487 public void enableBackgroundScanCommand(boolean enabled) {
1488 sendMessage(CMD_ENABLE_BACKGROUND_SCAN, enabled ? 1 : 0, 0);
1491 public void enableAllNetworks() {
1492 sendMessage(CMD_ENABLE_ALL_NETWORKS);
1496 * Start filtering Multicast v4 packets
1498 public void startFilteringMulticastV4Packets() {
1499 mFilteringMulticastV4Packets.set(true);
1500 sendMessage(CMD_START_PACKET_FILTERING, MULTICAST_V4, 0);
1504 * Stop filtering Multicast v4 packets
1506 public void stopFilteringMulticastV4Packets() {
1507 mFilteringMulticastV4Packets.set(false);
1508 sendMessage(CMD_STOP_PACKET_FILTERING, MULTICAST_V4, 0);
1512 * Start filtering Multicast v4 packets
1514 public void startFilteringMulticastV6Packets() {
1515 sendMessage(CMD_START_PACKET_FILTERING, MULTICAST_V6, 0);
1519 * Stop filtering Multicast v4 packets
1521 public void stopFilteringMulticastV6Packets() {
1522 sendMessage(CMD_STOP_PACKET_FILTERING, MULTICAST_V6, 0);
1526 * Set high performance mode of operation.
1527 * Enabling would set active power mode and disable suspend optimizations;
1528 * disabling would set auto power mode and enable suspend optimizations
1529 * @param enable true if enable, false otherwise
1531 public void setHighPerfModeEnabled(boolean enable) {
1532 sendMessage(CMD_SET_HIGH_PERF_MODE, enable ? 1 : 0, 0);
1536 * Set the country code
1537 * @param countryCode following ISO 3166 format
1538 * @param persist {@code true} if the setting should be remembered.
1540 public void setCountryCode(String countryCode, boolean persist) {
1541 // If it's a country code, apply immediately,
1542 // If it's empty, delay it in case it's a momentary dropout
1543 int countryCodeSequence = mCountryCodeSequence.incrementAndGet();
1544 if (TextUtils.isEmpty(countryCode)) {
1545 sendMessageDelayed(CMD_SET_COUNTRY_CODE, countryCodeSequence, persist ? 1 : 0,
1546 countryCode, COUNTRY_CODE_DELAY_MS);
1548 sendMessage(CMD_SET_COUNTRY_CODE, countryCodeSequence, persist ? 1 : 0, countryCode);
1553 * Set the operational frequency band
1555 * @param persist {@code true} if the setting should be remembered.
1557 public void setFrequencyBand(int band, boolean persist) {
1559 Settings.Global.putInt(mContext.getContentResolver(),
1560 Settings.Global.WIFI_FREQUENCY_BAND,
1563 sendMessage(CMD_SET_FREQUENCY_BAND, band, 0);
1567 * Enable TDLS for a specific MAC address
1569 public void enableTdls(String remoteMacAddress, boolean enable) {
1570 int enabler = enable ? 1 : 0;
1571 sendMessage(CMD_ENABLE_TDLS, enabler, 0, remoteMacAddress);
1575 * Returns the operational frequency band
1577 public int getFrequencyBand() {
1578 return mFrequencyBand.get();
1582 * Returns the wifi configuration file
1584 public String getConfigFile() {
1585 return mWifiConfigStore.getConfigFile();
1589 * Send a message indicating bluetooth adapter connection state changed
1591 public void sendBluetoothAdapterStateChange(int state) {
1592 sendMessage(CMD_BLUETOOTH_ADAPTER_STATE_CHANGE, state, 0);
1596 * Save configuration on supplicant
1598 * @return {@code true} if the operation succeeds, {@code false} otherwise
1600 * TODO: deprecate this
1602 public boolean syncSaveConfig(AsyncChannel channel) {
1603 Message resultMsg = channel.sendMessageSynchronously(CMD_SAVE_CONFIG);
1604 boolean result = (resultMsg.arg1 != FAILURE);
1605 resultMsg.recycle();
1609 public void updateBatteryWorkSource(WorkSource newSource) {
1610 synchronized (mRunningWifiUids) {
1612 if (newSource != null) {
1613 mRunningWifiUids.set(newSource);
1616 if (mReportedRunning) {
1617 // If the work source has changed since last time, need
1618 // to remove old work from battery stats.
1619 if (mLastRunningWifiUids.diff(mRunningWifiUids)) {
1620 mBatteryStats.noteWifiRunningChanged(mLastRunningWifiUids,
1622 mLastRunningWifiUids.set(mRunningWifiUids);
1625 // Now being started, report it.
1626 mBatteryStats.noteWifiRunning(mRunningWifiUids);
1627 mLastRunningWifiUids.set(mRunningWifiUids);
1628 mReportedRunning = true;
1631 if (mReportedRunning) {
1632 // Last reported we were running, time to stop.
1633 mBatteryStats.noteWifiStopped(mLastRunningWifiUids);
1634 mLastRunningWifiUids.clear();
1635 mReportedRunning = false;
1638 mWakeLock.setWorkSource(newSource);
1639 } catch (RemoteException ignore) {
1645 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1646 super.dump(fd, pw, args);
1647 mSupplicantStateTracker.dump(fd, pw, args);
1648 pw.println("mLinkProperties " + mLinkProperties);
1649 pw.println("mWifiInfo " + mWifiInfo);
1650 pw.println("mDhcpResults " + mDhcpResults);
1651 pw.println("mNetworkInfo " + mNetworkInfo);
1652 pw.println("mLastSignalLevel " + mLastSignalLevel);
1653 pw.println("mLastBssid " + mLastBssid);
1654 pw.println("mLastNetworkId " + mLastNetworkId);
1655 pw.println("mReconnectCount " + mReconnectCount);
1656 pw.println("mOperationalMode " + mOperationalMode);
1657 pw.println("mUserWantsSuspendOpt " + mUserWantsSuspendOpt);
1658 pw.println("mSuspendOptNeedsDisabled " + mSuspendOptNeedsDisabled);
1659 pw.println("Supplicant status " + mWifiNative.status());
1660 pw.println("mEnableBackgroundScan " + mEnableBackgroundScan);
1662 mWifiConfigStore.dump(fd, pw, args);
1665 /*********************************************************
1666 * Internal private functions
1667 ********************************************************/
1669 private void handleScreenStateChanged(boolean screenOn) {
1670 if (DBG) log("handleScreenStateChanged: " + screenOn);
1671 enableRssiPolling(screenOn);
1672 if (mBackgroundScanSupported) {
1673 enableBackgroundScanCommand(screenOn == false);
1676 if (screenOn) enableAllNetworks();
1677 if (mUserWantsSuspendOpt.get()) {
1679 sendMessage(CMD_SET_SUSPEND_OPT_ENABLED, 0, 0);
1681 //Allow 2s for suspend optimizations to be set
1682 mSuspendWakeLock.acquire(2000);
1683 sendMessage(CMD_SET_SUSPEND_OPT_ENABLED, 1, 0);
1686 mScreenBroadcastReceived.set(true);
1689 private void checkAndSetConnectivityInstance() {
1691 mCm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
1695 private boolean startTethering(ArrayList<String> available) {
1697 boolean wifiAvailable = false;
1699 checkAndSetConnectivityInstance();
1701 String[] wifiRegexs = mCm.getTetherableWifiRegexs();
1703 for (String intf : available) {
1704 for (String regex : wifiRegexs) {
1705 if (intf.matches(regex)) {
1707 InterfaceConfiguration ifcg = null;
1709 ifcg = mNwService.getInterfaceConfig(intf);
1711 /* IP/netmask: 192.168.43.1/255.255.255.0 */
1712 ifcg.setLinkAddress(new LinkAddress(
1713 NetworkUtils.numericToInetAddress("192.168.43.1"), 24));
1714 ifcg.setInterfaceUp();
1716 mNwService.setInterfaceConfig(intf, ifcg);
1718 } catch (Exception e) {
1719 loge("Error configuring interface " + intf + ", :" + e);
1723 if(mCm.tether(intf) != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
1724 loge("Error tethering on " + intf);
1727 mTetherInterfaceName = intf;
1732 // We found no interfaces to tether
1736 private void stopTethering() {
1738 checkAndSetConnectivityInstance();
1740 /* Clear the interface config to allow dhcp correctly configure new
1742 InterfaceConfiguration ifcg = null;
1744 ifcg = mNwService.getInterfaceConfig(mTetherInterfaceName);
1746 ifcg.setLinkAddress(
1747 new LinkAddress(NetworkUtils.numericToInetAddress("0.0.0.0"), 0));
1748 mNwService.setInterfaceConfig(mTetherInterfaceName, ifcg);
1750 } catch (Exception e) {
1751 loge("Error resetting interface " + mTetherInterfaceName + ", :" + e);
1754 if (mCm.untether(mTetherInterfaceName) != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
1755 loge("Untether initiate failed!");
1759 private boolean isWifiTethered(ArrayList<String> active) {
1761 checkAndSetConnectivityInstance();
1763 String[] wifiRegexs = mCm.getTetherableWifiRegexs();
1764 for (String intf : active) {
1765 for (String regex : wifiRegexs) {
1766 if (intf.matches(regex)) {
1771 // We found no interfaces that are tethered
1776 * Set the country code from the system setting value, if any.
1778 private void setCountryCode() {
1779 String countryCode = Settings.Global.getString(mContext.getContentResolver(),
1780 Settings.Global.WIFI_COUNTRY_CODE);
1781 if (countryCode != null && !countryCode.isEmpty()) {
1782 setCountryCode(countryCode, false);
1784 //use driver default
1789 * Set the frequency band from the system setting value, if any.
1791 private void setFrequencyBand() {
1792 int band = Settings.Global.getInt(mContext.getContentResolver(),
1793 Settings.Global.WIFI_FREQUENCY_BAND, WifiManager.WIFI_FREQUENCY_BAND_AUTO);
1794 setFrequencyBand(band, false);
1797 private void setSuspendOptimizationsNative(int reason, boolean enabled) {
1798 if (DBG) log("setSuspendOptimizationsNative: " + reason + " " + enabled);
1800 mSuspendOptNeedsDisabled &= ~reason;
1801 /* None of dhcp, screen or highperf need it disabled and user wants it enabled */
1802 if (mSuspendOptNeedsDisabled == 0 && mUserWantsSuspendOpt.get()) {
1803 mWifiNative.setSuspendOptimizations(true);
1806 mSuspendOptNeedsDisabled |= reason;
1807 mWifiNative.setSuspendOptimizations(false);
1811 private void setSuspendOptimizations(int reason, boolean enabled) {
1812 if (DBG) log("setSuspendOptimizations: " + reason + " " + enabled);
1814 mSuspendOptNeedsDisabled &= ~reason;
1816 mSuspendOptNeedsDisabled |= reason;
1818 if (DBG) log("mSuspendOptNeedsDisabled " + mSuspendOptNeedsDisabled);
1821 private void setWifiState(int wifiState) {
1822 final int previousWifiState = mWifiState.get();
1825 if (wifiState == WIFI_STATE_ENABLED) {
1826 mBatteryStats.noteWifiOn();
1827 } else if (wifiState == WIFI_STATE_DISABLED) {
1828 mBatteryStats.noteWifiOff();
1830 } catch (RemoteException e) {
1831 loge("Failed to note battery stats in wifi");
1834 mWifiState.set(wifiState);
1836 if (DBG) log("setWifiState: " + syncGetWifiStateByName());
1838 final Intent intent = new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION);
1839 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1840 intent.putExtra(WifiManager.EXTRA_WIFI_STATE, wifiState);
1841 intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_STATE, previousWifiState);
1842 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
1845 private void setWifiApState(int wifiApState) {
1846 final int previousWifiApState = mWifiApState.get();
1849 if (wifiApState == WIFI_AP_STATE_ENABLED) {
1850 mBatteryStats.noteWifiOn();
1851 } else if (wifiApState == WIFI_AP_STATE_DISABLED) {
1852 mBatteryStats.noteWifiOff();
1854 } catch (RemoteException e) {
1855 loge("Failed to note battery stats in wifi");
1859 mWifiApState.set(wifiApState);
1861 if (DBG) log("setWifiApState: " + syncGetWifiApStateByName());
1863 final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
1864 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1865 intent.putExtra(WifiManager.EXTRA_WIFI_AP_STATE, wifiApState);
1866 intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_AP_STATE, previousWifiApState);
1867 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
1870 private static final String ID_STR = "id=";
1871 private static final String BSSID_STR = "bssid=";
1872 private static final String FREQ_STR = "freq=";
1873 private static final String LEVEL_STR = "level=";
1874 private static final String TSF_STR = "tsf=";
1875 private static final String FLAGS_STR = "flags=";
1876 private static final String SSID_STR = "ssid=";
1877 private static final String DELIMITER_STR = "====";
1878 private static final String END_STR = "####";
1884 * bssid=68:7f:76:d7:1a:6e
1887 * tsf=1344626243700342
1888 * flags=[WPA2-PSK-CCMP][WPS][ESS]
1892 * bssid=68:5f:74:d7:1a:6f
1895 * tsf=1344626243700373
1896 * flags=[WPA2-PSK-CCMP][WPS][ESS]
1900 private void setScanResults() {
1906 WifiSsid wifiSsid = null;
1909 StringBuffer scanResultsBuf = new StringBuffer();
1913 tmpResults = mWifiNative.scanResults(sid);
1914 if (TextUtils.isEmpty(tmpResults)) break;
1915 scanResultsBuf.append(tmpResults);
1916 scanResultsBuf.append("\n");
1917 String[] lines = tmpResults.split("\n");
1919 for (int i=lines.length - 1; i >= 0; i--) {
1920 if (lines[i].startsWith(END_STR)) {
1922 } else if (lines[i].startsWith(ID_STR)) {
1924 sid = Integer.parseInt(lines[i].substring(ID_STR.length())) + 1;
1925 } catch (NumberFormatException e) {
1931 if (sid == -1) break;
1934 scanResults = scanResultsBuf.toString();
1935 if (TextUtils.isEmpty(scanResults)) {
1939 // note that all these splits and substrings keep references to the original
1940 // huge string buffer while the amount we really want is generally pretty small
1941 // so make copies instead (one example b/11087956 wasted 400k of heap here).
1942 synchronized(mScanResultCache) {
1943 mScanResults = new ArrayList<ScanResult>();
1944 String[] lines = scanResults.split("\n");
1945 final int bssidStrLen = BSSID_STR.length();
1946 final int flagLen = FLAGS_STR.length();
1948 for (String line : lines) {
1949 if (line.startsWith(BSSID_STR)) {
1950 bssid = new String(line.getBytes(), bssidStrLen, line.length() - bssidStrLen);
1951 } else if (line.startsWith(FREQ_STR)) {
1953 freq = Integer.parseInt(line.substring(FREQ_STR.length()));
1954 } catch (NumberFormatException e) {
1957 } else if (line.startsWith(LEVEL_STR)) {
1959 level = Integer.parseInt(line.substring(LEVEL_STR.length()));
1960 /* some implementations avoid negative values by adding 256
1961 * so we need to adjust for that here.
1963 if (level > 0) level -= 256;
1964 } catch(NumberFormatException e) {
1967 } else if (line.startsWith(TSF_STR)) {
1969 tsf = Long.parseLong(line.substring(TSF_STR.length()));
1970 } catch (NumberFormatException e) {
1973 } else if (line.startsWith(FLAGS_STR)) {
1974 flags = new String(line.getBytes(), flagLen, line.length() - flagLen);
1975 } else if (line.startsWith(SSID_STR)) {
1976 wifiSsid = WifiSsid.createFromAsciiEncoded(
1977 line.substring(SSID_STR.length()));
1978 } else if (line.startsWith(DELIMITER_STR) || line.startsWith(END_STR)) {
1979 if (bssid != null) {
1980 String ssid = (wifiSsid != null) ? wifiSsid.toString() : WifiSsid.NONE;
1981 String key = bssid + ssid;
1982 ScanResult scanResult = mScanResultCache.get(key);
1983 if (scanResult != null) {
1984 scanResult.level = level;
1985 scanResult.wifiSsid = wifiSsid;
1986 // Keep existing API
1987 scanResult.SSID = (wifiSsid != null) ? wifiSsid.toString() :
1989 scanResult.capabilities = flags;
1990 scanResult.frequency = freq;
1991 scanResult.timestamp = tsf;
1995 wifiSsid, bssid, flags, level, freq, tsf);
1996 mScanResultCache.put(key, scanResult);
1998 mScanResults.add(scanResult);
2012 * Fetch RSSI and linkspeed on current connection
2014 private void fetchRssiAndLinkSpeedNative() {
2016 int newLinkSpeed = -1;
2018 String signalPoll = mWifiNative.signalPoll();
2020 if (signalPoll != null) {
2021 String[] lines = signalPoll.split("\n");
2022 for (String line : lines) {
2023 String[] prop = line.split("=");
2024 if (prop.length < 2) continue;
2026 if (prop[0].equals("RSSI")) {
2027 newRssi = Integer.parseInt(prop[1]);
2028 } else if (prop[0].equals("LINKSPEED")) {
2029 newLinkSpeed = Integer.parseInt(prop[1]);
2031 } catch (NumberFormatException e) {
2032 //Ignore, defaults on rssi and linkspeed are assigned
2037 if (newRssi != -1 && MIN_RSSI < newRssi && newRssi < MAX_RSSI) { // screen out invalid values
2038 /* some implementations avoid negative values by adding 256
2039 * so we need to adjust for that here.
2041 if (newRssi > 0) newRssi -= 256;
2042 mWifiInfo.setRssi(newRssi);
2044 * Rather then sending the raw RSSI out every time it
2045 * changes, we precalculate the signal level that would
2046 * be displayed in the status bar, and only send the
2047 * broadcast if that much more coarse-grained number
2048 * changes. This cuts down greatly on the number of
2049 * broadcasts, at the cost of not informing others
2050 * interested in RSSI of all the changes in signal
2053 int newSignalLevel = WifiManager.calculateSignalLevel(newRssi, WifiManager.RSSI_LEVELS);
2054 if (newSignalLevel != mLastSignalLevel) {
2055 sendRssiChangeBroadcast(newRssi);
2057 mLastSignalLevel = newSignalLevel;
2059 mWifiInfo.setRssi(MIN_RSSI);
2062 if (newLinkSpeed != -1) {
2063 mWifiInfo.setLinkSpeed(newLinkSpeed);
2068 * Fetch TX packet counters on current connection
2070 private void fetchPktcntNative(RssiPacketCountInfo info) {
2071 String pktcntPoll = mWifiNative.pktcntPoll();
2073 if (pktcntPoll != null) {
2074 String[] lines = pktcntPoll.split("\n");
2075 for (String line : lines) {
2076 String[] prop = line.split("=");
2077 if (prop.length < 2) continue;
2079 if (prop[0].equals("TXGOOD")) {
2080 info.txgood = Integer.parseInt(prop[1]);
2081 } else if (prop[0].equals("TXBAD")) {
2082 info.txbad = Integer.parseInt(prop[1]);
2084 } catch (NumberFormatException e) {
2092 * Updates mLinkProperties by merging information from various sources.
2094 * This is needed because the information in mLinkProperties comes from multiple sources (DHCP,
2095 * netlink, static configuration, ...). When one of these sources of information has updated
2096 * link properties, we can't just assign them to mLinkProperties or we'd lose track of the
2097 * information that came from other sources. Instead, when one of those sources has new
2098 * information, we update the object that tracks the information from that source and then
2099 * call this method to apply the change to mLinkProperties.
2101 * The information in mLinkProperties is currently obtained as follows:
2102 * - Interface name: set in the constructor.
2103 * - IPv4 and IPv6 addresses: netlink, via mInterfaceObserver.
2104 * - IPv4 routes, DNS servers, and domains: DHCP.
2105 * - HTTP proxy: the wifi config store.
2107 private void updateLinkProperties() {
2108 LinkProperties newLp = new LinkProperties();
2110 // Interface name and proxy are locally configured.
2111 newLp.setInterfaceName(mInterfaceName);
2112 newLp.setHttpProxy(mWifiConfigStore.getProxyProperties(mLastNetworkId));
2114 // IPv4 and IPv6 addresses come from netlink.
2115 newLp.setLinkAddresses(mNetlinkLinkProperties.getLinkAddresses());
2117 // For now, routing and DNS only come from DHCP or static configuration. In the future,
2118 // we'll need to merge IPv6 DNS servers and domains coming from netlink.
2119 synchronized (mDhcpResultsLock) {
2120 // Even when we're using static configuration, we don't need to look at the config
2121 // store, because static IP configuration also populates mDhcpResults.
2122 if ((mDhcpResults != null) && (mDhcpResults.linkProperties != null)) {
2123 LinkProperties lp = mDhcpResults.linkProperties;
2124 for (RouteInfo route: lp.getRoutes()) {
2125 newLp.addRoute(route);
2127 for (InetAddress dns: lp.getDnses()) {
2130 newLp.setDomains(lp.getDomains());
2134 // If anything has changed, and we're already connected, send out a notification.
2135 // If we're still connecting, apps will be notified when we connect.
2136 if (!newLp.equals(mLinkProperties)) {
2138 log("Link configuration changed for netId: " + mLastNetworkId
2139 + " old: " + mLinkProperties + "new: " + newLp);
2141 mLinkProperties = newLp;
2142 if (getNetworkDetailedState() == DetailedState.CONNECTED) {
2143 sendLinkConfigurationChangedBroadcast();
2149 * Clears all our link properties.
2151 private void clearLinkProperties() {
2152 // If the network used DHCP, clear the LinkProperties we stored in the config store.
2153 if (!mWifiConfigStore.isUsingStaticIp(mLastNetworkId)) {
2154 mWifiConfigStore.clearLinkProperties(mLastNetworkId);
2157 // Clear the link properties obtained from DHCP and netlink.
2158 synchronized(mDhcpResultsLock) {
2159 if (mDhcpResults != null && mDhcpResults.linkProperties != null) {
2160 mDhcpResults.linkProperties.clear();
2163 mNetlinkLinkProperties.clear();
2165 // Now clear the merged link properties.
2166 mLinkProperties.clear();
2169 private int getMaxDhcpRetries() {
2170 return Settings.Global.getInt(mContext.getContentResolver(),
2171 Settings.Global.WIFI_MAX_DHCP_RETRY_COUNT,
2172 DEFAULT_MAX_DHCP_RETRIES);
2175 private void sendScanResultsAvailableBroadcast() {
2177 Intent intent = new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
2178 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
2179 mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
2182 private void sendRssiChangeBroadcast(final int newRssi) {
2183 Intent intent = new Intent(WifiManager.RSSI_CHANGED_ACTION);
2184 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
2185 intent.putExtra(WifiManager.EXTRA_NEW_RSSI, newRssi);
2186 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
2189 private void sendNetworkStateChangeBroadcast(String bssid) {
2190 Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
2191 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
2192 intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, new NetworkInfo(mNetworkInfo));
2193 intent.putExtra(WifiManager.EXTRA_LINK_PROPERTIES, new LinkProperties (mLinkProperties));
2195 intent.putExtra(WifiManager.EXTRA_BSSID, bssid);
2196 if (mNetworkInfo.getDetailedState() == DetailedState.VERIFYING_POOR_LINK ||
2197 mNetworkInfo.getDetailedState() == DetailedState.CONNECTED) {
2198 intent.putExtra(WifiManager.EXTRA_WIFI_INFO, new WifiInfo(mWifiInfo));
2200 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
2203 private void sendLinkConfigurationChangedBroadcast() {
2204 Intent intent = new Intent(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION);
2205 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
2206 intent.putExtra(WifiManager.EXTRA_LINK_PROPERTIES, new LinkProperties(mLinkProperties));
2207 mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
2210 private void sendSupplicantConnectionChangedBroadcast(boolean connected) {
2211 Intent intent = new Intent(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
2212 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
2213 intent.putExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, connected);
2214 mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
2218 * Record the detailed state of a network.
2219 * @param state the new {@code DetailedState}
2221 private void setNetworkDetailedState(NetworkInfo.DetailedState state) {
2223 log("setDetailed state, old ="
2224 + mNetworkInfo.getDetailedState() + " and new state=" + state);
2227 if (state != mNetworkInfo.getDetailedState()) {
2228 mNetworkInfo.setDetailedState(state, null, mWifiInfo.getSSID());
2232 private DetailedState getNetworkDetailedState() {
2233 return mNetworkInfo.getDetailedState();
2237 private SupplicantState handleSupplicantStateChange(Message message) {
2238 StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
2239 SupplicantState state = stateChangeResult.state;
2240 // Supplicant state change
2241 // [31-13] Reserved for future use
2242 // [8 - 0] Supplicant state (as defined in SupplicantState.java)
2243 // 50023 supplicant_state_changed (custom|1|5)
2244 mWifiInfo.setSupplicantState(state);
2245 // Network id is only valid when we start connecting
2246 if (SupplicantState.isConnecting(state)) {
2247 mWifiInfo.setNetworkId(stateChangeResult.networkId);
2249 mWifiInfo.setNetworkId(WifiConfiguration.INVALID_NETWORK_ID);
2252 mWifiInfo.setBSSID(stateChangeResult.BSSID);
2253 mWifiInfo.setSSID(stateChangeResult.wifiSsid);
2255 mSupplicantStateTracker.sendMessage(Message.obtain(message));
2261 * Resets the Wi-Fi Connections by clearing any state, resetting any sockets
2262 * using the interface, stopping DHCP & disabling interface
2264 private void handleNetworkDisconnect() {
2265 if (DBG) log("Stopping DHCP and clearing IP");
2270 mNwService.clearInterfaceAddresses(mInterfaceName);
2271 mNwService.disableIpv6(mInterfaceName);
2272 } catch (Exception e) {
2273 loge("Failed to clear addresses or disable ipv6" + e);
2276 /* Reset data structures */
2277 mWifiInfo.setInetAddress(null);
2278 mWifiInfo.setBSSID(null);
2279 mWifiInfo.setSSID(null);
2280 mWifiInfo.setNetworkId(WifiConfiguration.INVALID_NETWORK_ID);
2281 mWifiInfo.setRssi(MIN_RSSI);
2282 mWifiInfo.setLinkSpeed(-1);
2283 mWifiInfo.setMeteredHint(false);
2285 setNetworkDetailedState(DetailedState.DISCONNECTED);
2286 mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.DISCONNECTED);
2288 /* Clear network properties */
2289 clearLinkProperties();
2291 /* send event to CM & network change broadcast */
2292 sendNetworkStateChangeBroadcast(mLastBssid);
2295 mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
2298 private void handleSupplicantConnectionLoss() {
2299 /* Socket connection can be lost when we do a graceful shutdown
2300 * or when the driver is hung. Ensure supplicant is stopped here.
2302 mWifiMonitor.killSupplicant(mP2pSupported);
2303 sendSupplicantConnectionChangedBroadcast(false);
2304 setWifiState(WIFI_STATE_DISABLED);
2307 void handlePreDhcpSetup() {
2309 if (!mBluetoothConnectionActive) {
2311 * There are problems setting the Wi-Fi driver's power
2312 * mode to active when bluetooth coexistence mode is
2315 * We set Wi-Fi to active mode when
2316 * obtaining an IP address because we've found
2317 * compatibility issues with some routers with low power
2320 * In order for this active power mode to properly be set,
2321 * we disable coexistence mode until we're done with
2322 * obtaining an IP address. One exception is if we
2323 * are currently connected to a headset, since disabling
2324 * coexistence would interrupt that connection.
2326 // Disable the coexistence mode
2327 mWifiNative.setBluetoothCoexistenceMode(
2328 mWifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED);
2331 /* Disable power save and suspend optimizations during DHCP */
2332 // Note: The order here is important for now. Brcm driver changes
2333 // power settings when we control suspend mode optimizations.
2334 // TODO: Remove this comment when the driver is fixed.
2335 setSuspendOptimizationsNative(SUSPEND_DUE_TO_DHCP, false);
2336 mWifiNative.setPowerSave(false);
2340 /* P2p discovery breaks dhcp, shut it down in order to get through this */
2341 Message msg = new Message();
2342 msg.what = WifiP2pService.BLOCK_DISCOVERY;
2343 msg.arg1 = WifiP2pService.ENABLED;
2344 msg.arg2 = DhcpStateMachine.CMD_PRE_DHCP_ACTION_COMPLETE;
2345 msg.obj = mDhcpStateMachine;
2346 mWifiP2pChannel.sendMessage(msg);
2351 if (mDhcpStateMachine == null) {
2352 mDhcpStateMachine = DhcpStateMachine.makeDhcpStateMachine(
2353 mContext, WifiStateMachine.this, mInterfaceName);
2356 mDhcpStateMachine.registerForPreDhcpNotification();
2357 mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_START_DHCP);
2361 if (mDhcpStateMachine != null) {
2362 /* In case we were in middle of DHCP operation restore back powermode */
2363 handlePostDhcpSetup();
2364 mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_STOP_DHCP);
2368 void handlePostDhcpSetup() {
2369 /* Restore power save and suspend optimizations */
2370 setSuspendOptimizationsNative(SUSPEND_DUE_TO_DHCP, true);
2371 mWifiNative.setPowerSave(true);
2373 mWifiP2pChannel.sendMessage(WifiP2pService.BLOCK_DISCOVERY, WifiP2pService.DISABLED);
2375 // Set the coexistence mode back to its default value
2376 mWifiNative.setBluetoothCoexistenceMode(
2377 mWifiNative.BLUETOOTH_COEXISTENCE_MODE_SENSE);
2379 mDhcpActive = false;
2384 private void handleSuccessfulIpConfiguration(DhcpResults dhcpResults) {
2385 mLastSignalLevel = -1; // force update of signal strength
2386 mReconnectCount = 0; //Reset IP failure tracking
2387 synchronized (mDhcpResultsLock) {
2388 mDhcpResults = dhcpResults;
2390 LinkProperties linkProperties = dhcpResults.linkProperties;
2391 mWifiConfigStore.setLinkProperties(mLastNetworkId, new LinkProperties(linkProperties));
2392 InetAddress addr = null;
2393 Iterator<InetAddress> addrs = linkProperties.getAddresses().iterator();
2394 if (addrs.hasNext()) {
2395 addr = addrs.next();
2397 mWifiInfo.setInetAddress(addr);
2398 mWifiInfo.setMeteredHint(dhcpResults.hasMeteredHint());
2399 updateLinkProperties();
2402 private void handleFailedIpConfiguration() {
2403 loge("IP configuration failed");
2405 mWifiInfo.setInetAddress(null);
2406 mWifiInfo.setMeteredHint(false);
2408 * If we've exceeded the maximum number of retries for DHCP
2409 * to a given network, disable the network
2411 int maxRetries = getMaxDhcpRetries();
2412 // maxRetries == 0 means keep trying forever
2413 if (maxRetries > 0 && ++mReconnectCount > maxRetries) {
2415 mReconnectCount + " times, Disabling " + mLastNetworkId);
2416 mWifiConfigStore.disableNetwork(mLastNetworkId,
2417 WifiConfiguration.DISABLED_DHCP_FAILURE);
2418 mReconnectCount = 0;
2421 /* DHCP times out after about 30 seconds, we do a
2422 * disconnect and an immediate reconnect to try again
2424 mWifiNative.disconnect();
2425 mWifiNative.reconnect();
2428 /* Current design is to not set the config on a running hostapd but instead
2429 * stop and start tethering when user changes config on a running access point
2431 * TODO: Add control channel setup through hostapd that allows changing config
2432 * on a running daemon
2434 private void startSoftApWithConfig(final WifiConfiguration config) {
2435 // start hostapd on a seperate thread
2436 new Thread(new Runnable() {
2439 mNwService.startAccessPoint(config, mInterfaceName);
2440 } catch (Exception e) {
2441 loge("Exception in softap start " + e);
2443 mNwService.stopAccessPoint(mInterfaceName);
2444 mNwService.startAccessPoint(config, mInterfaceName);
2445 } catch (Exception e1) {
2446 loge("Exception in softap re-start " + e1);
2447 sendMessage(CMD_START_AP_FAILURE);
2451 if (DBG) log("Soft AP start successful");
2452 sendMessage(CMD_START_AP_SUCCESS);
2457 /********************************************************
2459 *******************************************************/
2461 class DefaultState extends State {
2463 public boolean processMessage(Message message) {
2464 switch (message.what) {
2465 case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
2466 if (message.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
2467 mWifiP2pChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
2469 loge("WifiP2pService connection failure, error=" + message.arg1);
2472 case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
2473 loge("WifiP2pService channel lost, message.arg1 =" + message.arg1);
2474 //TODO: Re-establish connection to state machine after a delay
2475 //mWifiP2pChannel.connect(mContext, getHandler(), mWifiP2pManager.getMessenger());
2477 case CMD_BLUETOOTH_ADAPTER_STATE_CHANGE:
2478 mBluetoothConnectionActive = (message.arg1 !=
2479 BluetoothAdapter.STATE_DISCONNECTED);
2481 /* Synchronous call returns */
2482 case CMD_PING_SUPPLICANT:
2483 case CMD_ENABLE_NETWORK:
2484 case CMD_ADD_OR_UPDATE_NETWORK:
2485 case CMD_REMOVE_NETWORK:
2486 case CMD_SAVE_CONFIG:
2487 replyToMessage(message, message.what, FAILURE);
2489 case CMD_GET_CONFIGURED_NETWORKS:
2490 replyToMessage(message, message.what, (List<WifiConfiguration>) null);
2492 case CMD_ENABLE_RSSI_POLL:
2493 mEnableRssiPolling = (message.arg1 == 1);
2495 case CMD_ENABLE_BACKGROUND_SCAN:
2496 mEnableBackgroundScan = (message.arg1 == 1);
2498 case CMD_SET_HIGH_PERF_MODE:
2499 if (message.arg1 == 1) {
2500 setSuspendOptimizations(SUSPEND_DUE_TO_HIGH_PERF, false);
2502 setSuspendOptimizations(SUSPEND_DUE_TO_HIGH_PERF, true);
2505 case CMD_BOOT_COMPLETED:
2506 String countryCode = mPersistedCountryCode;
2507 if (TextUtils.isEmpty(countryCode) == false) {
2508 Settings.Global.putString(mContext.getContentResolver(),
2509 Settings.Global.WIFI_COUNTRY_CODE,
2511 // it may be that the state transition that should send this info
2512 // to the driver happened between mPersistedCountryCode getting set
2513 // and now, so simply persisting it here would mean we have sent
2514 // nothing to the driver. Send the cmd so it might be set now.
2515 int sequenceNum = mCountryCodeSequence.incrementAndGet();
2516 sendMessageAtFrontOfQueue(CMD_SET_COUNTRY_CODE,
2517 sequenceNum, 0, countryCode);
2520 case CMD_SET_BATCHED_SCAN:
2521 recordBatchedScanSettings(message.arg1, message.arg2, (Bundle)message.obj);
2523 case CMD_POLL_BATCHED_SCAN:
2524 handleBatchedScanPollRequest();
2526 case CMD_START_NEXT_BATCHED_SCAN:
2527 startNextBatchedScan();
2530 case CMD_START_SCAN:
2531 case CMD_START_SUPPLICANT:
2532 case CMD_STOP_SUPPLICANT:
2533 case CMD_STOP_SUPPLICANT_FAILED:
2534 case CMD_START_DRIVER:
2535 case CMD_STOP_DRIVER:
2536 case CMD_DELAYED_STOP_DRIVER:
2537 case CMD_DRIVER_START_TIMED_OUT:
2538 case CMD_CAPTIVE_CHECK_COMPLETE:
2540 case CMD_START_AP_SUCCESS:
2541 case CMD_START_AP_FAILURE:
2543 case CMD_TETHER_STATE_CHANGE:
2544 case CMD_TETHER_NOTIFICATION_TIMED_OUT:
2545 case CMD_DISCONNECT:
2547 case CMD_REASSOCIATE:
2548 case CMD_RELOAD_TLS_AND_RECONNECT:
2549 case WifiMonitor.SUP_CONNECTION_EVENT:
2550 case WifiMonitor.SUP_DISCONNECTION_EVENT:
2551 case WifiMonitor.NETWORK_CONNECTION_EVENT:
2552 case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
2553 case WifiMonitor.SCAN_RESULTS_EVENT:
2554 case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
2555 case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
2556 case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
2557 case WifiMonitor.WPS_OVERLAP_EVENT:
2558 case CMD_BLACKLIST_NETWORK:
2559 case CMD_CLEAR_BLACKLIST:
2560 case CMD_SET_OPERATIONAL_MODE:
2561 case CMD_SET_COUNTRY_CODE:
2562 case CMD_SET_FREQUENCY_BAND:
2564 case CMD_ENABLE_ALL_NETWORKS:
2565 case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
2566 case DhcpStateMachine.CMD_POST_DHCP_ACTION:
2567 /* Handled by WifiApConfigStore */
2568 case CMD_SET_AP_CONFIG:
2569 case CMD_SET_AP_CONFIG_COMPLETED:
2570 case CMD_REQUEST_AP_CONFIG:
2571 case CMD_RESPONSE_AP_CONFIG:
2572 case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
2573 case WifiWatchdogStateMachine.GOOD_LINK_DETECTED:
2574 case CMD_NO_NETWORKS_PERIODIC_SCAN:
2575 case CMD_DISABLE_P2P_RSP:
2577 case DhcpStateMachine.CMD_ON_QUIT:
2578 mDhcpStateMachine = null;
2580 case CMD_SET_SUSPEND_OPT_ENABLED:
2581 if (message.arg1 == 1) {
2582 mSuspendWakeLock.release();
2583 setSuspendOptimizations(SUSPEND_DUE_TO_SCREEN, true);
2585 setSuspendOptimizations(SUSPEND_DUE_TO_SCREEN, false);
2588 case WifiMonitor.DRIVER_HUNG_EVENT:
2589 setSupplicantRunning(false);
2590 setSupplicantRunning(true);
2592 case WifiManager.CONNECT_NETWORK:
2593 replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
2596 case WifiManager.FORGET_NETWORK:
2597 replyToMessage(message, WifiManager.FORGET_NETWORK_FAILED,
2600 case WifiManager.SAVE_NETWORK:
2601 replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED,
2604 case WifiManager.START_WPS:
2605 replyToMessage(message, WifiManager.WPS_FAILED,
2608 case WifiManager.CANCEL_WPS:
2609 replyToMessage(message, WifiManager.CANCEL_WPS_FAILED,
2612 case WifiManager.DISABLE_NETWORK:
2613 replyToMessage(message, WifiManager.DISABLE_NETWORK_FAILED,
2616 case WifiManager.RSSI_PKTCNT_FETCH:
2617 replyToMessage(message, WifiManager.RSSI_PKTCNT_FETCH_FAILED,
2620 case WifiP2pService.P2P_CONNECTION_CHANGED:
2621 NetworkInfo info = (NetworkInfo) message.obj;
2622 mP2pConnected.set(info.isConnected());
2624 case WifiP2pService.DISCONNECT_WIFI_REQUEST:
2625 mTemporarilyDisconnectWifi = (message.arg1 == 1);
2626 replyToMessage(message, WifiP2pService.DISCONNECT_WIFI_RESPONSE);
2628 case CMD_IP_ADDRESS_UPDATED:
2629 // addLinkAddress is a no-op if called more than once with the same address.
2630 if (mNetlinkLinkProperties.addLinkAddress((LinkAddress) message.obj)) {
2631 updateLinkProperties();
2634 case CMD_IP_ADDRESS_REMOVED:
2635 if (mNetlinkLinkProperties.removeLinkAddress((LinkAddress) message.obj)) {
2636 updateLinkProperties();
2640 loge("Error! unhandled message" + message);
2647 class InitialState extends State {
2649 public void enter() {
2650 mWifiNative.unloadDriver();
2652 if (mWifiP2pChannel == null) {
2653 mWifiP2pChannel = new AsyncChannel();
2654 mWifiP2pChannel.connect(mContext, getHandler(), mWifiP2pManager.getMessenger());
2657 if (mWifiApConfigChannel == null) {
2658 mWifiApConfigChannel = new AsyncChannel();
2659 WifiApConfigStore wifiApConfigStore = WifiApConfigStore.makeWifiApConfigStore(
2660 mContext, getHandler());
2661 wifiApConfigStore.loadApConfiguration();
2662 mWifiApConfigChannel.connectSync(mContext, getHandler(),
2663 wifiApConfigStore.getMessenger());
2667 public boolean processMessage(Message message) {
2668 switch (message.what) {
2669 case CMD_START_SUPPLICANT:
2670 if (mWifiNative.loadDriver()) {
2672 mNwService.wifiFirmwareReload(mInterfaceName, "STA");
2673 } catch (Exception e) {
2674 loge("Failed to reload STA firmware " + e);
2679 // A runtime crash can leave the interface up and
2680 // this affects connectivity when supplicant starts up.
2681 // Ensure interface is down before a supplicant start.
2682 mNwService.setInterfaceDown(mInterfaceName);
2683 // Set privacy extensions
2684 mNwService.setInterfaceIpv6PrivacyExtensions(mInterfaceName, true);
2686 // IPv6 is enabled only as long as access point is connected since:
2687 // - IPv6 addresses and routes stick around after disconnection
2688 // - kernel is unaware when connected and fails to start IPv6 negotiation
2689 // - kernel can start autoconfiguration when 802.1x is not complete
2690 mNwService.disableIpv6(mInterfaceName);
2691 } catch (RemoteException re) {
2692 loge("Unable to change interface settings: " + re);
2693 } catch (IllegalStateException ie) {
2694 loge("Unable to change interface settings: " + ie);
2697 /* Stop a running supplicant after a runtime restart
2698 * Avoids issues with drivers that do not handle interface down
2699 * on a running supplicant properly.
2701 mWifiMonitor.killSupplicant(mP2pSupported);
2702 if(mWifiNative.startSupplicant(mP2pSupported)) {
2703 setWifiState(WIFI_STATE_ENABLING);
2704 if (DBG) log("Supplicant start successful");
2705 mWifiMonitor.startMonitoring();
2706 transitionTo(mSupplicantStartingState);
2708 loge("Failed to start supplicant!");
2711 loge("Failed to load driver");
2715 if (mWifiNative.loadDriver()) {
2716 setWifiApState(WIFI_AP_STATE_ENABLING);
2717 transitionTo(mSoftApStartingState);
2719 loge("Failed to load driver for softap");
2728 class SupplicantStartingState extends State {
2729 private void initializeWpsDetails() {
2731 detail = SystemProperties.get("ro.product.name", "");
2732 if (!mWifiNative.setDeviceName(detail)) {
2733 loge("Failed to set device name " + detail);
2735 detail = SystemProperties.get("ro.product.manufacturer", "");
2736 if (!mWifiNative.setManufacturer(detail)) {
2737 loge("Failed to set manufacturer " + detail);
2739 detail = SystemProperties.get("ro.product.model", "");
2740 if (!mWifiNative.setModelName(detail)) {
2741 loge("Failed to set model name " + detail);
2743 detail = SystemProperties.get("ro.product.model", "");
2744 if (!mWifiNative.setModelNumber(detail)) {
2745 loge("Failed to set model number " + detail);
2747 detail = SystemProperties.get("ro.serialno", "");
2748 if (!mWifiNative.setSerialNumber(detail)) {
2749 loge("Failed to set serial number " + detail);
2751 if (!mWifiNative.setConfigMethods("physical_display virtual_push_button")) {
2752 loge("Failed to set WPS config methods");
2754 if (!mWifiNative.setDeviceType(mPrimaryDeviceType)) {
2755 loge("Failed to set primary device type " + mPrimaryDeviceType);
2760 public boolean processMessage(Message message) {
2761 switch(message.what) {
2762 case WifiMonitor.SUP_CONNECTION_EVENT:
2763 if (DBG) log("Supplicant connection established");
2764 setWifiState(WIFI_STATE_ENABLED);
2765 mSupplicantRestartCount = 0;
2766 /* Reset the supplicant state to indicate the supplicant
2767 * state is not known at this time */
2768 mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
2769 /* Initialize data structures */
2771 mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
2772 mLastSignalLevel = -1;
2774 mWifiInfo.setMacAddress(mWifiNative.getMacAddress());
2775 mWifiConfigStore.loadAndEnableAllNetworks();
2776 initializeWpsDetails();
2778 sendSupplicantConnectionChangedBroadcast(true);
2779 transitionTo(mDriverStartedState);
2781 case WifiMonitor.SUP_DISCONNECTION_EVENT:
2782 if (++mSupplicantRestartCount <= SUPPLICANT_RESTART_TRIES) {
2783 loge("Failed to setup control channel, restart supplicant");
2784 mWifiMonitor.killSupplicant(mP2pSupported);
2785 transitionTo(mInitialState);
2786 sendMessageDelayed(CMD_START_SUPPLICANT, SUPPLICANT_RESTART_INTERVAL_MSECS);
2788 loge("Failed " + mSupplicantRestartCount +
2789 " times to start supplicant, unload driver");
2790 mSupplicantRestartCount = 0;
2791 setWifiState(WIFI_STATE_UNKNOWN);
2792 transitionTo(mInitialState);
2795 case CMD_START_SUPPLICANT:
2796 case CMD_STOP_SUPPLICANT:
2799 case CMD_START_DRIVER:
2800 case CMD_STOP_DRIVER:
2801 case CMD_SET_OPERATIONAL_MODE:
2802 case CMD_SET_COUNTRY_CODE:
2803 case CMD_SET_FREQUENCY_BAND:
2804 case CMD_START_PACKET_FILTERING:
2805 case CMD_STOP_PACKET_FILTERING:
2806 deferMessage(message);
2815 class SupplicantStartedState extends State {
2817 public void enter() {
2818 /* Wifi is available as long as we have a connection to supplicant */
2819 mNetworkInfo.setIsAvailable(true);
2821 int defaultInterval = mContext.getResources().getInteger(
2822 R.integer.config_wifi_supplicant_scan_interval);
2824 mSupplicantScanIntervalMs = Settings.Global.getLong(mContext.getContentResolver(),
2825 Settings.Global.WIFI_SUPPLICANT_SCAN_INTERVAL_MS,
2828 mWifiNative.setScanInterval((int)mSupplicantScanIntervalMs / 1000);
2831 public boolean processMessage(Message message) {
2832 switch(message.what) {
2833 case CMD_STOP_SUPPLICANT: /* Supplicant stopped by user */
2834 if (mP2pSupported) {
2835 transitionTo(mWaitForP2pDisableState);
2837 transitionTo(mSupplicantStoppingState);
2840 case WifiMonitor.SUP_DISCONNECTION_EVENT: /* Supplicant connection lost */
2841 loge("Connection lost, restart supplicant");
2842 handleSupplicantConnectionLoss();
2843 handleNetworkDisconnect();
2844 mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
2845 if (mP2pSupported) {
2846 transitionTo(mWaitForP2pDisableState);
2848 transitionTo(mInitialState);
2850 sendMessageDelayed(CMD_START_SUPPLICANT, SUPPLICANT_RESTART_INTERVAL_MSECS);
2852 case WifiMonitor.SCAN_RESULTS_EVENT:
2854 sendScanResultsAvailableBroadcast();
2855 mScanResultIsPending = false;
2857 case CMD_PING_SUPPLICANT:
2858 boolean ok = mWifiNative.ping();
2859 replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
2861 /* Cannot start soft AP while in client mode */
2863 loge("Failed to start soft AP with a running supplicant");
2864 setWifiApState(WIFI_AP_STATE_FAILED);
2866 case CMD_SET_OPERATIONAL_MODE:
2867 mOperationalMode = message.arg1;
2876 public void exit() {
2877 mNetworkInfo.setIsAvailable(false);
2881 class SupplicantStoppingState extends State {
2883 public void enter() {
2884 /* Send any reset commands to supplicant before shutting it down */
2885 handleNetworkDisconnect();
2886 if (mDhcpStateMachine != null) {
2887 mDhcpStateMachine.doQuit();
2890 if (DBG) log("stopping supplicant");
2891 mWifiMonitor.stopSupplicant();
2893 /* Send ourselves a delayed message to indicate failure after a wait time */
2894 sendMessageDelayed(obtainMessage(CMD_STOP_SUPPLICANT_FAILED,
2895 ++mSupplicantStopFailureToken, 0), SUPPLICANT_RESTART_INTERVAL_MSECS);
2896 setWifiState(WIFI_STATE_DISABLING);
2897 mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
2900 public boolean processMessage(Message message) {
2901 switch(message.what) {
2902 case WifiMonitor.SUP_CONNECTION_EVENT:
2903 loge("Supplicant connection received while stopping");
2905 case WifiMonitor.SUP_DISCONNECTION_EVENT:
2906 if (DBG) log("Supplicant connection lost");
2907 handleSupplicantConnectionLoss();
2908 transitionTo(mInitialState);
2910 case CMD_STOP_SUPPLICANT_FAILED:
2911 if (message.arg1 == mSupplicantStopFailureToken) {
2912 loge("Timed out on a supplicant stop, kill and proceed");
2913 handleSupplicantConnectionLoss();
2914 transitionTo(mInitialState);
2917 case CMD_START_SUPPLICANT:
2918 case CMD_STOP_SUPPLICANT:
2921 case CMD_START_DRIVER:
2922 case CMD_STOP_DRIVER:
2923 case CMD_SET_OPERATIONAL_MODE:
2924 case CMD_SET_COUNTRY_CODE:
2925 case CMD_SET_FREQUENCY_BAND:
2926 case CMD_START_PACKET_FILTERING:
2927 case CMD_STOP_PACKET_FILTERING:
2928 deferMessage(message);
2937 class DriverStartingState extends State {
2940 public void enter() {
2942 /* Send ourselves a delayed message to start driver a second time */
2943 sendMessageDelayed(obtainMessage(CMD_DRIVER_START_TIMED_OUT,
2944 ++mDriverStartToken, 0), DRIVER_START_TIME_OUT_MSECS);
2947 public boolean processMessage(Message message) {
2948 switch(message.what) {
2949 case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
2950 SupplicantState state = handleSupplicantStateChange(message);
2951 /* If suplicant is exiting out of INTERFACE_DISABLED state into
2952 * a state that indicates driver has started, it is ready to
2953 * receive driver commands
2955 if (SupplicantState.isDriverActive(state)) {
2956 transitionTo(mDriverStartedState);
2959 case CMD_DRIVER_START_TIMED_OUT:
2960 if (message.arg1 == mDriverStartToken) {
2962 loge("Failed to start driver after " + mTries);
2963 transitionTo(mDriverStoppedState);
2965 loge("Driver start failed, retrying");
2966 mWakeLock.acquire();
2967 mWifiNative.startDriver();
2968 mWakeLock.release();
2971 /* Send ourselves a delayed message to start driver again */
2972 sendMessageDelayed(obtainMessage(CMD_DRIVER_START_TIMED_OUT,
2973 ++mDriverStartToken, 0), DRIVER_START_TIME_OUT_MSECS);
2977 /* Queue driver commands & connection events */
2978 case CMD_START_DRIVER:
2979 case CMD_STOP_DRIVER:
2980 case WifiMonitor.NETWORK_CONNECTION_EVENT:
2981 case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
2982 case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
2983 case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
2984 case WifiMonitor.WPS_OVERLAP_EVENT:
2985 case CMD_SET_COUNTRY_CODE:
2986 case CMD_SET_FREQUENCY_BAND:
2987 case CMD_START_PACKET_FILTERING:
2988 case CMD_STOP_PACKET_FILTERING:
2989 case CMD_START_SCAN:
2990 case CMD_DISCONNECT:
2991 case CMD_REASSOCIATE:
2993 deferMessage(message);
3002 class DriverStartedState extends State {
3004 public void enter() {
3006 mInDelayedStop = false;
3007 mDelayedStopCounter++;
3008 updateBatteryWorkSource(null);
3010 * Enable bluetooth coexistence scan mode when bluetooth connection is active.
3011 * When this mode is on, some of the low-level scan parameters used by the
3012 * driver are changed to reduce interference with bluetooth
3014 mWifiNative.setBluetoothCoexistenceScanMode(mBluetoothConnectionActive);
3015 /* set country code */
3017 /* set frequency band of operation */
3019 /* initialize network state */
3020 setNetworkDetailedState(DetailedState.DISCONNECTED);
3022 /* Remove any filtering on Multicast v6 at start */
3023 mWifiNative.stopFilteringMulticastV6Packets();
3025 /* Reset Multicast v4 filtering state */
3026 if (mFilteringMulticastV4Packets.get()) {
3027 mWifiNative.startFilteringMulticastV4Packets();
3029 mWifiNative.stopFilteringMulticastV4Packets();
3032 mDhcpActive = false;
3036 if (mOperationalMode != CONNECT_MODE) {
3037 mWifiNative.disconnect();
3038 mWifiConfigStore.disableAllNetworks();
3039 if (mOperationalMode == SCAN_ONLY_WITH_WIFI_OFF_MODE) {
3040 setWifiState(WIFI_STATE_DISABLED);
3042 transitionTo(mScanModeState);
3044 /* Driver stop may have disabled networks, enable right after start */
3045 mWifiConfigStore.enableAllNetworks();
3047 if (DBG) log("Attempting to reconnect to wifi network ..");
3048 mWifiNative.reconnect();
3050 // Status pulls in the current supplicant state and network connection state
3051 // events over the monitor connection. This helps framework sync up with
3052 // current supplicant state
3053 mWifiNative.status();
3054 transitionTo(mDisconnectedState);
3057 // We may have missed screen update at boot
3058 if (mScreenBroadcastReceived.get() == false) {
3059 PowerManager powerManager = (PowerManager)mContext.getSystemService(
3060 Context.POWER_SERVICE);
3061 handleScreenStateChanged(powerManager.isScreenOn());
3063 // Set the right suspend mode settings
3064 mWifiNative.setSuspendOptimizations(mSuspendOptNeedsDisabled == 0
3065 && mUserWantsSuspendOpt.get());
3067 mWifiNative.setPowerSave(true);
3069 if (mP2pSupported) {
3070 if (mOperationalMode == CONNECT_MODE) {
3071 mWifiP2pChannel.sendMessage(WifiStateMachine.CMD_ENABLE_P2P);
3073 // P2P statemachine starts in disabled state, and is not enabled until
3074 // CMD_ENABLE_P2P is sent from here; so, nothing needs to be done to
3075 // keep it disabled.
3079 final Intent intent = new Intent(WifiManager.WIFI_SCAN_AVAILABLE);
3080 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
3081 intent.putExtra(WifiManager.EXTRA_SCAN_AVAILABLE, WIFI_STATE_ENABLED);
3082 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
3086 public boolean processMessage(Message message) {
3087 switch(message.what) {
3088 case CMD_START_SCAN:
3089 noteScanStart(message.arg1, (WorkSource) message.obj);
3090 startScanNative(WifiNative.SCAN_WITH_CONNECTION_SETUP);
3092 case CMD_SET_BATCHED_SCAN:
3093 if (recordBatchedScanSettings(message.arg1, message.arg2,
3094 (Bundle)message.obj)) {
3098 case CMD_SET_COUNTRY_CODE:
3099 String country = (String) message.obj;
3100 final boolean persist = (message.arg2 == 1);
3101 final int sequence = message.arg1;
3102 if (sequence != mCountryCodeSequence.get()) {
3103 if (DBG) log("set country code ignored due to sequence num");
3106 if (DBG) log("set country code " + country);
3108 mPersistedCountryCode = country;
3109 Settings.Global.putString(mContext.getContentResolver(),
3110 Settings.Global.WIFI_COUNTRY_CODE,
3113 country = country.toUpperCase(Locale.ROOT);
3114 if (mLastSetCountryCode == null
3115 || country.equals(mLastSetCountryCode) == false) {
3116 if (mWifiNative.setCountryCode(country)) {
3117 mLastSetCountryCode = country;
3119 loge("Failed to set country code " + country);
3122 mWifiP2pChannel.sendMessage(WifiP2pService.SET_COUNTRY_CODE, country);
3124 case CMD_SET_FREQUENCY_BAND:
3125 int band = message.arg1;
3126 if (DBG) log("set frequency band " + band);
3127 if (mWifiNative.setBand(band)) {
3128 mFrequencyBand.set(band);
3129 // flush old data - like scan results
3130 mWifiNative.bssFlush();
3131 //Fetch the latest scan results when frequency band is set
3132 startScanNative(WifiNative.SCAN_WITH_CONNECTION_SETUP);
3134 loge("Failed to set frequency band " + band);
3137 case CMD_BLUETOOTH_ADAPTER_STATE_CHANGE:
3138 mBluetoothConnectionActive = (message.arg1 !=
3139 BluetoothAdapter.STATE_DISCONNECTED);
3140 mWifiNative.setBluetoothCoexistenceScanMode(mBluetoothConnectionActive);
3142 case CMD_STOP_DRIVER:
3143 int mode = message.arg1;
3145 /* Already doing a delayed stop */
3146 if (mInDelayedStop) {
3147 if (DBG) log("Already in delayed stop");
3150 /* disconnect right now, but leave the driver running for a bit */
3151 mWifiConfigStore.disableAllNetworks();
3153 mInDelayedStop = true;
3154 mDelayedStopCounter++;
3155 if (DBG) log("Delayed stop message " + mDelayedStopCounter);
3157 /* send regular delayed shut down */
3158 Intent driverStopIntent = new Intent(ACTION_DELAYED_DRIVER_STOP, null);
3159 driverStopIntent.putExtra(DELAYED_STOP_COUNTER, mDelayedStopCounter);
3160 mDriverStopIntent = PendingIntent.getBroadcast(mContext,
3161 DRIVER_STOP_REQUEST, driverStopIntent,
3162 PendingIntent.FLAG_UPDATE_CURRENT);
3164 mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()
3165 + mDriverStopDelayMs, mDriverStopIntent);
3167 case CMD_START_DRIVER:
3168 if (mInDelayedStop) {
3169 mInDelayedStop = false;
3170 mDelayedStopCounter++;
3171 mAlarmManager.cancel(mDriverStopIntent);
3172 if (DBG) log("Delayed stop ignored due to start");
3173 if (mOperationalMode == CONNECT_MODE) {
3174 mWifiConfigStore.enableAllNetworks();
3178 case CMD_DELAYED_STOP_DRIVER:
3179 if (DBG) log("delayed stop " + message.arg1 + " " + mDelayedStopCounter);
3180 if (message.arg1 != mDelayedStopCounter) break;
3181 if (getCurrentState() != mDisconnectedState) {
3182 mWifiNative.disconnect();
3183 handleNetworkDisconnect();
3185 mWakeLock.acquire();
3186 mWifiNative.stopDriver();
3187 mWakeLock.release();
3188 if (mP2pSupported) {
3189 transitionTo(mWaitForP2pDisableState);
3191 transitionTo(mDriverStoppingState);
3194 case CMD_START_PACKET_FILTERING:
3195 if (message.arg1 == MULTICAST_V6) {
3196 mWifiNative.startFilteringMulticastV6Packets();
3197 } else if (message.arg1 == MULTICAST_V4) {
3198 mWifiNative.startFilteringMulticastV4Packets();
3200 loge("Illegal arugments to CMD_START_PACKET_FILTERING");
3203 case CMD_STOP_PACKET_FILTERING:
3204 if (message.arg1 == MULTICAST_V6) {
3205 mWifiNative.stopFilteringMulticastV6Packets();
3206 } else if (message.arg1 == MULTICAST_V4) {
3207 mWifiNative.stopFilteringMulticastV4Packets();
3209 loge("Illegal arugments to CMD_STOP_PACKET_FILTERING");
3212 case CMD_SET_SUSPEND_OPT_ENABLED:
3213 if (message.arg1 == 1) {
3214 setSuspendOptimizationsNative(SUSPEND_DUE_TO_SCREEN, true);
3215 mSuspendWakeLock.release();
3217 setSuspendOptimizationsNative(SUSPEND_DUE_TO_SCREEN, false);
3220 case CMD_SET_HIGH_PERF_MODE:
3221 if (message.arg1 == 1) {
3222 setSuspendOptimizationsNative(SUSPEND_DUE_TO_HIGH_PERF, false);
3224 setSuspendOptimizationsNative(SUSPEND_DUE_TO_HIGH_PERF, true);
3227 case CMD_ENABLE_TDLS:
3228 if (message.obj != null) {
3229 String remoteAddress = (String) message.obj;
3230 boolean enable = (message.arg1 == 1);
3231 mWifiNative.startTdls(remoteAddress, enable);
3240 public void exit() {
3242 updateBatteryWorkSource(null);
3243 mScanResults = new ArrayList<ScanResult>();
3247 final Intent intent = new Intent(WifiManager.WIFI_SCAN_AVAILABLE);
3248 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
3249 intent.putExtra(WifiManager.EXTRA_SCAN_AVAILABLE, WIFI_STATE_DISABLED);
3250 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
3251 noteScanEnd(); // wrap up any pending request.
3253 mLastSetCountryCode = null;
3257 class WaitForP2pDisableState extends State {
3258 private State mTransitionToState;
3260 public void enter() {
3261 switch (getCurrentMessage().what) {
3262 case WifiMonitor.SUP_DISCONNECTION_EVENT:
3263 mTransitionToState = mInitialState;
3265 case CMD_DELAYED_STOP_DRIVER:
3266 mTransitionToState = mDriverStoppingState;
3268 case CMD_STOP_SUPPLICANT:
3269 mTransitionToState = mSupplicantStoppingState;
3272 mTransitionToState = mDriverStoppingState;
3275 mWifiP2pChannel.sendMessage(WifiStateMachine.CMD_DISABLE_P2P_REQ);
3278 public boolean processMessage(Message message) {
3279 switch(message.what) {
3280 case WifiStateMachine.CMD_DISABLE_P2P_RSP:
3281 transitionTo(mTransitionToState);
3283 /* Defer wifi start/shut and driver commands */
3284 case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
3285 case CMD_START_SUPPLICANT:
3286 case CMD_STOP_SUPPLICANT:
3289 case CMD_START_DRIVER:
3290 case CMD_STOP_DRIVER:
3291 case CMD_SET_OPERATIONAL_MODE:
3292 case CMD_SET_COUNTRY_CODE:
3293 case CMD_SET_FREQUENCY_BAND:
3294 case CMD_START_PACKET_FILTERING:
3295 case CMD_STOP_PACKET_FILTERING:
3296 case CMD_START_SCAN:
3297 case CMD_DISCONNECT:
3298 case CMD_REASSOCIATE:
3300 deferMessage(message);
3309 class DriverStoppingState extends State {
3311 public boolean processMessage(Message message) {
3312 switch(message.what) {
3313 case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
3314 SupplicantState state = handleSupplicantStateChange(message);
3315 if (state == SupplicantState.INTERFACE_DISABLED) {
3316 transitionTo(mDriverStoppedState);
3319 /* Queue driver commands */
3320 case CMD_START_DRIVER:
3321 case CMD_STOP_DRIVER:
3322 case CMD_SET_COUNTRY_CODE:
3323 case CMD_SET_FREQUENCY_BAND:
3324 case CMD_START_PACKET_FILTERING:
3325 case CMD_STOP_PACKET_FILTERING:
3326 case CMD_START_SCAN:
3327 case CMD_DISCONNECT:
3328 case CMD_REASSOCIATE:
3330 deferMessage(message);
3339 class DriverStoppedState extends State {
3341 public boolean processMessage(Message message) {
3342 switch (message.what) {
3343 case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
3344 StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
3345 SupplicantState state = stateChangeResult.state;
3346 // A WEXT bug means that we can be back to driver started state
3348 if (SupplicantState.isDriverActive(state)) {
3349 transitionTo(mDriverStartedState);
3352 case CMD_START_DRIVER:
3353 mWakeLock.acquire();
3354 mWifiNative.startDriver();
3355 mWakeLock.release();
3356 transitionTo(mDriverStartingState);
3365 class ScanModeState extends State {
3366 private int mLastOperationMode;
3368 public void enter() {
3369 mLastOperationMode = mOperationalMode;
3372 public boolean processMessage(Message message) {
3373 switch(message.what) {
3374 case CMD_SET_OPERATIONAL_MODE:
3375 if (message.arg1 == CONNECT_MODE) {
3377 if (mLastOperationMode == SCAN_ONLY_WITH_WIFI_OFF_MODE) {
3378 setWifiState(WIFI_STATE_ENABLED);
3379 // Load and re-enable networks when going back to enabled state
3380 // This is essential for networks to show up after restore
3381 mWifiConfigStore.loadAndEnableAllNetworks();
3382 mWifiP2pChannel.sendMessage(CMD_ENABLE_P2P);
3384 mWifiConfigStore.enableAllNetworks();
3387 mWifiNative.reconnect();
3389 mOperationalMode = CONNECT_MODE;
3390 transitionTo(mDisconnectedState);
3396 // Handle scan. All the connection related commands are
3397 // handled only in ConnectModeState
3398 case CMD_START_SCAN:
3399 noteScanStart(message.arg1, (WorkSource) message.obj);
3400 startScanNative(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP);
3409 class ConnectModeState extends State {
3411 public boolean processMessage(Message message) {
3412 WifiConfiguration config;
3414 switch(message.what) {
3415 case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
3416 mSupplicantStateTracker.sendMessage(WifiMonitor.ASSOCIATION_REJECTION_EVENT);
3418 case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
3419 mSupplicantStateTracker.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT);
3421 case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
3422 SupplicantState state = handleSupplicantStateChange(message);
3423 // A driver/firmware hang can now put the interface in a down state.
3424 // We detect the interface going down and recover from it
3425 if (!SupplicantState.isDriverActive(state)) {
3426 if (mNetworkInfo.getState() != NetworkInfo.State.DISCONNECTED) {
3427 handleNetworkDisconnect();
3429 log("Detected an interface down, restart driver");
3430 transitionTo(mDriverStoppedState);
3431 sendMessage(CMD_START_DRIVER);
3435 // Supplicant can fail to report a NETWORK_DISCONNECTION_EVENT
3436 // when authentication times out after a successful connection,
3437 // we can figure this from the supplicant state. If supplicant
3438 // state is DISCONNECTED, but the mNetworkInfo says we are not
3439 // disconnected, we need to handle a disconnection
3440 if (state == SupplicantState.DISCONNECTED &&
3441 mNetworkInfo.getState() != NetworkInfo.State.DISCONNECTED) {
3442 if (DBG) log("Missed CTRL-EVENT-DISCONNECTED, disconnect");
3443 handleNetworkDisconnect();
3444 transitionTo(mDisconnectedState);
3447 case WifiP2pService.DISCONNECT_WIFI_REQUEST:
3448 if (message.arg1 == 1) {
3449 mWifiNative.disconnect();
3450 mTemporarilyDisconnectWifi = true;
3452 mWifiNative.reconnect();
3453 mTemporarilyDisconnectWifi = false;
3456 case CMD_ADD_OR_UPDATE_NETWORK:
3457 config = (WifiConfiguration) message.obj;
3458 replyToMessage(message, CMD_ADD_OR_UPDATE_NETWORK,
3459 mWifiConfigStore.addOrUpdateNetwork(config));
3461 case CMD_REMOVE_NETWORK:
3462 ok = mWifiConfigStore.removeNetwork(message.arg1);
3463 replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
3465 case CMD_ENABLE_NETWORK:
3466 ok = mWifiConfigStore.enableNetwork(message.arg1, message.arg2 == 1);
3467 replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
3469 case CMD_ENABLE_ALL_NETWORKS:
3470 long time = android.os.SystemClock.elapsedRealtime();
3471 if (time - mLastEnableAllNetworksTime > MIN_INTERVAL_ENABLE_ALL_NETWORKS_MS) {
3472 mWifiConfigStore.enableAllNetworks();
3473 mLastEnableAllNetworksTime = time;
3476 case WifiManager.DISABLE_NETWORK:
3477 if (mWifiConfigStore.disableNetwork(message.arg1,
3478 WifiConfiguration.DISABLED_UNKNOWN_REASON) == true) {
3479 replyToMessage(message, WifiManager.DISABLE_NETWORK_SUCCEEDED);
3481 replyToMessage(message, WifiManager.DISABLE_NETWORK_FAILED,
3485 case CMD_BLACKLIST_NETWORK:
3486 mWifiNative.addToBlacklist((String)message.obj);
3488 case CMD_CLEAR_BLACKLIST:
3489 mWifiNative.clearBlacklist();
3491 case CMD_SAVE_CONFIG:
3492 ok = mWifiConfigStore.saveConfig();
3493 replyToMessage(message, CMD_SAVE_CONFIG, ok ? SUCCESS : FAILURE);
3495 // Inform the backup manager about a data change
3496 IBackupManager ibm = IBackupManager.Stub.asInterface(
3497 ServiceManager.getService(Context.BACKUP_SERVICE));
3500 ibm.dataChanged("com.android.providers.settings");
3501 } catch (Exception e) {
3506 case CMD_GET_CONFIGURED_NETWORKS:
3507 replyToMessage(message, message.what,
3508 mWifiConfigStore.getConfiguredNetworks());
3510 /* Do a redundant disconnect without transition */
3511 case CMD_DISCONNECT:
3512 mWifiNative.disconnect();
3515 mWifiNative.reconnect();
3517 case CMD_REASSOCIATE:
3518 mWifiNative.reassociate();
3520 case CMD_RELOAD_TLS_AND_RECONNECT:
3521 if (mWifiConfigStore.needsUnlockedKeyStore()) {
3522 logd("Reconnecting to give a chance to un-connected TLS networks");
3523 mWifiNative.disconnect();
3524 mWifiNative.reconnect();
3527 case WifiManager.CONNECT_NETWORK:
3528 /* The connect message can contain a network id passed as arg1 on message or
3529 * or a config passed as obj on message.
3530 * For a new network, a config is passed to create and connect.
3531 * For an existing network, a network id is passed
3533 int netId = message.arg1;
3534 config = (WifiConfiguration) message.obj;
3536 /* Save the network config */
3537 if (config != null) {
3538 NetworkUpdateResult result = mWifiConfigStore.saveNetwork(config);
3539 netId = result.getNetworkId();
3542 if (mWifiConfigStore.selectNetwork(netId) &&
3543 mWifiNative.reconnect()) {
3544 /* The state tracker handles enabling networks upon completion/failure */
3545 mSupplicantStateTracker.sendMessage(WifiManager.CONNECT_NETWORK);
3546 replyToMessage(message, WifiManager.CONNECT_NETWORK_SUCCEEDED);
3547 /* Expect a disconnection from the old connection */
3548 transitionTo(mDisconnectingState);
3550 loge("Failed to connect config: " + config + " netId: " + netId);
3551 replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
3556 case WifiManager.SAVE_NETWORK:
3557 config = (WifiConfiguration) message.obj;
3558 NetworkUpdateResult result = mWifiConfigStore.saveNetwork(config);
3559 if (result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID) {
3560 replyToMessage(message, WifiManager.SAVE_NETWORK_SUCCEEDED);
3562 loge("Failed to save network");
3563 replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED,
3567 case WifiManager.FORGET_NETWORK:
3568 if (mWifiConfigStore.forgetNetwork(message.arg1)) {
3569 replyToMessage(message, WifiManager.FORGET_NETWORK_SUCCEEDED);
3571 loge("Failed to forget network");
3572 replyToMessage(message, WifiManager.FORGET_NETWORK_FAILED,
3576 case WifiManager.START_WPS:
3577 WpsInfo wpsInfo = (WpsInfo) message.obj;
3578 WpsResult wpsResult;
3579 switch (wpsInfo.setup) {
3581 wpsResult = mWifiConfigStore.startWpsPbc(wpsInfo);
3583 case WpsInfo.KEYPAD:
3584 wpsResult = mWifiConfigStore.startWpsWithPinFromAccessPoint(wpsInfo);
3586 case WpsInfo.DISPLAY:
3587 wpsResult = mWifiConfigStore.startWpsWithPinFromDevice(wpsInfo);
3590 wpsResult = new WpsResult(Status.FAILURE);
3591 loge("Invalid setup for WPS");
3594 if (wpsResult.status == Status.SUCCESS) {
3595 replyToMessage(message, WifiManager.START_WPS_SUCCEEDED, wpsResult);
3596 transitionTo(mWpsRunningState);
3598 loge("Failed to start WPS with config " + wpsInfo.toString());
3599 replyToMessage(message, WifiManager.WPS_FAILED, WifiManager.ERROR);
3602 case WifiMonitor.NETWORK_CONNECTION_EVENT:
3603 if (DBG) log("Network connection established");
3604 mLastNetworkId = message.arg1;
3605 mLastBssid = (String) message.obj;
3607 mWifiInfo.setBSSID(mLastBssid);
3608 mWifiInfo.setNetworkId(mLastNetworkId);
3609 /* send event to CM & network change broadcast */
3610 setNetworkDetailedState(DetailedState.OBTAINING_IPADDR);
3611 sendNetworkStateChangeBroadcast(mLastBssid);
3612 transitionTo(mObtainingIpState);
3614 case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
3615 if (DBG) log("Network connection lost");
3616 handleNetworkDisconnect();
3617 transitionTo(mDisconnectedState);
3626 class L2ConnectedState extends State {
3628 public void enter() {
3630 if (mEnableRssiPolling) {
3631 sendMessage(CMD_RSSI_POLL, mRssiPollToken, 0);
3636 public void exit() {
3637 handleNetworkDisconnect();
3641 public boolean processMessage(Message message) {
3642 switch (message.what) {
3643 case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
3644 handlePreDhcpSetup();
3646 case DhcpStateMachine.CMD_POST_DHCP_ACTION:
3647 handlePostDhcpSetup();
3648 if (message.arg1 == DhcpStateMachine.DHCP_SUCCESS) {
3649 if (DBG) log("DHCP successful");
3650 handleSuccessfulIpConfiguration((DhcpResults) message.obj);
3651 transitionTo(mVerifyingLinkState);
3652 } else if (message.arg1 == DhcpStateMachine.DHCP_FAILURE) {
3653 if (DBG) log("DHCP failed");
3654 handleFailedIpConfiguration();
3655 transitionTo(mDisconnectingState);
3658 case CMD_DISCONNECT:
3659 mWifiNative.disconnect();
3660 transitionTo(mDisconnectingState);
3662 case WifiP2pService.DISCONNECT_WIFI_REQUEST:
3663 if (message.arg1 == 1) {
3664 mWifiNative.disconnect();
3665 mTemporarilyDisconnectWifi = true;
3666 transitionTo(mDisconnectingState);
3669 case CMD_SET_OPERATIONAL_MODE:
3670 if (message.arg1 != CONNECT_MODE) {
3671 sendMessage(CMD_DISCONNECT);
3672 deferMessage(message);
3675 case CMD_START_SCAN:
3676 /* Do not attempt to connect when we are already connected */
3677 noteScanStart(message.arg1, (WorkSource) message.obj);
3678 startScanNative(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP);
3680 /* Ignore connection to same network */
3681 case WifiManager.CONNECT_NETWORK:
3682 int netId = message.arg1;
3683 if (mWifiInfo.getNetworkId() == netId) {
3687 case WifiManager.SAVE_NETWORK:
3688 WifiConfiguration config = (WifiConfiguration) message.obj;
3689 NetworkUpdateResult result = mWifiConfigStore.saveNetwork(config);
3690 if (mWifiInfo.getNetworkId() == result.getNetworkId()) {
3691 if (result.hasIpChanged()) {
3692 log("Reconfiguring IP on connection");
3693 transitionTo(mObtainingIpState);
3695 if (result.hasProxyChanged()) {
3696 log("Reconfiguring proxy on connection");
3697 updateLinkProperties();
3701 if (result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID) {
3702 replyToMessage(message, WifiManager.SAVE_NETWORK_SUCCEEDED);
3704 loge("Failed to save network");
3705 replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED,
3710 case WifiMonitor.NETWORK_CONNECTION_EVENT:
3713 if (message.arg1 == mRssiPollToken) {
3714 // Get Info and continue polling
3715 fetchRssiAndLinkSpeedNative();
3716 sendMessageDelayed(obtainMessage(CMD_RSSI_POLL,
3717 mRssiPollToken, 0), POLL_RSSI_INTERVAL_MSECS);
3719 // Polling has completed
3722 case CMD_ENABLE_RSSI_POLL:
3723 mEnableRssiPolling = (message.arg1 == 1);
3725 if (mEnableRssiPolling) {
3727 fetchRssiAndLinkSpeedNative();
3728 sendMessageDelayed(obtainMessage(CMD_RSSI_POLL,
3729 mRssiPollToken, 0), POLL_RSSI_INTERVAL_MSECS);
3732 case WifiManager.RSSI_PKTCNT_FETCH:
3733 RssiPacketCountInfo info = new RssiPacketCountInfo();
3734 fetchRssiAndLinkSpeedNative();
3735 info.rssi = mWifiInfo.getRssi();
3736 fetchPktcntNative(info);
3737 replyToMessage(message, WifiManager.RSSI_PKTCNT_FETCH_SUCCEEDED, info);
3747 class ObtainingIpState extends State {
3749 public void enter() {
3750 if (!mWifiConfigStore.isUsingStaticIp(mLastNetworkId)) {
3751 // TODO: If we're switching between static IP configuration and DHCP, remove the
3752 // static configuration first.
3755 // stop any running dhcp before assigning static IP
3757 DhcpResults dhcpResults = new DhcpResults(
3758 mWifiConfigStore.getLinkProperties(mLastNetworkId));
3759 InterfaceConfiguration ifcg = new InterfaceConfiguration();
3760 Iterator<LinkAddress> addrs =
3761 dhcpResults.linkProperties.getLinkAddresses().iterator();
3762 if (!addrs.hasNext()) {
3763 loge("Static IP lacks address");
3764 sendMessage(CMD_STATIC_IP_FAILURE);
3766 ifcg.setLinkAddress(addrs.next());
3767 ifcg.setInterfaceUp();
3769 mNwService.setInterfaceConfig(mInterfaceName, ifcg);
3770 if (DBG) log("Static IP configuration succeeded");
3771 sendMessage(CMD_STATIC_IP_SUCCESS, dhcpResults);
3772 } catch (RemoteException re) {
3773 loge("Static IP configuration failed: " + re);
3774 sendMessage(CMD_STATIC_IP_FAILURE);
3775 } catch (IllegalStateException e) {
3776 loge("Static IP configuration failed: " + e);
3777 sendMessage(CMD_STATIC_IP_FAILURE);
3783 public boolean processMessage(Message message) {
3784 if (DBG) log(getName() + message.toString() + "\n");
3785 switch(message.what) {
3786 case CMD_STATIC_IP_SUCCESS:
3787 handleSuccessfulIpConfiguration((DhcpResults) message.obj);
3788 transitionTo(mVerifyingLinkState);
3790 case CMD_STATIC_IP_FAILURE:
3791 handleFailedIpConfiguration();
3792 transitionTo(mDisconnectingState);
3794 case WifiManager.SAVE_NETWORK:
3795 deferMessage(message);
3797 /* Defer any power mode changes since we must keep active power mode at DHCP */
3798 case CMD_SET_HIGH_PERF_MODE:
3799 deferMessage(message);
3801 /* Defer scan request since we should not switch to other channels at DHCP */
3802 case CMD_START_SCAN:
3803 deferMessage(message);
3812 class VerifyingLinkState extends State {
3814 public void enter() {
3815 log(getName() + " enter");
3816 setNetworkDetailedState(DetailedState.VERIFYING_POOR_LINK);
3817 mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.VERIFYING_POOR_LINK);
3818 sendNetworkStateChangeBroadcast(mLastBssid);
3821 public boolean processMessage(Message message) {
3822 switch (message.what) {
3823 case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
3825 log(getName() + " POOR_LINK_DETECTED: no transition");
3827 case WifiWatchdogStateMachine.GOOD_LINK_DETECTED:
3828 log(getName() + " GOOD_LINK_DETECTED: transition to captive portal check");
3829 transitionTo(mCaptivePortalCheckState);
3832 if (DBG) log(getName() + " what=" + message.what + " NOT_HANDLED");
3839 class CaptivePortalCheckState extends State {
3841 public void enter() {
3842 log(getName() + " enter");
3843 setNetworkDetailedState(DetailedState.CAPTIVE_PORTAL_CHECK);
3844 mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.CAPTIVE_PORTAL_CHECK);
3845 sendNetworkStateChangeBroadcast(mLastBssid);
3848 public boolean processMessage(Message message) {
3849 switch (message.what) {
3850 case CMD_CAPTIVE_CHECK_COMPLETE:
3851 log(getName() + " CMD_CAPTIVE_CHECK_COMPLETE");
3853 mNwService.enableIpv6(mInterfaceName);
3854 } catch (RemoteException re) {
3855 loge("Failed to enable IPv6: " + re);
3856 } catch (IllegalStateException e) {
3857 loge("Failed to enable IPv6: " + e);
3859 setNetworkDetailedState(DetailedState.CONNECTED);
3860 mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.CONNECTED);
3861 sendNetworkStateChangeBroadcast(mLastBssid);
3862 transitionTo(mConnectedState);
3871 class ConnectedState extends State {
3873 public boolean processMessage(Message message) {
3874 switch (message.what) {
3875 case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
3876 if (DBG) log("Watchdog reports poor link");
3878 mNwService.disableIpv6(mInterfaceName);
3879 } catch (RemoteException re) {
3880 loge("Failed to disable IPv6: " + re);
3881 } catch (IllegalStateException e) {
3882 loge("Failed to disable IPv6: " + e);
3884 /* Report a disconnect */
3885 setNetworkDetailedState(DetailedState.DISCONNECTED);
3886 mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.DISCONNECTED);
3887 sendNetworkStateChangeBroadcast(mLastBssid);
3889 transitionTo(mVerifyingLinkState);
3897 public void exit() {
3898 /* Request a CS wakelock during transition to mobile */
3899 checkAndSetConnectivityInstance();
3900 mCm.requestNetworkTransitionWakelock(getName());
3904 class DisconnectingState extends State {
3906 public boolean processMessage(Message message) {
3907 switch (message.what) {
3908 case CMD_SET_OPERATIONAL_MODE:
3909 if (message.arg1 != CONNECT_MODE) {
3910 deferMessage(message);
3913 case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
3914 /* If we get a SUPPLICANT_STATE_CHANGE_EVENT before NETWORK_DISCONNECTION_EVENT
3915 * we have missed the network disconnection, transition to mDisconnectedState
3916 * and handle the rest of the events there
3918 deferMessage(message);
3919 handleNetworkDisconnect();
3920 transitionTo(mDisconnectedState);
3929 class DisconnectedState extends State {
3930 private boolean mAlarmEnabled = false;
3931 /* This is set from the overlay config file or from a secure setting.
3932 * A value of 0 disables scanning in the framework.
3934 private long mFrameworkScanIntervalMs;
3936 private void setScanAlarm(boolean enabled) {
3937 if (enabled == mAlarmEnabled) return;
3939 if (mFrameworkScanIntervalMs > 0) {
3940 mAlarmManager.setRepeating(AlarmManager.RTC_WAKEUP,
3941 System.currentTimeMillis() + mFrameworkScanIntervalMs,
3942 mFrameworkScanIntervalMs,
3944 mAlarmEnabled = true;
3947 mAlarmManager.cancel(mScanIntent);
3948 mAlarmEnabled = false;
3953 public void enter() {
3954 // We dont scan frequently if this is a temporary disconnect
3956 if (mTemporarilyDisconnectWifi) {
3957 mWifiP2pChannel.sendMessage(WifiP2pService.DISCONNECT_WIFI_RESPONSE);
3961 mFrameworkScanIntervalMs = Settings.Global.getLong(mContext.getContentResolver(),
3962 Settings.Global.WIFI_FRAMEWORK_SCAN_INTERVAL_MS,
3963 mDefaultFrameworkScanIntervalMs);
3965 * We initiate background scanning if it is enabled, otherwise we
3966 * initiate an infrequent scan that wakes up the device to ensure
3967 * a user connects to an access point on the move
3969 if (mEnableBackgroundScan) {
3970 /* If a regular scan result is pending, do not initiate background
3971 * scan until the scan results are returned. This is needed because
3972 * initiating a background scan will cancel the regular scan and
3973 * scan results will not be returned until background scanning is
3976 if (!mScanResultIsPending) {
3977 mWifiNative.enableBackgroundScan(true);
3984 * If we have no networks saved, the supplicant stops doing the periodic scan.
3985 * The scans are useful to notify the user of the presence of an open network.
3986 * Note that these are not wake up scans.
3988 if (!mP2pConnected.get() && mWifiConfigStore.getConfiguredNetworks().size() == 0) {
3989 sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,
3990 ++mPeriodicScanToken, 0), mSupplicantScanIntervalMs);
3994 public boolean processMessage(Message message) {
3995 boolean ret = HANDLED;
3996 switch (message.what) {
3997 case CMD_NO_NETWORKS_PERIODIC_SCAN:
3998 if (mP2pConnected.get()) break;
3999 if (message.arg1 == mPeriodicScanToken &&
4000 mWifiConfigStore.getConfiguredNetworks().size() == 0) {
4001 sendMessage(CMD_START_SCAN, UNKNOWN_SCAN_SOURCE, 0, (WorkSource) null);
4002 sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,
4003 ++mPeriodicScanToken, 0), mSupplicantScanIntervalMs);
4006 case WifiManager.FORGET_NETWORK:
4007 case CMD_REMOVE_NETWORK:
4008 // Set up a delayed message here. After the forget/remove is handled
4009 // the handled delayed message will determine if there is a need to
4010 // scan and continue
4011 sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,
4012 ++mPeriodicScanToken, 0), mSupplicantScanIntervalMs);
4015 case CMD_SET_OPERATIONAL_MODE:
4016 if (message.arg1 != CONNECT_MODE) {
4017 mOperationalMode = message.arg1;
4019 mWifiConfigStore.disableAllNetworks();
4020 if (mOperationalMode == SCAN_ONLY_WITH_WIFI_OFF_MODE) {
4021 mWifiP2pChannel.sendMessage(CMD_DISABLE_P2P_REQ);
4022 setWifiState(WIFI_STATE_DISABLED);
4025 transitionTo(mScanModeState);
4028 case CMD_ENABLE_BACKGROUND_SCAN:
4029 mEnableBackgroundScan = (message.arg1 == 1);
4030 if (mEnableBackgroundScan) {
4031 mWifiNative.enableBackgroundScan(true);
4032 setScanAlarm(false);
4034 mWifiNative.enableBackgroundScan(false);
4038 /* Ignore network disconnect */
4039 case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
4041 case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
4042 StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
4043 setNetworkDetailedState(WifiInfo.getDetailedStateOf(stateChangeResult.state));
4044 /* ConnectModeState does the rest of the handling */
4047 case CMD_START_SCAN:
4048 /* Disable background scan temporarily during a regular scan */
4049 if (mEnableBackgroundScan) {
4050 mWifiNative.enableBackgroundScan(false);
4052 /* Handled in parent state */
4055 case WifiMonitor.SCAN_RESULTS_EVENT:
4056 /* Re-enable background scan when a pending scan result is received */
4057 if (mEnableBackgroundScan && mScanResultIsPending) {
4058 mWifiNative.enableBackgroundScan(true);
4060 /* Handled in parent state */
4063 case WifiP2pService.P2P_CONNECTION_CHANGED:
4064 NetworkInfo info = (NetworkInfo) message.obj;
4065 mP2pConnected.set(info.isConnected());
4066 if (mP2pConnected.get()) {
4067 int defaultInterval = mContext.getResources().getInteger(
4068 R.integer.config_wifi_scan_interval_p2p_connected);
4069 long scanIntervalMs = Settings.Global.getLong(mContext.getContentResolver(),
4070 Settings.Global.WIFI_SCAN_INTERVAL_WHEN_P2P_CONNECTED_MS,
4072 mWifiNative.setScanInterval((int) scanIntervalMs/1000);
4073 } else if (mWifiConfigStore.getConfiguredNetworks().size() == 0) {
4074 if (DBG) log("Turn on scanning after p2p disconnected");
4075 sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,
4076 ++mPeriodicScanToken, 0), mSupplicantScanIntervalMs);
4079 case CMD_REASSOCIATE:
4080 if (mTemporarilyDisconnectWifi) {
4081 // Drop a third party reconnect/reassociate if STA is
4082 // temporarily disconnected for p2p
4085 // ConnectModeState handles it
4096 public void exit() {
4097 /* No need for a background scan upon exit from a disconnected state */
4098 if (mEnableBackgroundScan) {
4099 mWifiNative.enableBackgroundScan(false);
4101 setScanAlarm(false);
4105 class WpsRunningState extends State {
4106 //Tracks the source to provide a reply
4107 private Message mSourceMessage;
4109 public void enter() {
4110 mSourceMessage = Message.obtain(getCurrentMessage());
4113 public boolean processMessage(Message message) {
4114 switch (message.what) {
4115 case WifiMonitor.WPS_SUCCESS_EVENT:
4116 // Ignore intermediate success, wait for full connection
4118 case WifiMonitor.NETWORK_CONNECTION_EVENT:
4119 replyToMessage(mSourceMessage, WifiManager.WPS_COMPLETED);
4120 mSourceMessage.recycle();
4121 mSourceMessage = null;
4122 deferMessage(message);
4123 transitionTo(mDisconnectedState);
4125 case WifiMonitor.WPS_OVERLAP_EVENT:
4126 replyToMessage(mSourceMessage, WifiManager.WPS_FAILED,
4127 WifiManager.WPS_OVERLAP_ERROR);
4128 mSourceMessage.recycle();
4129 mSourceMessage = null;
4130 transitionTo(mDisconnectedState);
4132 case WifiMonitor.WPS_FAIL_EVENT:
4133 //arg1 has the reason for the failure
4134 replyToMessage(mSourceMessage, WifiManager.WPS_FAILED, message.arg1);
4135 mSourceMessage.recycle();
4136 mSourceMessage = null;
4137 transitionTo(mDisconnectedState);
4139 case WifiMonitor.WPS_TIMEOUT_EVENT:
4140 replyToMessage(mSourceMessage, WifiManager.WPS_FAILED,
4141 WifiManager.WPS_TIMED_OUT);
4142 mSourceMessage.recycle();
4143 mSourceMessage = null;
4144 transitionTo(mDisconnectedState);
4146 case WifiManager.START_WPS:
4147 replyToMessage(message, WifiManager.WPS_FAILED, WifiManager.IN_PROGRESS);
4149 case WifiManager.CANCEL_WPS:
4150 if (mWifiNative.cancelWps()) {
4151 replyToMessage(message, WifiManager.CANCEL_WPS_SUCCEDED);
4153 replyToMessage(message, WifiManager.CANCEL_WPS_FAILED, WifiManager.ERROR);
4155 transitionTo(mDisconnectedState);
4157 /* Defer all commands that can cause connections to a different network
4158 * or put the state machine out of connect mode
4160 case CMD_STOP_DRIVER:
4161 case CMD_SET_OPERATIONAL_MODE:
4162 case WifiManager.CONNECT_NETWORK:
4163 case CMD_ENABLE_NETWORK:
4165 case CMD_REASSOCIATE:
4166 deferMessage(message);
4168 case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
4169 if (DBG) log("Network connection lost");
4170 handleNetworkDisconnect();
4172 case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
4173 if (DBG) log("Ignore Assoc reject event during WPS Connection");
4175 case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
4176 // Disregard auth failure events during WPS connection. The
4177 // EAP sequence is retried several times, and there might be
4178 // failures (especially for wps pin). We will get a WPS_XXX
4179 // event at the end of the sequence anyway.
4180 if (DBG) log("Ignore auth failure during WPS connection");
4182 case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
4183 //Throw away supplicant state changes when WPS is running.
4184 //We will start getting supplicant state changes once we get
4185 //a WPS success or failure
4194 public void exit() {
4195 mWifiConfigStore.enableAllNetworks();
4196 mWifiConfigStore.loadConfiguredNetworks();
4200 class SoftApStartingState extends State {
4202 public void enter() {
4203 final Message message = getCurrentMessage();
4204 if (message.what == CMD_START_AP) {
4205 final WifiConfiguration config = (WifiConfiguration) message.obj;
4207 if (config == null) {
4208 mWifiApConfigChannel.sendMessage(CMD_REQUEST_AP_CONFIG);
4210 mWifiApConfigChannel.sendMessage(CMD_SET_AP_CONFIG, config);
4211 startSoftApWithConfig(config);
4214 throw new RuntimeException("Illegal transition to SoftApStartingState: " + message);
4218 public boolean processMessage(Message message) {
4219 switch(message.what) {
4220 case CMD_START_SUPPLICANT:
4221 case CMD_STOP_SUPPLICANT:
4224 case CMD_START_DRIVER:
4225 case CMD_STOP_DRIVER:
4226 case CMD_SET_OPERATIONAL_MODE:
4227 case CMD_SET_COUNTRY_CODE:
4228 case CMD_SET_FREQUENCY_BAND:
4229 case CMD_START_PACKET_FILTERING:
4230 case CMD_STOP_PACKET_FILTERING:
4231 case CMD_TETHER_STATE_CHANGE:
4232 deferMessage(message);
4234 case WifiStateMachine.CMD_RESPONSE_AP_CONFIG:
4235 WifiConfiguration config = (WifiConfiguration) message.obj;
4236 if (config != null) {
4237 startSoftApWithConfig(config);
4239 loge("Softap config is null!");
4240 sendMessage(CMD_START_AP_FAILURE);
4243 case CMD_START_AP_SUCCESS:
4244 setWifiApState(WIFI_AP_STATE_ENABLED);
4245 transitionTo(mSoftApStartedState);
4247 case CMD_START_AP_FAILURE:
4248 setWifiApState(WIFI_AP_STATE_FAILED);
4249 transitionTo(mInitialState);
4258 class SoftApStartedState extends State {
4260 public boolean processMessage(Message message) {
4261 switch(message.what) {
4263 if (DBG) log("Stopping Soft AP");
4264 /* We have not tethered at this point, so we just shutdown soft Ap */
4266 mNwService.stopAccessPoint(mInterfaceName);
4267 } catch(Exception e) {
4268 loge("Exception in stopAccessPoint()");
4270 setWifiApState(WIFI_AP_STATE_DISABLED);
4271 transitionTo(mInitialState);
4274 // Ignore a start on a running access point
4276 /* Fail client mode operation when soft AP is enabled */
4277 case CMD_START_SUPPLICANT:
4278 loge("Cannot start supplicant with a running soft AP");
4279 setWifiState(WIFI_STATE_UNKNOWN);
4281 case CMD_TETHER_STATE_CHANGE:
4282 TetherStateChange stateChange = (TetherStateChange) message.obj;
4283 if (startTethering(stateChange.available)) {
4284 transitionTo(mTetheringState);
4294 class TetheringState extends State {
4296 public void enter() {
4297 /* Send ourselves a delayed message to shut down if tethering fails to notify */
4298 sendMessageDelayed(obtainMessage(CMD_TETHER_NOTIFICATION_TIMED_OUT,
4299 ++mTetherToken, 0), TETHER_NOTIFICATION_TIME_OUT_MSECS);
4302 public boolean processMessage(Message message) {
4303 switch(message.what) {
4304 case CMD_TETHER_STATE_CHANGE:
4305 TetherStateChange stateChange = (TetherStateChange) message.obj;
4306 if (isWifiTethered(stateChange.active)) {
4307 transitionTo(mTetheredState);
4310 case CMD_TETHER_NOTIFICATION_TIMED_OUT:
4311 if (message.arg1 == mTetherToken) {
4312 loge("Failed to get tether update, shutdown soft access point");
4313 transitionTo(mSoftApStartedState);
4314 // Needs to be first thing handled
4315 sendMessageAtFrontOfQueue(CMD_STOP_AP);
4318 case CMD_START_SUPPLICANT:
4319 case CMD_STOP_SUPPLICANT:
4322 case CMD_START_DRIVER:
4323 case CMD_STOP_DRIVER:
4324 case CMD_SET_OPERATIONAL_MODE:
4325 case CMD_SET_COUNTRY_CODE:
4326 case CMD_SET_FREQUENCY_BAND:
4327 case CMD_START_PACKET_FILTERING:
4328 case CMD_STOP_PACKET_FILTERING:
4329 deferMessage(message);
4338 class TetheredState extends State {
4340 public boolean processMessage(Message message) {
4341 switch(message.what) {
4342 case CMD_TETHER_STATE_CHANGE:
4343 TetherStateChange stateChange = (TetherStateChange) message.obj;
4344 if (!isWifiTethered(stateChange.active)) {
4345 loge("Tethering reports wifi as untethered!, shut down soft Ap");
4346 setHostApRunning(null, false);
4347 setHostApRunning(null, true);
4351 if (DBG) log("Untethering before stopping AP");
4352 setWifiApState(WIFI_AP_STATE_DISABLING);
4354 transitionTo(mUntetheringState);
4355 // More work to do after untethering
4356 deferMessage(message);
4365 class UntetheringState extends State {
4367 public void enter() {
4368 /* Send ourselves a delayed message to shut down if tethering fails to notify */
4369 sendMessageDelayed(obtainMessage(CMD_TETHER_NOTIFICATION_TIMED_OUT,
4370 ++mTetherToken, 0), TETHER_NOTIFICATION_TIME_OUT_MSECS);
4374 public boolean processMessage(Message message) {
4375 switch(message.what) {
4376 case CMD_TETHER_STATE_CHANGE:
4377 TetherStateChange stateChange = (TetherStateChange) message.obj;
4379 /* Wait till wifi is untethered */
4380 if (isWifiTethered(stateChange.active)) break;
4382 transitionTo(mSoftApStartedState);
4384 case CMD_TETHER_NOTIFICATION_TIMED_OUT:
4385 if (message.arg1 == mTetherToken) {
4386 loge("Failed to get tether update, force stop access point");
4387 transitionTo(mSoftApStartedState);
4390 case CMD_START_SUPPLICANT:
4391 case CMD_STOP_SUPPLICANT:
4394 case CMD_START_DRIVER:
4395 case CMD_STOP_DRIVER:
4396 case CMD_SET_OPERATIONAL_MODE:
4397 case CMD_SET_COUNTRY_CODE:
4398 case CMD_SET_FREQUENCY_BAND:
4399 case CMD_START_PACKET_FILTERING:
4400 case CMD_STOP_PACKET_FILTERING:
4401 deferMessage(message);
4410 //State machine initiated requests can have replyTo set to null indicating
4411 //there are no recepients, we ignore those reply actions
4412 private void replyToMessage(Message msg, int what) {
4413 if (msg.replyTo == null) return;
4414 Message dstMsg = obtainMessageWithArg2(msg);
4416 mReplyChannel.replyToMessage(msg, dstMsg);
4419 private void replyToMessage(Message msg, int what, int arg1) {
4420 if (msg.replyTo == null) return;
4421 Message dstMsg = obtainMessageWithArg2(msg);
4424 mReplyChannel.replyToMessage(msg, dstMsg);
4427 private void replyToMessage(Message msg, int what, Object obj) {
4428 if (msg.replyTo == null) return;
4429 Message dstMsg = obtainMessageWithArg2(msg);
4432 mReplyChannel.replyToMessage(msg, dstMsg);
4436 * arg2 on the source message has a unique id that needs to be retained in replies
4437 * to match the request
4439 * see WifiManager for details
4441 private Message obtainMessageWithArg2(Message srcMsg) {
4442 Message msg = Message.obtain();
4443 msg.arg2 = srcMsg.arg2;