WifiStateMachine.java revision a0009d14075b5345b8f916c3fb3f2260c938cb9d
1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.wifi;
18
19import static android.net.wifi.WifiManager.WIFI_STATE_DISABLED;
20import static android.net.wifi.WifiManager.WIFI_STATE_DISABLING;
21import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED;
22import static android.net.wifi.WifiManager.WIFI_STATE_ENABLING;
23import static android.net.wifi.WifiManager.WIFI_STATE_UNKNOWN;
24
25/**
26 * TODO:
27 * Deprecate WIFI_STATE_UNKNOWN
28 */
29import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED;
30import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLING;
31import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
32import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLING;
33import static android.net.wifi.WifiManager.WIFI_AP_STATE_FAILED;
34
35import android.app.AlarmManager;
36import android.app.PendingIntent;
37import android.app.backup.IBackupManager;
38import android.bluetooth.BluetoothAdapter;
39import android.content.BroadcastReceiver;
40import android.content.Context;
41import android.content.Intent;
42import android.content.IntentFilter;
43import android.content.pm.PackageManager;
44import android.database.ContentObserver;
45import android.net.ConnectivityManager;
46import android.net.DhcpResults;
47import android.net.DhcpStateMachine;
48import android.net.InterfaceConfiguration;
49import android.net.LinkAddress;
50import android.net.LinkProperties;
51import android.net.NetworkInfo;
52import android.net.NetworkInfo.DetailedState;
53import android.net.NetworkUtils;
54import android.net.RouteInfo;
55import android.net.wifi.BatchedScanResult;
56import android.net.wifi.BatchedScanSettings;
57import android.net.wifi.RssiPacketCountInfo;
58import android.net.wifi.ScanResult;
59import android.net.wifi.ScanSettings;
60import android.net.wifi.WifiChannel;
61import android.net.wifi.SupplicantState;
62import android.net.wifi.WifiConfiguration;
63import android.net.wifi.WifiInfo;
64import android.net.wifi.WifiManager;
65import android.net.wifi.WifiSsid;
66import android.net.wifi.WpsInfo;
67import android.net.wifi.WpsResult;
68import android.net.wifi.WpsResult.Status;
69import android.net.wifi.p2p.WifiP2pManager;
70import android.os.BatteryStats;
71import android.os.Bundle;
72import android.os.IBinder;
73import android.os.INetworkManagementService;
74import android.os.Message;
75import android.os.Messenger;
76import android.os.Parcel;
77import android.os.Parcelable;
78import android.os.PowerManager;
79import android.os.RemoteException;
80import android.os.ServiceManager;
81import android.os.SystemClock;
82import android.os.SystemProperties;
83import android.os.UserHandle;
84import android.os.WorkSource;
85import android.provider.Settings;
86import android.util.LruCache;
87import android.text.TextUtils;
88
89import com.android.internal.R;
90import com.android.internal.app.IBatteryStats;
91import com.android.internal.util.AsyncChannel;
92import com.android.internal.util.Protocol;
93import com.android.internal.util.State;
94import com.android.internal.util.StateMachine;
95import com.android.server.net.BaseNetworkObserver;
96import com.android.server.wifi.p2p.WifiP2pServiceImpl;
97
98import java.io.FileDescriptor;
99import java.io.PrintWriter;
100import java.net.InetAddress;
101import java.util.ArrayList;
102import java.util.LinkedList;
103import java.util.List;
104import java.util.Locale;
105import java.util.Queue;
106import java.util.concurrent.atomic.AtomicInteger;
107import java.util.concurrent.atomic.AtomicBoolean;
108import java.util.Iterator;
109import java.util.regex.Pattern;
110
111/**
112 * Track the state of Wifi connectivity. All event handling is done here,
113 * and all changes in connectivity state are initiated here.
114 *
115 * Wi-Fi now supports three modes of operation: Client, SoftAp and p2p
116 * In the current implementation, we support concurrent wifi p2p and wifi operation.
117 * The WifiStateMachine handles SoftAp and Client operations while WifiP2pService
118 * handles p2p operation.
119 *
120 * @hide
121 */
122public class WifiStateMachine extends StateMachine {
123
124    private static final String NETWORKTYPE = "WIFI";
125    private static final boolean DBG = false;
126
127    private WifiMonitor mWifiMonitor;
128    private WifiNative mWifiNative;
129    private WifiConfigStore mWifiConfigStore;
130    private INetworkManagementService mNwService;
131    private ConnectivityManager mCm;
132
133    private final boolean mP2pSupported;
134    private final AtomicBoolean mP2pConnected = new AtomicBoolean(false);
135    private boolean mTemporarilyDisconnectWifi = false;
136    private final String mPrimaryDeviceType;
137
138    /* Scan results handling */
139    private List<ScanResult> mScanResults = new ArrayList<ScanResult>();
140    private static final Pattern scanResultPattern = Pattern.compile("\t+");
141    private static final int SCAN_RESULT_CACHE_SIZE = 80;
142    private final LruCache<String, ScanResult> mScanResultCache;
143
144    /* Batch scan results */
145    private final List<BatchedScanResult> mBatchedScanResults =
146            new ArrayList<BatchedScanResult>();
147    private int mBatchedScanOwnerUid = UNKNOWN_SCAN_SOURCE;
148    private int mExpectedBatchedScans = 0;
149    private long mBatchedScanMinPollTime = 0;
150
151    /* Chipset supports background scan */
152    private final boolean mBackgroundScanSupported;
153
154    private String mInterfaceName;
155    /* Tethering interface could be separate from wlan interface */
156    private String mTetherInterfaceName;
157
158    private int mLastSignalLevel = -1;
159    private String mLastBssid;
160    private int mLastNetworkId;
161    private boolean mEnableRssiPolling = false;
162    private boolean mEnableBackgroundScan = false;
163    private int mRssiPollToken = 0;
164    private int mReconnectCount = 0;
165    /* 3 operational states for STA operation: CONNECT_MODE, SCAN_ONLY_MODE, SCAN_ONLY_WIFI_OFF_MODE
166    * In CONNECT_MODE, the STA can scan and connect to an access point
167    * In SCAN_ONLY_MODE, the STA can only scan for access points
168    * In SCAN_ONLY_WIFI_OFF_MODE, the STA can only scan for access points with wifi toggle being off
169    */
170    private int mOperationalMode = CONNECT_MODE;
171    private boolean mIsScanOngoing = false;
172    private boolean mIsFullScanOngoing = false;
173    private final Queue<Message> mBufferedScanMsg = new LinkedList<Message>();
174    private WorkSource mScanWorkSource = null;
175    private static final int UNKNOWN_SCAN_SOURCE = -1;
176    private static final int SCAN_REQUEST_BUFFER_MAX_SIZE = 10;
177    private static final String CUSTOMIZED_SCAN_SETTING = "customized_scan_settings";
178    private static final String CUSTOMIZED_SCAN_WORKSOURCE = "customized_scan_worksource";
179    private static final String BATCHED_SETTING = "batched_settings";
180    private static final String BATCHED_WORKSOURCE = "batched_worksource";
181
182    /* Tracks if state machine has received any screen state change broadcast yet.
183     * We can miss one of these at boot.
184     */
185    private AtomicBoolean mScreenBroadcastReceived = new AtomicBoolean(false);
186
187    private boolean mBluetoothConnectionActive = false;
188
189    private PowerManager.WakeLock mSuspendWakeLock;
190
191    /**
192     * Interval in milliseconds between polling for RSSI
193     * and linkspeed information
194     */
195    private static final int POLL_RSSI_INTERVAL_MSECS = 3000;
196
197    /**
198     * Delay between supplicant restarts upon failure to establish connection
199     */
200    private static final int SUPPLICANT_RESTART_INTERVAL_MSECS = 5000;
201
202    /**
203     * Number of times we attempt to restart supplicant
204     */
205    private static final int SUPPLICANT_RESTART_TRIES = 5;
206
207    private int mSupplicantRestartCount = 0;
208    /* Tracks sequence number on stop failure message */
209    private int mSupplicantStopFailureToken = 0;
210
211    /**
212     * Tether state change notification time out
213     */
214    private static final int TETHER_NOTIFICATION_TIME_OUT_MSECS = 5000;
215
216    /* Tracks sequence number on a tether notification time out */
217    private int mTetherToken = 0;
218
219    /**
220     * Driver start time out.
221     */
222    private static final int DRIVER_START_TIME_OUT_MSECS = 10000;
223
224    /* Tracks sequence number on a driver time out */
225    private int mDriverStartToken = 0;
226
227    /**
228     * The link properties of the wifi interface.
229     * Do not modify this directly; use updateLinkProperties instead.
230     */
231    private LinkProperties mLinkProperties;
232
233    /**
234     * Subset of link properties coming from netlink.
235     * Currently includes IPv4 and IPv6 addresses. In the future will also include IPv6 DNS servers
236     * and domains obtained from router advertisements (RFC 6106).
237     */
238    private final LinkProperties mNetlinkLinkProperties;
239
240    /* Tracks sequence number on a periodic scan message */
241    private int mPeriodicScanToken = 0;
242
243    // Wakelock held during wifi start/stop and driver load/unload
244    private PowerManager.WakeLock mWakeLock;
245
246    private Context mContext;
247
248    private final Object mDhcpResultsLock = new Object();
249    private DhcpResults mDhcpResults;
250    private WifiInfo mWifiInfo;
251    private NetworkInfo mNetworkInfo;
252    private SupplicantStateTracker mSupplicantStateTracker;
253    private DhcpStateMachine mDhcpStateMachine;
254    private boolean mDhcpActive = false;
255
256    private class InterfaceObserver extends BaseNetworkObserver {
257        private WifiStateMachine mWifiStateMachine;
258
259        InterfaceObserver(WifiStateMachine wifiStateMachine) {
260            super();
261            mWifiStateMachine = wifiStateMachine;
262        }
263
264        private void maybeLog(String operation, String iface, LinkAddress address) {
265            if (DBG) {
266                log(operation + ": " + address + " on " + iface +
267                    " flags " + address.getFlags() + " scope " + address.getScope());
268            }
269        }
270
271        @Override
272        public void addressUpdated(String iface, LinkAddress address) {
273            if (mWifiStateMachine.mInterfaceName.equals(iface)) {
274                maybeLog("addressUpdated", iface, address);
275                mWifiStateMachine.sendMessage(CMD_IP_ADDRESS_UPDATED, address);
276            }
277        }
278
279        @Override
280        public void addressRemoved(String iface, LinkAddress address) {
281            if (mWifiStateMachine.mInterfaceName.equals(iface)) {
282                maybeLog("addressRemoved", iface, address);
283                mWifiStateMachine.sendMessage(CMD_IP_ADDRESS_REMOVED, address);
284            }
285        }
286    }
287
288    private InterfaceObserver mInterfaceObserver;
289
290    private AlarmManager mAlarmManager;
291    private PendingIntent mScanIntent;
292    private PendingIntent mDriverStopIntent;
293    private PendingIntent mBatchedScanIntervalIntent;
294
295    /* Tracks current frequency mode */
296    private AtomicInteger mFrequencyBand = new AtomicInteger(WifiManager.WIFI_FREQUENCY_BAND_AUTO);
297
298    /* Tracks if we are filtering Multicast v4 packets. Default is to filter. */
299    private AtomicBoolean mFilteringMulticastV4Packets = new AtomicBoolean(true);
300
301    // Channel for sending replies.
302    private AsyncChannel mReplyChannel = new AsyncChannel();
303
304    private WifiP2pManager mWifiP2pManager;
305    //Used to initiate a connection with WifiP2pService
306    private AsyncChannel mWifiP2pChannel;
307    private AsyncChannel mWifiApConfigChannel;
308
309    /* The base for wifi message types */
310    static final int BASE = Protocol.BASE_WIFI;
311    /* Start the supplicant */
312    static final int CMD_START_SUPPLICANT                 = BASE + 11;
313    /* Stop the supplicant */
314    static final int CMD_STOP_SUPPLICANT                  = BASE + 12;
315    /* Start the driver */
316    static final int CMD_START_DRIVER                     = BASE + 13;
317    /* Stop the driver */
318    static final int CMD_STOP_DRIVER                      = BASE + 14;
319    /* Indicates Static IP succeeded */
320    static final int CMD_STATIC_IP_SUCCESS                = BASE + 15;
321    /* Indicates Static IP failed */
322    static final int CMD_STATIC_IP_FAILURE                = BASE + 16;
323    /* Indicates supplicant stop failed */
324    static final int CMD_STOP_SUPPLICANT_FAILED           = BASE + 17;
325    /* Delayed stop to avoid shutting down driver too quick*/
326    static final int CMD_DELAYED_STOP_DRIVER              = BASE + 18;
327    /* A delayed message sent to start driver when it fail to come up */
328    static final int CMD_DRIVER_START_TIMED_OUT           = BASE + 19;
329
330    /* Start the soft access point */
331    static final int CMD_START_AP                         = BASE + 21;
332    /* Indicates soft ap start succeeded */
333    static final int CMD_START_AP_SUCCESS                 = BASE + 22;
334    /* Indicates soft ap start failed */
335    static final int CMD_START_AP_FAILURE                 = BASE + 23;
336    /* Stop the soft access point */
337    static final int CMD_STOP_AP                          = BASE + 24;
338    /* Set the soft access point configuration */
339    static final int CMD_SET_AP_CONFIG                    = BASE + 25;
340    /* Soft access point configuration set completed */
341    static final int CMD_SET_AP_CONFIG_COMPLETED          = BASE + 26;
342    /* Request the soft access point configuration */
343    static final int CMD_REQUEST_AP_CONFIG                = BASE + 27;
344    /* Response to access point configuration request */
345    static final int CMD_RESPONSE_AP_CONFIG               = BASE + 28;
346    /* Invoked when getting a tether state change notification */
347    static final int CMD_TETHER_STATE_CHANGE              = BASE + 29;
348    /* A delayed message sent to indicate tether state change failed to arrive */
349    static final int CMD_TETHER_NOTIFICATION_TIMED_OUT    = BASE + 30;
350
351    static final int CMD_BLUETOOTH_ADAPTER_STATE_CHANGE   = BASE + 31;
352
353    /* Supplicant commands */
354    /* Is supplicant alive ? */
355    static final int CMD_PING_SUPPLICANT                  = BASE + 51;
356    /* Add/update a network configuration */
357    static final int CMD_ADD_OR_UPDATE_NETWORK            = BASE + 52;
358    /* Delete a network */
359    static final int CMD_REMOVE_NETWORK                   = BASE + 53;
360    /* Enable a network. The device will attempt a connection to the given network. */
361    static final int CMD_ENABLE_NETWORK                   = BASE + 54;
362    /* Enable all networks */
363    static final int CMD_ENABLE_ALL_NETWORKS              = BASE + 55;
364    /* Blacklist network. De-prioritizes the given BSSID for connection. */
365    static final int CMD_BLACKLIST_NETWORK                = BASE + 56;
366    /* Clear the blacklist network list */
367    static final int CMD_CLEAR_BLACKLIST                  = BASE + 57;
368    /* Save configuration */
369    static final int CMD_SAVE_CONFIG                      = BASE + 58;
370    /* Get configured networks */
371    static final int CMD_GET_CONFIGURED_NETWORKS          = BASE + 59;
372    /* Get available frequencies */
373    static final int CMD_GET_CAPABILITY_FREQ              = BASE + 60;
374
375    /* Supplicant commands after driver start*/
376    /* Initiate a scan */
377    static final int CMD_START_SCAN                       = BASE + 71;
378    /* Set operational mode. CONNECT, SCAN ONLY, SCAN_ONLY with Wi-Fi off mode */
379    static final int CMD_SET_OPERATIONAL_MODE             = BASE + 72;
380    /* Disconnect from a network */
381    static final int CMD_DISCONNECT                       = BASE + 73;
382    /* Reconnect to a network */
383    static final int CMD_RECONNECT                        = BASE + 74;
384    /* Reassociate to a network */
385    static final int CMD_REASSOCIATE                      = BASE + 75;
386    /* Controls suspend mode optimizations
387     *
388     * When high perf mode is enabled, suspend mode optimizations are disabled
389     *
390     * When high perf mode is disabled, suspend mode optimizations are enabled
391     *
392     * Suspend mode optimizations include:
393     * - packet filtering
394     * - turn off roaming
395     * - DTIM wake up settings
396     */
397    static final int CMD_SET_HIGH_PERF_MODE               = BASE + 77;
398    /* Set the country code */
399    static final int CMD_SET_COUNTRY_CODE                 = BASE + 80;
400    /* Enables RSSI poll */
401    static final int CMD_ENABLE_RSSI_POLL                 = BASE + 82;
402    /* RSSI poll */
403    static final int CMD_RSSI_POLL                        = BASE + 83;
404    /* Set up packet filtering */
405    static final int CMD_START_PACKET_FILTERING           = BASE + 84;
406    /* Clear packet filter */
407    static final int CMD_STOP_PACKET_FILTERING            = BASE + 85;
408    /* Enable suspend mode optimizations in the driver */
409    static final int CMD_SET_SUSPEND_OPT_ENABLED          = BASE + 86;
410    /* When there are no saved networks, we do a periodic scan to notify user of
411     * an open network */
412    static final int CMD_NO_NETWORKS_PERIODIC_SCAN        = BASE + 88;
413
414    /* arg1 values to CMD_STOP_PACKET_FILTERING and CMD_START_PACKET_FILTERING */
415    static final int MULTICAST_V6  = 1;
416    static final int MULTICAST_V4  = 0;
417
418   /* Set the frequency band */
419    static final int CMD_SET_FREQUENCY_BAND               = BASE + 90;
420    /* Enable background scan for configured networks */
421    static final int CMD_ENABLE_BACKGROUND_SCAN           = BASE + 91;
422    /* Enable TDLS on a specific MAC address */
423    static final int CMD_ENABLE_TDLS                      = BASE + 92;
424
425    /* Commands from/to the SupplicantStateTracker */
426    /* Reset the supplicant state tracker */
427    static final int CMD_RESET_SUPPLICANT_STATE           = BASE + 111;
428
429    /* P2p commands */
430    /* We are ok with no response here since we wont do much with it anyway */
431    public static final int CMD_ENABLE_P2P                = BASE + 131;
432    /* In order to shut down supplicant cleanly, we wait till p2p has
433     * been disabled */
434    public static final int CMD_DISABLE_P2P_REQ           = BASE + 132;
435    public static final int CMD_DISABLE_P2P_RSP           = BASE + 133;
436
437    public static final int CMD_BOOT_COMPLETED            = BASE + 134;
438
439    /* change the batch scan settings.
440     * arg1 = responsible UID
441     * arg2 = csph (channel scans per hour)
442     * obj = bundle with the new settings and the optional worksource
443     */
444    public static final int CMD_SET_BATCHED_SCAN          = BASE + 135;
445    public static final int CMD_START_NEXT_BATCHED_SCAN   = BASE + 136;
446    public static final int CMD_POLL_BATCHED_SCAN         = BASE + 137;
447
448    /* Link configuration (IP address, DNS, ...) changes */
449    /* An new IP address was added to our interface, or an existing IP address was updated */
450    static final int CMD_IP_ADDRESS_UPDATED               = BASE + 140;
451    /* An IP address was removed from our interface */
452    static final int CMD_IP_ADDRESS_REMOVED               = BASE + 141;
453    /* Reload all networks and reconnect */
454    static final int CMD_RELOAD_TLS_AND_RECONNECT         = BASE + 142;
455
456    /* Wifi state machine modes of operation */
457    /* CONNECT_MODE - connect to any 'known' AP when it becomes available */
458    public static final int CONNECT_MODE                   = 1;
459    /* SCAN_ONLY_MODE - don't connect to any APs; scan, but only while apps hold lock */
460    public static final int SCAN_ONLY_MODE                 = 2;
461    /* SCAN_ONLY_WITH_WIFI_OFF - scan, but don't connect to any APs */
462    public static final int SCAN_ONLY_WITH_WIFI_OFF_MODE   = 3;
463
464    private static final int SUCCESS = 1;
465    private static final int FAILURE = -1;
466
467    /**
468     * The maximum number of times we will retry a connection to an access point
469     * for which we have failed in acquiring an IP address from DHCP. A value of
470     * N means that we will make N+1 connection attempts in all.
471     * <p>
472     * See {@link Settings.Secure#WIFI_MAX_DHCP_RETRY_COUNT}. This is the default
473     * value if a Settings value is not present.
474     */
475    private static final int DEFAULT_MAX_DHCP_RETRIES = 9;
476
477    /* Tracks if suspend optimizations need to be disabled by DHCP,
478     * screen or due to high perf mode.
479     * When any of them needs to disable it, we keep the suspend optimizations
480     * disabled
481     */
482    private int mSuspendOptNeedsDisabled = 0;
483
484    private static final int SUSPEND_DUE_TO_DHCP       = 1;
485    private static final int SUSPEND_DUE_TO_HIGH_PERF  = 1<<1;
486    private static final int SUSPEND_DUE_TO_SCREEN     = 1<<2;
487
488    /* Tracks if user has enabled suspend optimizations through settings */
489    private AtomicBoolean mUserWantsSuspendOpt = new AtomicBoolean(true);
490
491    /**
492     * Default framework scan interval in milliseconds. This is used in the scenario in which
493     * wifi chipset does not support background scanning to set up a
494     * periodic wake up scan so that the device can connect to a new access
495     * point on the move. {@link Settings.Global#WIFI_FRAMEWORK_SCAN_INTERVAL_MS} can
496     * override this.
497     */
498    private final int mDefaultFrameworkScanIntervalMs;
499
500    /**
501     * Supplicant scan interval in milliseconds.
502     * Comes from {@link Settings.Global#WIFI_SUPPLICANT_SCAN_INTERVAL_MS} or
503     * from the default config if the setting is not set
504     */
505    private long mSupplicantScanIntervalMs;
506
507    /**
508     * Minimum time interval between enabling all networks.
509     * A device can end up repeatedly connecting to a bad network on screen on/off toggle
510     * due to enabling every time. We add a threshold to avoid this.
511     */
512    private static final int MIN_INTERVAL_ENABLE_ALL_NETWORKS_MS = 10 * 60 * 1000; /* 10 minutes */
513    private long mLastEnableAllNetworksTime;
514
515    /**
516     * Starting and shutting down driver too quick causes problems leading to driver
517     * being in a bad state. Delay driver stop.
518     */
519    private final int mDriverStopDelayMs;
520    private int mDelayedStopCounter;
521    private boolean mInDelayedStop = false;
522
523    // sometimes telephony gives us this data before boot is complete and we can't store it
524    // until after, so the write is deferred
525    private volatile String mPersistedCountryCode;
526
527    // Supplicant doesn't like setting the same country code multiple times (it may drop
528    // currently connected network), so we save the country code here to avoid redundency
529    private String mLastSetCountryCode;
530
531    private static final int MIN_RSSI = -200;
532    private static final int MAX_RSSI = 256;
533
534    /* Default parent state */
535    private State mDefaultState = new DefaultState();
536    /* Temporary initial state */
537    private State mInitialState = new InitialState();
538    /* Driver loaded, waiting for supplicant to start */
539    private State mSupplicantStartingState = new SupplicantStartingState();
540    /* Driver loaded and supplicant ready */
541    private State mSupplicantStartedState = new SupplicantStartedState();
542    /* Waiting for supplicant to stop and monitor to exit */
543    private State mSupplicantStoppingState = new SupplicantStoppingState();
544    /* Driver start issued, waiting for completed event */
545    private State mDriverStartingState = new DriverStartingState();
546    /* Driver started */
547    private State mDriverStartedState = new DriverStartedState();
548    /* Wait until p2p is disabled
549     * This is a special state which is entered right after we exit out of DriverStartedState
550     * before transitioning to another state.
551     */
552    private State mWaitForP2pDisableState = new WaitForP2pDisableState();
553    /* Driver stopping */
554    private State mDriverStoppingState = new DriverStoppingState();
555    /* Driver stopped */
556    private State mDriverStoppedState = new DriverStoppedState();
557    /* Scan for networks, no connection will be established */
558    private State mScanModeState = new ScanModeState();
559    /* Connecting to an access point */
560    private State mConnectModeState = new ConnectModeState();
561    /* Connected at 802.11 (L2) level */
562    private State mL2ConnectedState = new L2ConnectedState();
563    /* fetching IP after connection to access point (assoc+auth complete) */
564    private State mObtainingIpState = new ObtainingIpState();
565    /* Waiting for link quality verification to be complete */
566    private State mVerifyingLinkState = new VerifyingLinkState();
567    /* Connected with IP addr */
568    private State mConnectedState = new ConnectedState();
569    /* disconnect issued, waiting for network disconnect confirmation */
570    private State mDisconnectingState = new DisconnectingState();
571    /* Network is not connected, supplicant assoc+auth is not complete */
572    private State mDisconnectedState = new DisconnectedState();
573    /* Waiting for WPS to be completed*/
574    private State mWpsRunningState = new WpsRunningState();
575
576    /* Soft ap is starting up */
577    private State mSoftApStartingState = new SoftApStartingState();
578    /* Soft ap is running */
579    private State mSoftApStartedState = new SoftApStartedState();
580    /* Soft ap is running and we are waiting for tether notification */
581    private State mTetheringState = new TetheringState();
582    /* Soft ap is running and we are tethered through connectivity service */
583    private State mTetheredState = new TetheredState();
584    /* Waiting for untether confirmation before stopping soft Ap */
585    private State mUntetheringState = new UntetheringState();
586
587    private class TetherStateChange {
588        ArrayList<String> available;
589        ArrayList<String> active;
590        TetherStateChange(ArrayList<String> av, ArrayList<String> ac) {
591            available = av;
592            active = ac;
593        }
594    }
595
596
597    /**
598     * One of  {@link WifiManager#WIFI_STATE_DISABLED},
599     *         {@link WifiManager#WIFI_STATE_DISABLING},
600     *         {@link WifiManager#WIFI_STATE_ENABLED},
601     *         {@link WifiManager#WIFI_STATE_ENABLING},
602     *         {@link WifiManager#WIFI_STATE_UNKNOWN}
603     *
604     */
605    private final AtomicInteger mWifiState = new AtomicInteger(WIFI_STATE_DISABLED);
606
607    /**
608     * One of  {@link WifiManager#WIFI_AP_STATE_DISABLED},
609     *         {@link WifiManager#WIFI_AP_STATE_DISABLING},
610     *         {@link WifiManager#WIFI_AP_STATE_ENABLED},
611     *         {@link WifiManager#WIFI_AP_STATE_ENABLING},
612     *         {@link WifiManager#WIFI_AP_STATE_FAILED}
613     *
614     */
615    private final AtomicInteger mWifiApState = new AtomicInteger(WIFI_AP_STATE_DISABLED);
616
617    private static final int SCAN_REQUEST = 0;
618    private static final String ACTION_START_SCAN =
619        "com.android.server.WifiManager.action.START_SCAN";
620
621    private static final String DELAYED_STOP_COUNTER = "DelayedStopCounter";
622    private static final int DRIVER_STOP_REQUEST = 0;
623    private static final String ACTION_DELAYED_DRIVER_STOP =
624        "com.android.server.WifiManager.action.DELAYED_DRIVER_STOP";
625
626    private static final String ACTION_REFRESH_BATCHED_SCAN =
627            "com.android.server.WifiManager.action.REFRESH_BATCHED_SCAN";
628    /**
629     * Keep track of whether WIFI is running.
630     */
631    private boolean mIsRunning = false;
632
633    /**
634     * Keep track of whether we last told the battery stats we had started.
635     */
636    private boolean mReportedRunning = false;
637
638    /**
639     * Most recently set source of starting WIFI.
640     */
641    private final WorkSource mRunningWifiUids = new WorkSource();
642
643    /**
644     * The last reported UIDs that were responsible for starting WIFI.
645     */
646    private final WorkSource mLastRunningWifiUids = new WorkSource();
647
648    private final IBatteryStats mBatteryStats;
649
650    private BatchedScanSettings mBatchedScanSettings = null;
651
652    /**
653     * Track the worksource/cost of the current settings and track what's been noted
654     * to the battery stats, so we can mark the end of the previous when changing.
655     */
656    private WorkSource mBatchedScanWorkSource = null;
657    private int mBatchedScanCsph = 0;
658    private WorkSource mNotedBatchedScanWorkSource = null;
659    private int mNotedBatchedScanCsph = 0;
660
661
662    public WifiStateMachine(Context context, String wlanInterface) {
663        super("WifiStateMachine");
664        mContext = context;
665        mInterfaceName = wlanInterface;
666
667        mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, NETWORKTYPE, "");
668        mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
669                BatteryStats.SERVICE_NAME));
670
671        IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
672        mNwService = INetworkManagementService.Stub.asInterface(b);
673
674        mP2pSupported = mContext.getPackageManager().hasSystemFeature(
675                PackageManager.FEATURE_WIFI_DIRECT);
676
677        mWifiNative = new WifiNative(mInterfaceName);
678        mWifiConfigStore = new WifiConfigStore(context, mWifiNative);
679        mWifiMonitor = new WifiMonitor(this, mWifiNative);
680        mWifiInfo = new WifiInfo();
681        mSupplicantStateTracker = new SupplicantStateTracker(context, this, mWifiConfigStore,
682                getHandler());
683        mLinkProperties = new LinkProperties();
684        mNetlinkLinkProperties = new LinkProperties();
685
686        mWifiP2pManager = (WifiP2pManager) mContext.getSystemService(Context.WIFI_P2P_SERVICE);
687
688        mNetworkInfo.setIsAvailable(false);
689        mLastBssid = null;
690        mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
691        mLastSignalLevel = -1;
692
693        mInterfaceObserver = new InterfaceObserver(this);
694        try {
695            mNwService.registerObserver(mInterfaceObserver);
696        } catch (RemoteException e) {
697            loge("Couldn't register interface observer: " + e.toString());
698        }
699
700        mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
701        Intent scanIntent = new Intent(ACTION_START_SCAN, null);
702        mScanIntent = PendingIntent.getBroadcast(mContext, SCAN_REQUEST, scanIntent, 0);
703
704        Intent batchedIntent = new Intent(ACTION_REFRESH_BATCHED_SCAN, null);
705        mBatchedScanIntervalIntent = PendingIntent.getBroadcast(mContext, 0, batchedIntent, 0);
706
707        mDefaultFrameworkScanIntervalMs = mContext.getResources().getInteger(
708                R.integer.config_wifi_framework_scan_interval);
709
710        mDriverStopDelayMs = mContext.getResources().getInteger(
711                R.integer.config_wifi_driver_stop_delay);
712
713        mBackgroundScanSupported = mContext.getResources().getBoolean(
714                R.bool.config_wifi_background_scan_support);
715
716        mPrimaryDeviceType = mContext.getResources().getString(
717                R.string.config_wifi_p2p_device_type);
718
719        mUserWantsSuspendOpt.set(Settings.Global.getInt(mContext.getContentResolver(),
720                    Settings.Global.WIFI_SUSPEND_OPTIMIZATIONS_ENABLED, 1) == 1);
721
722        mContext.registerReceiver(
723            new BroadcastReceiver() {
724                @Override
725                public void onReceive(Context context, Intent intent) {
726                    ArrayList<String> available = intent.getStringArrayListExtra(
727                            ConnectivityManager.EXTRA_AVAILABLE_TETHER);
728                    ArrayList<String> active = intent.getStringArrayListExtra(
729                            ConnectivityManager.EXTRA_ACTIVE_TETHER);
730                    sendMessage(CMD_TETHER_STATE_CHANGE, new TetherStateChange(available, active));
731                }
732            },new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED));
733
734        mContext.registerReceiver(
735                new BroadcastReceiver() {
736                    @Override
737                    public void onReceive(Context context, Intent intent) {
738                        startScan(UNKNOWN_SCAN_SOURCE, null, null);
739                    }
740                },
741                new IntentFilter(ACTION_START_SCAN));
742
743        IntentFilter filter = new IntentFilter();
744        filter.addAction(Intent.ACTION_SCREEN_ON);
745        filter.addAction(Intent.ACTION_SCREEN_OFF);
746        filter.addAction(ACTION_REFRESH_BATCHED_SCAN);
747        mContext.registerReceiver(
748                new BroadcastReceiver() {
749                    @Override
750                    public void onReceive(Context context, Intent intent) {
751                        String action = intent.getAction();
752
753                        if (action.equals(Intent.ACTION_SCREEN_ON)) {
754                            handleScreenStateChanged(true);
755                        } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
756                            handleScreenStateChanged(false);
757                        } else if (action.equals(ACTION_REFRESH_BATCHED_SCAN)) {
758                            startNextBatchedScanAsync();
759                        }
760                    }
761                }, filter);
762
763        mContext.registerReceiver(
764                new BroadcastReceiver() {
765                    @Override
766                    public void onReceive(Context context, Intent intent) {
767                       int counter = intent.getIntExtra(DELAYED_STOP_COUNTER, 0);
768                       sendMessage(CMD_DELAYED_STOP_DRIVER, counter, 0);
769                    }
770                },
771                new IntentFilter(ACTION_DELAYED_DRIVER_STOP));
772
773        mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor(
774                Settings.Global.WIFI_SUSPEND_OPTIMIZATIONS_ENABLED), false,
775                new ContentObserver(getHandler()) {
776                    @Override
777                    public void onChange(boolean selfChange) {
778                        mUserWantsSuspendOpt.set(Settings.Global.getInt(mContext.getContentResolver(),
779                                Settings.Global.WIFI_SUSPEND_OPTIMIZATIONS_ENABLED, 1) == 1);
780                    }
781                });
782
783        mContext.registerReceiver(
784                new BroadcastReceiver() {
785                    @Override
786                    public void onReceive(Context context, Intent intent) {
787                        sendMessage(CMD_BOOT_COMPLETED);
788                    }
789                },
790                new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
791
792        mScanResultCache = new LruCache<String, ScanResult>(SCAN_RESULT_CACHE_SIZE);
793
794        PowerManager powerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
795        mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, getName());
796
797        mSuspendWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "WifiSuspend");
798        mSuspendWakeLock.setReferenceCounted(false);
799
800        addState(mDefaultState);
801            addState(mInitialState, mDefaultState);
802            addState(mSupplicantStartingState, mDefaultState);
803            addState(mSupplicantStartedState, mDefaultState);
804                addState(mDriverStartingState, mSupplicantStartedState);
805                addState(mDriverStartedState, mSupplicantStartedState);
806                    addState(mScanModeState, mDriverStartedState);
807                    addState(mConnectModeState, mDriverStartedState);
808                        addState(mL2ConnectedState, mConnectModeState);
809                            addState(mObtainingIpState, mL2ConnectedState);
810                            addState(mVerifyingLinkState, mL2ConnectedState);
811                            addState(mConnectedState, mL2ConnectedState);
812                        addState(mDisconnectingState, mConnectModeState);
813                        addState(mDisconnectedState, mConnectModeState);
814                        addState(mWpsRunningState, mConnectModeState);
815                addState(mWaitForP2pDisableState, mSupplicantStartedState);
816                addState(mDriverStoppingState, mSupplicantStartedState);
817                addState(mDriverStoppedState, mSupplicantStartedState);
818            addState(mSupplicantStoppingState, mDefaultState);
819            addState(mSoftApStartingState, mDefaultState);
820            addState(mSoftApStartedState, mDefaultState);
821                addState(mTetheringState, mSoftApStartedState);
822                addState(mTetheredState, mSoftApStartedState);
823                addState(mUntetheringState, mSoftApStartedState);
824
825        setInitialState(mInitialState);
826
827        setLogRecSize(2000);
828        setLogOnlyTransitions(false);
829        if (DBG) setDbg(true);
830
831        //start the state machine
832        start();
833
834        final Intent intent = new Intent(WifiManager.WIFI_SCAN_AVAILABLE);
835        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
836        intent.putExtra(WifiManager.EXTRA_SCAN_AVAILABLE, WIFI_STATE_DISABLED);
837        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
838    }
839
840    /*********************************************************
841     * Methods exposed for public use
842     ********************************************************/
843
844    public Messenger getMessenger() {
845        return new Messenger(getHandler());
846    }
847    /**
848     * TODO: doc
849     */
850    public boolean syncPingSupplicant(AsyncChannel channel) {
851        Message resultMsg = channel.sendMessageSynchronously(CMD_PING_SUPPLICANT);
852        boolean result = (resultMsg.arg1 != FAILURE);
853        resultMsg.recycle();
854        return result;
855    }
856
857    public List<WifiChannel> syncGetChannelList(AsyncChannel channel) {
858        Message resultMsg = channel.sendMessageSynchronously(CMD_GET_CAPABILITY_FREQ);
859        List<WifiChannel> list = null;
860        if (resultMsg.obj != null) {
861            list = new ArrayList<WifiChannel>();
862            String freqs = (String) resultMsg.obj;
863            String[] lines = freqs.split("\n");
864            for (String line : lines)
865                if (line.contains("MHz")) {
866                    // line format: " 52 = 5260 MHz (NO_IBSS) (DFS)"
867                    WifiChannel c = new WifiChannel();
868                    String[] prop = line.split(" ");
869                    if (prop.length < 5) continue;
870                    try {
871                        c.channelNum = Integer.parseInt(prop[1]);
872                        c.freqMHz = Integer.parseInt(prop[3]);
873                    } catch (NumberFormatException e) { }
874                    c.isDFS = line.contains("(DFS)");
875                    list.add(c);
876                } else if (line.contains("Mode[B] Channels:")) {
877                    // B channels are the same as G channels, skipped
878                    break;
879                }
880        }
881        resultMsg.recycle();
882        return (list != null && list.size() > 0) ? list : null;
883    }
884
885
886    /**
887     * Initiate a wifi scan. If workSource is not null, blame is given to it, otherwise blame is
888     * given to callingUid.
889     *
890     * @param callingUid The uid initiating the wifi scan. Blame will be given here unless
891     *                   workSource is specified.
892     * @param workSource If not null, blame is given to workSource.
893     * @param settings Scan settings, see {@link ScanSettings}.
894     */
895    public void startScan(int callingUid, ScanSettings settings, WorkSource workSource) {
896        Bundle bundle = new Bundle();
897        bundle.putParcelable(CUSTOMIZED_SCAN_SETTING, settings);
898        bundle.putParcelable(CUSTOMIZED_SCAN_WORKSOURCE, workSource);
899        sendMessage(CMD_START_SCAN, callingUid, 0, bundle);
900    }
901
902    /**
903     * start or stop batched scanning using the given settings
904     */
905    public void setBatchedScanSettings(BatchedScanSettings settings, int callingUid, int csph,
906            WorkSource workSource) {
907        Bundle bundle = new Bundle();
908        bundle.putParcelable(BATCHED_SETTING, settings);
909        bundle.putParcelable(BATCHED_WORKSOURCE, workSource);
910        sendMessage(CMD_SET_BATCHED_SCAN, callingUid, csph, bundle);
911    }
912
913    public List<BatchedScanResult> syncGetBatchedScanResultsList() {
914        synchronized (mBatchedScanResults) {
915            List<BatchedScanResult> batchedScanList =
916                    new ArrayList<BatchedScanResult>(mBatchedScanResults.size());
917            for(BatchedScanResult result: mBatchedScanResults) {
918                batchedScanList.add(new BatchedScanResult(result));
919            }
920            return batchedScanList;
921        }
922    }
923
924    public void requestBatchedScanPoll() {
925        sendMessage(CMD_POLL_BATCHED_SCAN);
926    }
927
928    private void startBatchedScan() {
929        if (mBatchedScanSettings == null) return;
930
931        if (mDhcpActive) {
932            if (DBG) log("not starting Batched Scans due to DHCP");
933            return;
934        }
935
936        // first grab any existing data
937        retrieveBatchedScanData();
938
939        mAlarmManager.cancel(mBatchedScanIntervalIntent);
940
941        String scansExpected = mWifiNative.setBatchedScanSettings(mBatchedScanSettings);
942        try {
943            mExpectedBatchedScans = Integer.parseInt(scansExpected);
944            setNextBatchedAlarm(mExpectedBatchedScans);
945            if (mExpectedBatchedScans > 0) noteBatchedScanStart();
946        } catch (NumberFormatException e) {
947            stopBatchedScan();
948            loge("Exception parsing WifiNative.setBatchedScanSettings response " + e);
949        }
950    }
951
952    // called from BroadcastListener
953    private void startNextBatchedScanAsync() {
954        sendMessage(CMD_START_NEXT_BATCHED_SCAN);
955    }
956
957    private void startNextBatchedScan() {
958        // first grab any existing data
959        retrieveBatchedScanData();
960
961        setNextBatchedAlarm(mExpectedBatchedScans);
962    }
963
964    private void handleBatchedScanPollRequest() {
965        if (DBG) {
966            log("handleBatchedScanPoll Request - mBatchedScanMinPollTime=" +
967                    mBatchedScanMinPollTime + " , mBatchedScanSettings=" +
968                    mBatchedScanSettings);
969        }
970        // if there is no appropriate PollTime that's because we either aren't
971        // batching or we've already set a time for a poll request
972        if (mBatchedScanMinPollTime == 0) return;
973        if (mBatchedScanSettings == null) return;
974
975        long now = System.currentTimeMillis();
976
977        if (now > mBatchedScanMinPollTime) {
978            // do the poll and reset our timers
979            startNextBatchedScan();
980        } else {
981            mAlarmManager.setExact(AlarmManager.RTC_WAKEUP, mBatchedScanMinPollTime,
982                    mBatchedScanIntervalIntent);
983            mBatchedScanMinPollTime = 0;
984        }
985    }
986
987    // return true if new/different
988    private boolean recordBatchedScanSettings(int responsibleUid, int csph, Bundle bundle) {
989        BatchedScanSettings settings = bundle.getParcelable(BATCHED_SETTING);
990        WorkSource responsibleWorkSource = bundle.getParcelable(BATCHED_WORKSOURCE);
991
992        if (DBG) {
993            log("set batched scan to " + settings + " for uid=" + responsibleUid +
994                    ", worksource=" + responsibleWorkSource);
995        }
996        if (settings != null) {
997            if (settings.equals(mBatchedScanSettings)) return false;
998        } else {
999            if (mBatchedScanSettings == null) return false;
1000        }
1001        mBatchedScanSettings = settings;
1002        if (responsibleWorkSource == null) responsibleWorkSource = new WorkSource(responsibleUid);
1003        mBatchedScanWorkSource = responsibleWorkSource;
1004        mBatchedScanCsph = csph;
1005        return true;
1006    }
1007
1008    private void stopBatchedScan() {
1009        mAlarmManager.cancel(mBatchedScanIntervalIntent);
1010        retrieveBatchedScanData();
1011        mWifiNative.setBatchedScanSettings(null);
1012        noteBatchedScanStop();
1013    }
1014
1015    private void setNextBatchedAlarm(int scansExpected) {
1016
1017        if (mBatchedScanSettings == null || scansExpected < 1) return;
1018
1019        mBatchedScanMinPollTime = System.currentTimeMillis() +
1020                mBatchedScanSettings.scanIntervalSec * 1000;
1021
1022        if (mBatchedScanSettings.maxScansPerBatch < scansExpected) {
1023            scansExpected = mBatchedScanSettings.maxScansPerBatch;
1024        }
1025
1026        int secToFull = mBatchedScanSettings.scanIntervalSec;
1027        secToFull *= scansExpected;
1028
1029        int debugPeriod = SystemProperties.getInt("wifi.batchedScan.pollPeriod", 0);
1030        if (debugPeriod > 0) secToFull = debugPeriod;
1031
1032        // set the alarm to do the next poll.  We set it a little short as we'd rather
1033        // wake up wearly than miss a scan due to buffer overflow
1034        mAlarmManager.setExact(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()
1035                + ((secToFull - (mBatchedScanSettings.scanIntervalSec / 2)) * 1000),
1036                mBatchedScanIntervalIntent);
1037    }
1038
1039    /**
1040     * Start reading new scan data
1041     * Data comes in as:
1042     * "scancount=5\n"
1043     * "nextcount=5\n"
1044     *   "apcount=3\n"
1045     *   "trunc\n" (optional)
1046     *     "bssid=...\n"
1047     *     "ssid=...\n"
1048     *     "freq=...\n" (in Mhz)
1049     *     "level=...\n"
1050     *     "dist=...\n" (in cm)
1051     *     "distsd=...\n" (standard deviation, in cm)
1052     *     "===="
1053     *     "bssid=...\n"
1054     *     etc
1055     *     "===="
1056     *     "bssid=...\n"
1057     *     etc
1058     *     "%%%%"
1059     *   "apcount=2\n"
1060     *     "bssid=...\n"
1061     *     etc
1062     *     "%%%%
1063     *   etc
1064     *   "----"
1065     */
1066    private final static boolean DEBUG_PARSE = false;
1067    private void retrieveBatchedScanData() {
1068        String rawData = mWifiNative.getBatchedScanResults();
1069        if (DEBUG_PARSE) log("rawData = " + rawData);
1070        mBatchedScanMinPollTime = 0;
1071        if (rawData == null || rawData.equalsIgnoreCase("OK")) {
1072            loge("Unexpected BatchedScanResults :" + rawData);
1073            return;
1074        }
1075
1076        int scanCount = 0;
1077        final String END_OF_BATCHES = "----";
1078        final String SCANCOUNT = "scancount=";
1079        final String TRUNCATED = "trunc";
1080        final String AGE = "age=";
1081        final String DIST = "dist=";
1082        final String DISTSD = "distSd=";
1083
1084        String splitData[] = rawData.split("\n");
1085        int n = 0;
1086        if (splitData[n].startsWith(SCANCOUNT)) {
1087            try {
1088                scanCount = Integer.parseInt(splitData[n++].substring(SCANCOUNT.length()));
1089            } catch (NumberFormatException e) {
1090                loge("scancount parseInt Exception from " + splitData[n]);
1091            }
1092        } else log("scancount not found");
1093        if (scanCount == 0) {
1094            loge("scanCount==0 - aborting");
1095            return;
1096        }
1097
1098        final Intent intent = new Intent(WifiManager.BATCHED_SCAN_RESULTS_AVAILABLE_ACTION);
1099        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1100
1101        synchronized (mBatchedScanResults) {
1102            mBatchedScanResults.clear();
1103            BatchedScanResult batchedScanResult = new BatchedScanResult();
1104
1105            String bssid = null;
1106            WifiSsid wifiSsid = null;
1107            int level = 0;
1108            int freq = 0;
1109            int dist, distSd;
1110            long tsf = 0;
1111            dist = distSd = ScanResult.UNSPECIFIED;
1112            final long now = SystemClock.elapsedRealtime();
1113            final int bssidStrLen = BSSID_STR.length();
1114
1115            while (true) {
1116                while (n < splitData.length) {
1117                    if (DEBUG_PARSE) logd("parsing " + splitData[n]);
1118                    if (splitData[n].equals(END_OF_BATCHES)) {
1119                        if (n+1 != splitData.length) {
1120                            loge("didn't consume " + (splitData.length-n));
1121                        }
1122                        if (mBatchedScanResults.size() > 0) {
1123                            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
1124                        }
1125                        logd("retrieveBatchedScanResults X");
1126                        return;
1127                    }
1128                    if ((splitData[n].equals(END_STR)) || splitData[n].equals(DELIMITER_STR)) {
1129                        if (bssid != null) {
1130                            batchedScanResult.scanResults.add(new ScanResult(
1131                                    wifiSsid, bssid, "", level, freq, tsf, dist, distSd));
1132                            wifiSsid = null;
1133                            bssid = null;
1134                            level = 0;
1135                            freq = 0;
1136                            tsf = 0;
1137                            dist = distSd = ScanResult.UNSPECIFIED;
1138                        }
1139                        if (splitData[n].equals(END_STR)) {
1140                            if (batchedScanResult.scanResults.size() != 0) {
1141                                mBatchedScanResults.add(batchedScanResult);
1142                                batchedScanResult = new BatchedScanResult();
1143                            } else {
1144                                logd("Found empty batch");
1145                            }
1146                        }
1147                    } else if (splitData[n].equals(TRUNCATED)) {
1148                        batchedScanResult.truncated = true;
1149                    } else if (splitData[n].startsWith(BSSID_STR)) {
1150                        bssid = new String(splitData[n].getBytes(), bssidStrLen,
1151                                splitData[n].length() - bssidStrLen);
1152                    } else if (splitData[n].startsWith(FREQ_STR)) {
1153                        try {
1154                            freq = Integer.parseInt(splitData[n].substring(FREQ_STR.length()));
1155                        } catch (NumberFormatException e) {
1156                            loge("Invalid freqency: " + splitData[n]);
1157                            freq = 0;
1158                        }
1159                    } else if (splitData[n].startsWith(AGE)) {
1160                        try {
1161                            tsf = now - Long.parseLong(splitData[n].substring(AGE.length()));
1162                            tsf *= 1000; // convert mS -> uS
1163                        } catch (NumberFormatException e) {
1164                            loge("Invalid timestamp: " + splitData[n]);
1165                            tsf = 0;
1166                        }
1167                    } else if (splitData[n].startsWith(SSID_STR)) {
1168                        wifiSsid = WifiSsid.createFromAsciiEncoded(
1169                                splitData[n].substring(SSID_STR.length()));
1170                    } else if (splitData[n].startsWith(LEVEL_STR)) {
1171                        try {
1172                            level = Integer.parseInt(splitData[n].substring(LEVEL_STR.length()));
1173                            if (level > 0) level -= 256;
1174                        } catch (NumberFormatException e) {
1175                            loge("Invalid level: " + splitData[n]);
1176                            level = 0;
1177                        }
1178                    } else if (splitData[n].startsWith(DIST)) {
1179                        try {
1180                            dist = Integer.parseInt(splitData[n].substring(DIST.length()));
1181                        } catch (NumberFormatException e) {
1182                            loge("Invalid distance: " + splitData[n]);
1183                            dist = ScanResult.UNSPECIFIED;
1184                        }
1185                    } else if (splitData[n].startsWith(DISTSD)) {
1186                        try {
1187                            distSd = Integer.parseInt(splitData[n].substring(DISTSD.length()));
1188                        } catch (NumberFormatException e) {
1189                            loge("Invalid distanceSd: " + splitData[n]);
1190                            distSd = ScanResult.UNSPECIFIED;
1191                        }
1192                    } else {
1193                        loge("Unable to parse batched scan result line: " + splitData[n]);
1194                    }
1195                    n++;
1196                }
1197                rawData = mWifiNative.getBatchedScanResults();
1198                if (DEBUG_PARSE) log("reading more data:\n" + rawData);
1199                if (rawData == null) {
1200                    loge("Unexpected null BatchedScanResults");
1201                    return;
1202                }
1203                splitData = rawData.split("\n");
1204                if (splitData.length == 0 || splitData[0].equals("ok")) {
1205                    loge("batch scan results just ended!");
1206                    if (mBatchedScanResults.size() > 0) {
1207                        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
1208                    }
1209                    return;
1210                }
1211                n = 0;
1212            }
1213        }
1214    }
1215
1216    // If workSource is not null, blame is given to it, otherwise blame is given to callingUid.
1217    private void noteScanStart(int callingUid, WorkSource workSource) {
1218        if (mScanWorkSource == null && (callingUid != UNKNOWN_SCAN_SOURCE || workSource != null)) {
1219            mScanWorkSource = workSource != null ? workSource : new WorkSource(callingUid);
1220            try {
1221                mBatteryStats.noteWifiScanStartedFromSource(mScanWorkSource);
1222            } catch (RemoteException e) {
1223                log(e.toString());
1224            }
1225        }
1226    }
1227
1228    private void noteScanEnd() {
1229        if (mScanWorkSource != null) {
1230            try {
1231                mBatteryStats.noteWifiScanStoppedFromSource(mScanWorkSource);
1232            } catch (RemoteException e) {
1233                log(e.toString());
1234            } finally {
1235                mScanWorkSource = null;
1236            }
1237        }
1238    }
1239
1240    private void noteBatchedScanStart() {
1241        // note the end of a previous scan set
1242        if (mNotedBatchedScanWorkSource != null &&
1243                (mNotedBatchedScanWorkSource.equals(mBatchedScanWorkSource) == false ||
1244                 mNotedBatchedScanCsph != mBatchedScanCsph)) {
1245            try {
1246                mBatteryStats.noteWifiBatchedScanStoppedFromSource(mNotedBatchedScanWorkSource);
1247            } catch (RemoteException e) {
1248                log(e.toString());
1249            } finally {
1250                mNotedBatchedScanWorkSource = null;
1251                mNotedBatchedScanCsph = 0;
1252            }
1253        }
1254        // note the start of the new
1255        try {
1256            mBatteryStats.noteWifiBatchedScanStartedFromSource(mBatchedScanWorkSource,
1257                    mBatchedScanCsph);
1258            mNotedBatchedScanWorkSource = mBatchedScanWorkSource;
1259            mNotedBatchedScanCsph = mBatchedScanCsph;
1260        } catch (RemoteException e) {
1261            log(e.toString());
1262        }
1263    }
1264
1265    private void noteBatchedScanStop() {
1266        if (mNotedBatchedScanWorkSource != null) {
1267            try {
1268                mBatteryStats.noteWifiBatchedScanStoppedFromSource(mNotedBatchedScanWorkSource);
1269            } catch (RemoteException e) {
1270                log(e.toString());
1271            } finally {
1272                mNotedBatchedScanWorkSource = null;
1273                mNotedBatchedScanCsph = 0;
1274            }
1275        }
1276    }
1277
1278    private void handleScanRequest(int type, Message message) {
1279        // unbundle parameters
1280        Bundle bundle = (Bundle) message.obj;
1281        ScanSettings settings = bundle.getParcelable(CUSTOMIZED_SCAN_SETTING);
1282        WorkSource workSource = bundle.getParcelable(CUSTOMIZED_SCAN_WORKSOURCE);
1283
1284        // parse scan settings
1285        String freqs = null;
1286        if (settings != null && settings.channelSet != null) {
1287            StringBuilder sb = new StringBuilder();
1288            boolean first = true;
1289            for (WifiChannel channel : settings.channelSet) {
1290                if (!first) sb.append(','); else first = false;
1291                sb.append(channel.freqMHz);
1292            }
1293            freqs = sb.toString();
1294        }
1295
1296        // call wifi native to start the scan
1297        if (startScanNative(type, freqs)) {
1298            // only count battery consumption if scan request is accepted
1299            noteScanStart(message.arg1, workSource);
1300            // a full scan covers everything, clearing scan request buffer
1301            if (freqs == null)
1302                mBufferedScanMsg.clear();
1303            return;
1304        }
1305
1306        // if reach here, scan request is rejected
1307
1308        if (!mIsScanOngoing) {
1309            // if rejection is NOT due to ongoing scan (e.g. bad scan parameters),
1310            // discard this request and pop up the next one
1311            if (mBufferedScanMsg.size() > 0)
1312                sendMessage(mBufferedScanMsg.remove());
1313        } else if (!mIsFullScanOngoing) {
1314            // if rejection is due to an ongoing scan, and the ongoing one is NOT a full scan,
1315            // buffer the scan request to make sure specified channels will be scanned eventually
1316            if (freqs == null)
1317                mBufferedScanMsg.clear();
1318            if (mBufferedScanMsg.size() < SCAN_REQUEST_BUFFER_MAX_SIZE) {
1319                Message msg = obtainMessage(CMD_START_SCAN, message.arg1, 0, bundle);
1320                mBufferedScanMsg.add(msg);
1321            } else {
1322                // if too many requests in buffer, combine them into a single full scan
1323                bundle = new Bundle();
1324                bundle.putParcelable(CUSTOMIZED_SCAN_SETTING, null);
1325                bundle.putParcelable(CUSTOMIZED_SCAN_WORKSOURCE, workSource);
1326                Message msg = obtainMessage(CMD_START_SCAN, message.arg1, 0, bundle);
1327                mBufferedScanMsg.clear();
1328                mBufferedScanMsg.add(msg);
1329            }
1330        }
1331    }
1332
1333
1334    /** return true iff scan request is accepted */
1335    private boolean startScanNative(int type, String freqs) {
1336        if (mWifiNative.scan(type, freqs)) {
1337            mIsScanOngoing = true;
1338            mIsFullScanOngoing = (freqs == null);
1339            return true;
1340        }
1341        return false;
1342    }
1343
1344    /**
1345     * TODO: doc
1346     */
1347    public void setSupplicantRunning(boolean enable) {
1348        if (enable) {
1349            sendMessage(CMD_START_SUPPLICANT);
1350        } else {
1351            sendMessage(CMD_STOP_SUPPLICANT);
1352        }
1353    }
1354
1355    /**
1356     * TODO: doc
1357     */
1358    public void setHostApRunning(WifiConfiguration wifiConfig, boolean enable) {
1359        if (enable) {
1360            sendMessage(CMD_START_AP, wifiConfig);
1361        } else {
1362            sendMessage(CMD_STOP_AP);
1363        }
1364    }
1365
1366    public void setWifiApConfiguration(WifiConfiguration config) {
1367        mWifiApConfigChannel.sendMessage(CMD_SET_AP_CONFIG, config);
1368    }
1369
1370    public WifiConfiguration syncGetWifiApConfiguration() {
1371        Message resultMsg = mWifiApConfigChannel.sendMessageSynchronously(CMD_REQUEST_AP_CONFIG);
1372        WifiConfiguration ret = (WifiConfiguration) resultMsg.obj;
1373        resultMsg.recycle();
1374        return ret;
1375    }
1376
1377    /**
1378     * TODO: doc
1379     */
1380    public int syncGetWifiState() {
1381        return mWifiState.get();
1382    }
1383
1384    /**
1385     * TODO: doc
1386     */
1387    public String syncGetWifiStateByName() {
1388        switch (mWifiState.get()) {
1389            case WIFI_STATE_DISABLING:
1390                return "disabling";
1391            case WIFI_STATE_DISABLED:
1392                return "disabled";
1393            case WIFI_STATE_ENABLING:
1394                return "enabling";
1395            case WIFI_STATE_ENABLED:
1396                return "enabled";
1397            case WIFI_STATE_UNKNOWN:
1398                return "unknown state";
1399            default:
1400                return "[invalid state]";
1401        }
1402    }
1403
1404    /**
1405     * TODO: doc
1406     */
1407    public int syncGetWifiApState() {
1408        return mWifiApState.get();
1409    }
1410
1411    /**
1412     * TODO: doc
1413     */
1414    public String syncGetWifiApStateByName() {
1415        switch (mWifiApState.get()) {
1416            case WIFI_AP_STATE_DISABLING:
1417                return "disabling";
1418            case WIFI_AP_STATE_DISABLED:
1419                return "disabled";
1420            case WIFI_AP_STATE_ENABLING:
1421                return "enabling";
1422            case WIFI_AP_STATE_ENABLED:
1423                return "enabled";
1424            case WIFI_AP_STATE_FAILED:
1425                return "failed";
1426            default:
1427                return "[invalid state]";
1428        }
1429    }
1430
1431    /**
1432     * Get status information for the current connection, if any.
1433     * @return a {@link WifiInfo} object containing information about the current connection
1434     *
1435     */
1436    public WifiInfo syncRequestConnectionInfo() {
1437        return mWifiInfo;
1438    }
1439
1440    public DhcpResults syncGetDhcpResults() {
1441        synchronized (mDhcpResultsLock) {
1442            return new DhcpResults(mDhcpResults);
1443        }
1444    }
1445
1446    /**
1447     * TODO: doc
1448     */
1449    public void setDriverStart(boolean enable) {
1450        if (enable) {
1451            sendMessage(CMD_START_DRIVER);
1452        } else {
1453            sendMessage(CMD_STOP_DRIVER);
1454        }
1455    }
1456
1457    /**
1458     * TODO: doc
1459     */
1460    public void setOperationalMode(int mode) {
1461        if (DBG) log("setting operational mode to " + String.valueOf(mode));
1462        sendMessage(CMD_SET_OPERATIONAL_MODE, mode, 0);
1463    }
1464
1465    /**
1466     * TODO: doc
1467     */
1468    public List<ScanResult> syncGetScanResultsList() {
1469        synchronized (mScanResultCache) {
1470            List<ScanResult> scanList = new ArrayList<ScanResult>();
1471            for(ScanResult result: mScanResults) {
1472                scanList.add(new ScanResult(result));
1473            }
1474            return scanList;
1475        }
1476    }
1477
1478    /**
1479     * Disconnect from Access Point
1480     */
1481    public void disconnectCommand() {
1482        sendMessage(CMD_DISCONNECT);
1483    }
1484
1485    /**
1486     * Initiate a reconnection to AP
1487     */
1488    public void reconnectCommand() {
1489        sendMessage(CMD_RECONNECT);
1490    }
1491
1492    /**
1493     * Initiate a re-association to AP
1494     */
1495    public void reassociateCommand() {
1496        sendMessage(CMD_REASSOCIATE);
1497    }
1498
1499    /**
1500     * Reload networks and then reconnect; helps load correct data for TLS networks
1501     */
1502
1503    public void reloadTlsNetworksAndReconnect() {
1504        sendMessage(CMD_RELOAD_TLS_AND_RECONNECT);
1505    }
1506
1507    /**
1508     * Add a network synchronously
1509     *
1510     * @return network id of the new network
1511     */
1512    public int syncAddOrUpdateNetwork(AsyncChannel channel, WifiConfiguration config) {
1513        Message resultMsg = channel.sendMessageSynchronously(CMD_ADD_OR_UPDATE_NETWORK, config);
1514        int result = resultMsg.arg1;
1515        resultMsg.recycle();
1516        return result;
1517    }
1518
1519    public List<WifiConfiguration> syncGetConfiguredNetworks(AsyncChannel channel) {
1520        Message resultMsg = channel.sendMessageSynchronously(CMD_GET_CONFIGURED_NETWORKS);
1521        List<WifiConfiguration> result = (List<WifiConfiguration>) resultMsg.obj;
1522        resultMsg.recycle();
1523        return result;
1524    }
1525
1526    /**
1527     * Delete a network
1528     *
1529     * @param networkId id of the network to be removed
1530     */
1531    public boolean syncRemoveNetwork(AsyncChannel channel, int networkId) {
1532        Message resultMsg = channel.sendMessageSynchronously(CMD_REMOVE_NETWORK, networkId);
1533        boolean result = (resultMsg.arg1 != FAILURE);
1534        resultMsg.recycle();
1535        return result;
1536    }
1537
1538    /**
1539     * Enable a network
1540     *
1541     * @param netId network id of the network
1542     * @param disableOthers true, if all other networks have to be disabled
1543     * @return {@code true} if the operation succeeds, {@code false} otherwise
1544     */
1545    public boolean syncEnableNetwork(AsyncChannel channel, int netId, boolean disableOthers) {
1546        Message resultMsg = channel.sendMessageSynchronously(CMD_ENABLE_NETWORK, netId,
1547                disableOthers ? 1 : 0);
1548        boolean result = (resultMsg.arg1 != FAILURE);
1549        resultMsg.recycle();
1550        return result;
1551    }
1552
1553    /**
1554     * Disable a network
1555     *
1556     * @param netId network id of the network
1557     * @return {@code true} if the operation succeeds, {@code false} otherwise
1558     */
1559    public boolean syncDisableNetwork(AsyncChannel channel, int netId) {
1560        Message resultMsg = channel.sendMessageSynchronously(WifiManager.DISABLE_NETWORK, netId);
1561        boolean result = (resultMsg.arg1 != WifiManager.DISABLE_NETWORK_FAILED);
1562        resultMsg.recycle();
1563        return result;
1564    }
1565
1566    /**
1567     * Retrieves a WPS-NFC configuration token for the specified network
1568     * @return a hex string representation of the WPS-NFC configuration token
1569     */
1570    public String syncGetWpsNfcConfigurationToken(int netId) {
1571        return mWifiNative.getNfcWpsConfigurationToken(netId);
1572    }
1573
1574    /**
1575     * Blacklist a BSSID. This will avoid the AP if there are
1576     * alternate APs to connect
1577     *
1578     * @param bssid BSSID of the network
1579     */
1580    public void addToBlacklist(String bssid) {
1581        sendMessage(CMD_BLACKLIST_NETWORK, bssid);
1582    }
1583
1584    /**
1585     * Clear the blacklist list
1586     *
1587     */
1588    public void clearBlacklist() {
1589        sendMessage(CMD_CLEAR_BLACKLIST);
1590    }
1591
1592    public void enableRssiPolling(boolean enabled) {
1593       sendMessage(CMD_ENABLE_RSSI_POLL, enabled ? 1 : 0, 0);
1594    }
1595
1596    public void enableBackgroundScanCommand(boolean enabled) {
1597       sendMessage(CMD_ENABLE_BACKGROUND_SCAN, enabled ? 1 : 0, 0);
1598    }
1599
1600    public void enableAllNetworks() {
1601        sendMessage(CMD_ENABLE_ALL_NETWORKS);
1602    }
1603
1604    /**
1605     * Start filtering Multicast v4 packets
1606     */
1607    public void startFilteringMulticastV4Packets() {
1608        mFilteringMulticastV4Packets.set(true);
1609        sendMessage(CMD_START_PACKET_FILTERING, MULTICAST_V4, 0);
1610    }
1611
1612    /**
1613     * Stop filtering Multicast v4 packets
1614     */
1615    public void stopFilteringMulticastV4Packets() {
1616        mFilteringMulticastV4Packets.set(false);
1617        sendMessage(CMD_STOP_PACKET_FILTERING, MULTICAST_V4, 0);
1618    }
1619
1620    /**
1621     * Start filtering Multicast v4 packets
1622     */
1623    public void startFilteringMulticastV6Packets() {
1624        sendMessage(CMD_START_PACKET_FILTERING, MULTICAST_V6, 0);
1625    }
1626
1627    /**
1628     * Stop filtering Multicast v4 packets
1629     */
1630    public void stopFilteringMulticastV6Packets() {
1631        sendMessage(CMD_STOP_PACKET_FILTERING, MULTICAST_V6, 0);
1632    }
1633
1634    /**
1635     * Set high performance mode of operation.
1636     * Enabling would set active power mode and disable suspend optimizations;
1637     * disabling would set auto power mode and enable suspend optimizations
1638     * @param enable true if enable, false otherwise
1639     */
1640    public void setHighPerfModeEnabled(boolean enable) {
1641        sendMessage(CMD_SET_HIGH_PERF_MODE, enable ? 1 : 0, 0);
1642    }
1643
1644    /**
1645     * Set the country code
1646     * @param countryCode following ISO 3166 format
1647     * @param persist {@code true} if the setting should be remembered.
1648     */
1649    public void setCountryCode(String countryCode, boolean persist) {
1650        if (persist) {
1651            mPersistedCountryCode = countryCode;
1652            Settings.Global.putString(mContext.getContentResolver(),
1653                    Settings.Global.WIFI_COUNTRY_CODE,
1654                    countryCode);
1655        }
1656        sendMessage(CMD_SET_COUNTRY_CODE, countryCode);
1657        mWifiP2pChannel.sendMessage(WifiP2pServiceImpl.SET_COUNTRY_CODE, countryCode);
1658    }
1659
1660    /**
1661     * Set the operational frequency band
1662     * @param band
1663     * @param persist {@code true} if the setting should be remembered.
1664     */
1665    public void setFrequencyBand(int band, boolean persist) {
1666        if (persist) {
1667            Settings.Global.putInt(mContext.getContentResolver(),
1668                    Settings.Global.WIFI_FREQUENCY_BAND,
1669                    band);
1670        }
1671        sendMessage(CMD_SET_FREQUENCY_BAND, band, 0);
1672    }
1673
1674    /**
1675     * Enable TDLS for a specific MAC address
1676     */
1677    public void enableTdls(String remoteMacAddress, boolean enable) {
1678        int enabler = enable ? 1 : 0;
1679        sendMessage(CMD_ENABLE_TDLS, enabler, 0, remoteMacAddress);
1680    }
1681
1682    /**
1683     * Returns the operational frequency band
1684     */
1685    public int getFrequencyBand() {
1686        return mFrequencyBand.get();
1687    }
1688
1689    /**
1690     * Returns the wifi configuration file
1691     */
1692    public String getConfigFile() {
1693        return mWifiConfigStore.getConfigFile();
1694    }
1695
1696    /**
1697     * Send a message indicating bluetooth adapter connection state changed
1698     */
1699    public void sendBluetoothAdapterStateChange(int state) {
1700        sendMessage(CMD_BLUETOOTH_ADAPTER_STATE_CHANGE, state, 0);
1701    }
1702
1703    /**
1704     * Save configuration on supplicant
1705     *
1706     * @return {@code true} if the operation succeeds, {@code false} otherwise
1707     *
1708     * TODO: deprecate this
1709     */
1710    public boolean syncSaveConfig(AsyncChannel channel) {
1711        Message resultMsg = channel.sendMessageSynchronously(CMD_SAVE_CONFIG);
1712        boolean result = (resultMsg.arg1 != FAILURE);
1713        resultMsg.recycle();
1714        return result;
1715    }
1716
1717    public void updateBatteryWorkSource(WorkSource newSource) {
1718        synchronized (mRunningWifiUids) {
1719            try {
1720                if (newSource != null) {
1721                    mRunningWifiUids.set(newSource);
1722                }
1723                if (mIsRunning) {
1724                    if (mReportedRunning) {
1725                        // If the work source has changed since last time, need
1726                        // to remove old work from battery stats.
1727                        if (mLastRunningWifiUids.diff(mRunningWifiUids)) {
1728                            mBatteryStats.noteWifiRunningChanged(mLastRunningWifiUids,
1729                                    mRunningWifiUids);
1730                            mLastRunningWifiUids.set(mRunningWifiUids);
1731                        }
1732                    } else {
1733                        // Now being started, report it.
1734                        mBatteryStats.noteWifiRunning(mRunningWifiUids);
1735                        mLastRunningWifiUids.set(mRunningWifiUids);
1736                        mReportedRunning = true;
1737                    }
1738                } else {
1739                    if (mReportedRunning) {
1740                        // Last reported we were running, time to stop.
1741                        mBatteryStats.noteWifiStopped(mLastRunningWifiUids);
1742                        mLastRunningWifiUids.clear();
1743                        mReportedRunning = false;
1744                    }
1745                }
1746                mWakeLock.setWorkSource(newSource);
1747            } catch (RemoteException ignore) {
1748            }
1749        }
1750    }
1751
1752    @Override
1753    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1754        super.dump(fd, pw, args);
1755        mSupplicantStateTracker.dump(fd, pw, args);
1756        pw.println("mLinkProperties " + mLinkProperties);
1757        pw.println("mWifiInfo " + mWifiInfo);
1758        pw.println("mDhcpResults " + mDhcpResults);
1759        pw.println("mNetworkInfo " + mNetworkInfo);
1760        pw.println("mLastSignalLevel " + mLastSignalLevel);
1761        pw.println("mLastBssid " + mLastBssid);
1762        pw.println("mLastNetworkId " + mLastNetworkId);
1763        pw.println("mReconnectCount " + mReconnectCount);
1764        pw.println("mOperationalMode " + mOperationalMode);
1765        pw.println("mUserWantsSuspendOpt " + mUserWantsSuspendOpt);
1766        pw.println("mSuspendOptNeedsDisabled " + mSuspendOptNeedsDisabled);
1767        pw.println("Supplicant status " + mWifiNative.status());
1768        pw.println("mEnableBackgroundScan " + mEnableBackgroundScan);
1769        pw.println();
1770        mWifiConfigStore.dump(fd, pw, args);
1771    }
1772
1773    /*********************************************************
1774     * Internal private functions
1775     ********************************************************/
1776
1777    private void handleScreenStateChanged(boolean screenOn) {
1778        if (DBG) log("handleScreenStateChanged: " + screenOn);
1779        enableRssiPolling(screenOn);
1780        if (mBackgroundScanSupported) {
1781            enableBackgroundScanCommand(screenOn == false);
1782        }
1783
1784        if (screenOn) enableAllNetworks();
1785        if (mUserWantsSuspendOpt.get()) {
1786            if (screenOn) {
1787                sendMessage(CMD_SET_SUSPEND_OPT_ENABLED, 0, 0);
1788            } else {
1789                //Allow 2s for suspend optimizations to be set
1790                mSuspendWakeLock.acquire(2000);
1791                sendMessage(CMD_SET_SUSPEND_OPT_ENABLED, 1, 0);
1792            }
1793        }
1794        mScreenBroadcastReceived.set(true);
1795    }
1796
1797    private void checkAndSetConnectivityInstance() {
1798        if (mCm == null) {
1799            mCm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
1800        }
1801    }
1802
1803    private boolean startTethering(ArrayList<String> available) {
1804
1805        boolean wifiAvailable = false;
1806
1807        checkAndSetConnectivityInstance();
1808
1809        String[] wifiRegexs = mCm.getTetherableWifiRegexs();
1810
1811        for (String intf : available) {
1812            for (String regex : wifiRegexs) {
1813                if (intf.matches(regex)) {
1814
1815                    InterfaceConfiguration ifcg = null;
1816                    try {
1817                        ifcg = mNwService.getInterfaceConfig(intf);
1818                        if (ifcg != null) {
1819                            /* IP/netmask: 192.168.43.1/255.255.255.0 */
1820                            ifcg.setLinkAddress(new LinkAddress(
1821                                    NetworkUtils.numericToInetAddress("192.168.43.1"), 24));
1822                            ifcg.setInterfaceUp();
1823
1824                            mNwService.setInterfaceConfig(intf, ifcg);
1825                        }
1826                    } catch (Exception e) {
1827                        loge("Error configuring interface " + intf + ", :" + e);
1828                        return false;
1829                    }
1830
1831                    if(mCm.tether(intf) != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
1832                        loge("Error tethering on " + intf);
1833                        return false;
1834                    }
1835                    mTetherInterfaceName = intf;
1836                    return true;
1837                }
1838            }
1839        }
1840        // We found no interfaces to tether
1841        return false;
1842    }
1843
1844    private void stopTethering() {
1845
1846        checkAndSetConnectivityInstance();
1847
1848        /* Clear the interface config to allow dhcp correctly configure new
1849           ip settings */
1850        InterfaceConfiguration ifcg = null;
1851        try {
1852            ifcg = mNwService.getInterfaceConfig(mTetherInterfaceName);
1853            if (ifcg != null) {
1854                ifcg.setLinkAddress(
1855                        new LinkAddress(NetworkUtils.numericToInetAddress("0.0.0.0"), 0));
1856                mNwService.setInterfaceConfig(mTetherInterfaceName, ifcg);
1857            }
1858        } catch (Exception e) {
1859            loge("Error resetting interface " + mTetherInterfaceName + ", :" + e);
1860        }
1861
1862        if (mCm.untether(mTetherInterfaceName) != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
1863            loge("Untether initiate failed!");
1864        }
1865    }
1866
1867    private boolean isWifiTethered(ArrayList<String> active) {
1868
1869        checkAndSetConnectivityInstance();
1870
1871        String[] wifiRegexs = mCm.getTetherableWifiRegexs();
1872        for (String intf : active) {
1873            for (String regex : wifiRegexs) {
1874                if (intf.matches(regex)) {
1875                    return true;
1876                }
1877            }
1878        }
1879        // We found no interfaces that are tethered
1880        return false;
1881    }
1882
1883    /**
1884     * Set the country code from the system setting value, if any.
1885     */
1886    private void setCountryCode() {
1887        String countryCode = Settings.Global.getString(mContext.getContentResolver(),
1888                Settings.Global.WIFI_COUNTRY_CODE);
1889        if (countryCode != null && !countryCode.isEmpty()) {
1890            setCountryCode(countryCode, false);
1891        } else {
1892            //use driver default
1893        }
1894    }
1895
1896    /**
1897     * Set the frequency band from the system setting value, if any.
1898     */
1899    private void setFrequencyBand() {
1900        int band = Settings.Global.getInt(mContext.getContentResolver(),
1901                Settings.Global.WIFI_FREQUENCY_BAND, WifiManager.WIFI_FREQUENCY_BAND_AUTO);
1902        setFrequencyBand(band, false);
1903    }
1904
1905    private void setSuspendOptimizationsNative(int reason, boolean enabled) {
1906        if (DBG) log("setSuspendOptimizationsNative: " + reason + " " + enabled);
1907        if (enabled) {
1908            mSuspendOptNeedsDisabled &= ~reason;
1909            /* None of dhcp, screen or highperf need it disabled and user wants it enabled */
1910            if (mSuspendOptNeedsDisabled == 0 && mUserWantsSuspendOpt.get()) {
1911                mWifiNative.setSuspendOptimizations(true);
1912            }
1913        } else {
1914            mSuspendOptNeedsDisabled |= reason;
1915            mWifiNative.setSuspendOptimizations(false);
1916        }
1917    }
1918
1919    private void setSuspendOptimizations(int reason, boolean enabled) {
1920        if (DBG) log("setSuspendOptimizations: " + reason + " " + enabled);
1921        if (enabled) {
1922            mSuspendOptNeedsDisabled &= ~reason;
1923        } else {
1924            mSuspendOptNeedsDisabled |= reason;
1925        }
1926        if (DBG) log("mSuspendOptNeedsDisabled " + mSuspendOptNeedsDisabled);
1927    }
1928
1929    private void setWifiState(int wifiState) {
1930        final int previousWifiState = mWifiState.get();
1931
1932        try {
1933            if (wifiState == WIFI_STATE_ENABLED) {
1934                mBatteryStats.noteWifiOn();
1935            } else if (wifiState == WIFI_STATE_DISABLED) {
1936                mBatteryStats.noteWifiOff();
1937            }
1938        } catch (RemoteException e) {
1939            loge("Failed to note battery stats in wifi");
1940        }
1941
1942        mWifiState.set(wifiState);
1943
1944        if (DBG) log("setWifiState: " + syncGetWifiStateByName());
1945
1946        final Intent intent = new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION);
1947        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1948        intent.putExtra(WifiManager.EXTRA_WIFI_STATE, wifiState);
1949        intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_STATE, previousWifiState);
1950        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
1951    }
1952
1953    private void setWifiApState(int wifiApState) {
1954        final int previousWifiApState = mWifiApState.get();
1955
1956        try {
1957            if (wifiApState == WIFI_AP_STATE_ENABLED) {
1958                mBatteryStats.noteWifiOn();
1959            } else if (wifiApState == WIFI_AP_STATE_DISABLED) {
1960                mBatteryStats.noteWifiOff();
1961            }
1962        } catch (RemoteException e) {
1963            loge("Failed to note battery stats in wifi");
1964        }
1965
1966        // Update state
1967        mWifiApState.set(wifiApState);
1968
1969        if (DBG) log("setWifiApState: " + syncGetWifiApStateByName());
1970
1971        final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
1972        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1973        intent.putExtra(WifiManager.EXTRA_WIFI_AP_STATE, wifiApState);
1974        intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_AP_STATE, previousWifiApState);
1975        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
1976    }
1977
1978    private static final String ID_STR = "id=";
1979    private static final String BSSID_STR = "bssid=";
1980    private static final String FREQ_STR = "freq=";
1981    private static final String LEVEL_STR = "level=";
1982    private static final String TSF_STR = "tsf=";
1983    private static final String FLAGS_STR = "flags=";
1984    private static final String SSID_STR = "ssid=";
1985    private static final String DELIMITER_STR = "====";
1986    private static final String END_STR = "####";
1987
1988    /**
1989     * Format:
1990     *
1991     * id=1
1992     * bssid=68:7f:76:d7:1a:6e
1993     * freq=2412
1994     * level=-44
1995     * tsf=1344626243700342
1996     * flags=[WPA2-PSK-CCMP][WPS][ESS]
1997     * ssid=zfdy
1998     * ====
1999     * id=2
2000     * bssid=68:5f:74:d7:1a:6f
2001     * freq=5180
2002     * level=-73
2003     * tsf=1344626243700373
2004     * flags=[WPA2-PSK-CCMP][WPS][ESS]
2005     * ssid=zuby
2006     * ====
2007     */
2008    private void setScanResults() {
2009        String bssid = "";
2010        int level = 0;
2011        int freq = 0;
2012        long tsf = 0;
2013        String flags = "";
2014        WifiSsid wifiSsid = null;
2015        String scanResults;
2016        String tmpResults;
2017        StringBuffer scanResultsBuf = new StringBuffer();
2018        int sid = 0;
2019
2020        while (true) {
2021            tmpResults = mWifiNative.scanResults(sid);
2022            if (TextUtils.isEmpty(tmpResults)) break;
2023            scanResultsBuf.append(tmpResults);
2024            scanResultsBuf.append("\n");
2025            String[] lines = tmpResults.split("\n");
2026            sid = -1;
2027            for (int i=lines.length - 1; i >= 0; i--) {
2028                if (lines[i].startsWith(END_STR)) {
2029                    break;
2030                } else if (lines[i].startsWith(ID_STR)) {
2031                    try {
2032                        sid = Integer.parseInt(lines[i].substring(ID_STR.length())) + 1;
2033                    } catch (NumberFormatException e) {
2034                        // Nothing to do
2035                    }
2036                    break;
2037                }
2038            }
2039            if (sid == -1) break;
2040        }
2041
2042        scanResults = scanResultsBuf.toString();
2043        if (TextUtils.isEmpty(scanResults)) {
2044           return;
2045        }
2046
2047        // note that all these splits and substrings keep references to the original
2048        // huge string buffer while the amount we really want is generally pretty small
2049        // so make copies instead (one example b/11087956 wasted 400k of heap here).
2050        synchronized(mScanResultCache) {
2051            mScanResults = new ArrayList<ScanResult>();
2052            String[] lines = scanResults.split("\n");
2053            final int bssidStrLen = BSSID_STR.length();
2054            final int flagLen = FLAGS_STR.length();
2055
2056            for (String line : lines) {
2057                if (line.startsWith(BSSID_STR)) {
2058                    bssid = new String(line.getBytes(), bssidStrLen, line.length() - bssidStrLen);
2059                } else if (line.startsWith(FREQ_STR)) {
2060                    try {
2061                        freq = Integer.parseInt(line.substring(FREQ_STR.length()));
2062                    } catch (NumberFormatException e) {
2063                        freq = 0;
2064                    }
2065                } else if (line.startsWith(LEVEL_STR)) {
2066                    try {
2067                        level = Integer.parseInt(line.substring(LEVEL_STR.length()));
2068                        /* some implementations avoid negative values by adding 256
2069                         * so we need to adjust for that here.
2070                         */
2071                        if (level > 0) level -= 256;
2072                    } catch(NumberFormatException e) {
2073                        level = 0;
2074                    }
2075                } else if (line.startsWith(TSF_STR)) {
2076                    try {
2077                        tsf = Long.parseLong(line.substring(TSF_STR.length()));
2078                    } catch (NumberFormatException e) {
2079                        tsf = 0;
2080                    }
2081                } else if (line.startsWith(FLAGS_STR)) {
2082                    flags = new String(line.getBytes(), flagLen, line.length() - flagLen);
2083                } else if (line.startsWith(SSID_STR)) {
2084                    wifiSsid = WifiSsid.createFromAsciiEncoded(
2085                            line.substring(SSID_STR.length()));
2086                } else if (line.startsWith(DELIMITER_STR) || line.startsWith(END_STR)) {
2087                    if (bssid != null) {
2088                        String ssid = (wifiSsid != null) ? wifiSsid.toString() : WifiSsid.NONE;
2089                        String key = bssid + ssid;
2090                        ScanResult scanResult = mScanResultCache.get(key);
2091                        if (scanResult != null) {
2092                            scanResult.level = level;
2093                            scanResult.wifiSsid = wifiSsid;
2094                            // Keep existing API
2095                            scanResult.SSID = (wifiSsid != null) ? wifiSsid.toString() :
2096                                    WifiSsid.NONE;
2097                            scanResult.capabilities = flags;
2098                            scanResult.frequency = freq;
2099                            scanResult.timestamp = tsf;
2100                        } else {
2101                            scanResult =
2102                                new ScanResult(
2103                                        wifiSsid, bssid, flags, level, freq, tsf);
2104                            mScanResultCache.put(key, scanResult);
2105                        }
2106                        mScanResults.add(scanResult);
2107                    }
2108                    bssid = null;
2109                    level = 0;
2110                    freq = 0;
2111                    tsf = 0;
2112                    flags = "";
2113                    wifiSsid = null;
2114                }
2115            }
2116        }
2117    }
2118
2119    /*
2120     * Fetch RSSI and linkspeed on current connection
2121     */
2122    private void fetchRssiAndLinkSpeedNative() {
2123        int newRssi = -1;
2124        int newLinkSpeed = -1;
2125
2126        String signalPoll = mWifiNative.signalPoll();
2127
2128        if (signalPoll != null) {
2129            String[] lines = signalPoll.split("\n");
2130            for (String line : lines) {
2131                String[] prop = line.split("=");
2132                if (prop.length < 2) continue;
2133                try {
2134                    if (prop[0].equals("RSSI")) {
2135                        newRssi = Integer.parseInt(prop[1]);
2136                    } else if (prop[0].equals("LINKSPEED")) {
2137                        newLinkSpeed = Integer.parseInt(prop[1]);
2138                    }
2139                } catch (NumberFormatException e) {
2140                    //Ignore, defaults on rssi and linkspeed are assigned
2141                }
2142            }
2143        }
2144
2145        if (newRssi != -1 && MIN_RSSI < newRssi && newRssi < MAX_RSSI) { // screen out invalid values
2146            /* some implementations avoid negative values by adding 256
2147             * so we need to adjust for that here.
2148             */
2149            if (newRssi > 0) newRssi -= 256;
2150            mWifiInfo.setRssi(newRssi);
2151            /*
2152             * Rather then sending the raw RSSI out every time it
2153             * changes, we precalculate the signal level that would
2154             * be displayed in the status bar, and only send the
2155             * broadcast if that much more coarse-grained number
2156             * changes. This cuts down greatly on the number of
2157             * broadcasts, at the cost of not informing others
2158             * interested in RSSI of all the changes in signal
2159             * level.
2160             */
2161            int newSignalLevel = WifiManager.calculateSignalLevel(newRssi, WifiManager.RSSI_LEVELS);
2162            if (newSignalLevel != mLastSignalLevel) {
2163                sendRssiChangeBroadcast(newRssi);
2164            }
2165            mLastSignalLevel = newSignalLevel;
2166        } else {
2167            mWifiInfo.setRssi(MIN_RSSI);
2168        }
2169
2170        if (newLinkSpeed != -1) {
2171            mWifiInfo.setLinkSpeed(newLinkSpeed);
2172        }
2173    }
2174
2175    /*
2176     * Fetch TX packet counters on current connection
2177     */
2178    private void fetchPktcntNative(RssiPacketCountInfo info) {
2179        String pktcntPoll = mWifiNative.pktcntPoll();
2180
2181        if (pktcntPoll != null) {
2182            String[] lines = pktcntPoll.split("\n");
2183            for (String line : lines) {
2184                String[] prop = line.split("=");
2185                if (prop.length < 2) continue;
2186                try {
2187                    if (prop[0].equals("TXGOOD")) {
2188                        info.txgood = Integer.parseInt(prop[1]);
2189                    } else if (prop[0].equals("TXBAD")) {
2190                        info.txbad = Integer.parseInt(prop[1]);
2191                    }
2192                } catch (NumberFormatException e) {
2193                    //Ignore
2194                }
2195            }
2196        }
2197    }
2198
2199    /**
2200     * Updates mLinkProperties by merging information from various sources.
2201     *
2202     * This is needed because the information in mLinkProperties comes from multiple sources (DHCP,
2203     * netlink, static configuration, ...). When one of these sources of information has updated
2204     * link properties, we can't just assign them to mLinkProperties or we'd lose track of the
2205     * information that came from other sources. Instead, when one of those sources has new
2206     * information, we update the object that tracks the information from that source and then
2207     * call this method to apply the change to mLinkProperties.
2208     *
2209     * The information in mLinkProperties is currently obtained as follows:
2210     * - Interface name: set in the constructor.
2211     * - IPv4 and IPv6 addresses: netlink, via mInterfaceObserver.
2212     * - IPv4 routes, DNS servers, and domains: DHCP.
2213     * - HTTP proxy: the wifi config store.
2214     */
2215    private void updateLinkProperties() {
2216        LinkProperties newLp = new LinkProperties();
2217
2218        // Interface name and proxy are locally configured.
2219        newLp.setInterfaceName(mInterfaceName);
2220        newLp.setHttpProxy(mWifiConfigStore.getProxyProperties(mLastNetworkId));
2221
2222        // IPv4 and IPv6 addresses come from netlink.
2223        newLp.setLinkAddresses(mNetlinkLinkProperties.getLinkAddresses());
2224
2225        // For now, routing and DNS only come from DHCP or static configuration. In the future,
2226        // we'll need to merge IPv6 DNS servers and domains coming from netlink.
2227        synchronized (mDhcpResultsLock) {
2228            // Even when we're using static configuration, we don't need to look at the config
2229            // store, because static IP configuration also populates mDhcpResults.
2230            if ((mDhcpResults != null) && (mDhcpResults.linkProperties != null)) {
2231                LinkProperties lp = mDhcpResults.linkProperties;
2232                for (RouteInfo route: lp.getRoutes()) {
2233                    newLp.addRoute(route);
2234                }
2235                for (InetAddress dns: lp.getDnses()) {
2236                    newLp.addDns(dns);
2237                }
2238                newLp.setDomains(lp.getDomains());
2239            }
2240        }
2241
2242        // If anything has changed, and we're already connected, send out a notification.
2243        // If we're still connecting, apps will be notified when we connect.
2244        if (!newLp.equals(mLinkProperties)) {
2245            if (DBG) {
2246                log("Link configuration changed for netId: " + mLastNetworkId
2247                        + " old: " + mLinkProperties + "new: " + newLp);
2248            }
2249            mLinkProperties = newLp;
2250            if (getNetworkDetailedState() == DetailedState.CONNECTED) {
2251                sendLinkConfigurationChangedBroadcast();
2252            }
2253        }
2254    }
2255
2256    /**
2257     * Clears all our link properties.
2258     */
2259    private void clearLinkProperties() {
2260        // If the network used DHCP, clear the LinkProperties we stored in the config store.
2261        if (!mWifiConfigStore.isUsingStaticIp(mLastNetworkId)) {
2262            mWifiConfigStore.clearLinkProperties(mLastNetworkId);
2263        }
2264
2265        // Clear the link properties obtained from DHCP and netlink.
2266        synchronized(mDhcpResultsLock) {
2267            if (mDhcpResults != null && mDhcpResults.linkProperties != null) {
2268                mDhcpResults.linkProperties.clear();
2269            }
2270        }
2271        mNetlinkLinkProperties.clear();
2272
2273        // Now clear the merged link properties.
2274        mLinkProperties.clear();
2275    }
2276
2277    private int getMaxDhcpRetries() {
2278        return Settings.Global.getInt(mContext.getContentResolver(),
2279                                      Settings.Global.WIFI_MAX_DHCP_RETRY_COUNT,
2280                                      DEFAULT_MAX_DHCP_RETRIES);
2281    }
2282
2283    private void sendScanResultsAvailableBroadcast() {
2284        noteScanEnd();
2285        Intent intent = new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
2286        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
2287        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
2288    }
2289
2290    private void sendRssiChangeBroadcast(final int newRssi) {
2291        Intent intent = new Intent(WifiManager.RSSI_CHANGED_ACTION);
2292        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
2293        intent.putExtra(WifiManager.EXTRA_NEW_RSSI, newRssi);
2294        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
2295    }
2296
2297    private void sendNetworkStateChangeBroadcast(String bssid) {
2298        Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
2299        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
2300        intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, new NetworkInfo(mNetworkInfo));
2301        intent.putExtra(WifiManager.EXTRA_LINK_PROPERTIES, new LinkProperties (mLinkProperties));
2302        if (bssid != null)
2303            intent.putExtra(WifiManager.EXTRA_BSSID, bssid);
2304        if (mNetworkInfo.getDetailedState() == DetailedState.VERIFYING_POOR_LINK ||
2305                mNetworkInfo.getDetailedState() == DetailedState.CONNECTED) {
2306            intent.putExtra(WifiManager.EXTRA_WIFI_INFO, new WifiInfo(mWifiInfo));
2307        }
2308        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
2309    }
2310
2311    private void sendLinkConfigurationChangedBroadcast() {
2312        Intent intent = new Intent(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION);
2313        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
2314        intent.putExtra(WifiManager.EXTRA_LINK_PROPERTIES, new LinkProperties(mLinkProperties));
2315        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
2316    }
2317
2318    private void sendSupplicantConnectionChangedBroadcast(boolean connected) {
2319        Intent intent = new Intent(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
2320        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
2321        intent.putExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, connected);
2322        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
2323    }
2324
2325    /**
2326     * Record the detailed state of a network.
2327     * @param state the new {@code DetailedState}
2328     */
2329    private void setNetworkDetailedState(NetworkInfo.DetailedState state) {
2330        if (DBG) {
2331            log("setDetailed state, old ="
2332                    + mNetworkInfo.getDetailedState() + " and new state=" + state);
2333        }
2334
2335        if (state != mNetworkInfo.getDetailedState()) {
2336            mNetworkInfo.setDetailedState(state, null, mWifiInfo.getSSID());
2337        }
2338    }
2339
2340    private DetailedState getNetworkDetailedState() {
2341        return mNetworkInfo.getDetailedState();
2342    }
2343
2344
2345    private SupplicantState handleSupplicantStateChange(Message message) {
2346        StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
2347        SupplicantState state = stateChangeResult.state;
2348        // Supplicant state change
2349        // [31-13] Reserved for future use
2350        // [8 - 0] Supplicant state (as defined in SupplicantState.java)
2351        // 50023 supplicant_state_changed (custom|1|5)
2352        mWifiInfo.setSupplicantState(state);
2353        // Network id is only valid when we start connecting
2354        if (SupplicantState.isConnecting(state)) {
2355            mWifiInfo.setNetworkId(stateChangeResult.networkId);
2356        } else {
2357            mWifiInfo.setNetworkId(WifiConfiguration.INVALID_NETWORK_ID);
2358        }
2359
2360        mWifiInfo.setBSSID(stateChangeResult.BSSID);
2361        mWifiInfo.setSSID(stateChangeResult.wifiSsid);
2362
2363        mSupplicantStateTracker.sendMessage(Message.obtain(message));
2364
2365        return state;
2366    }
2367
2368    /**
2369     * Resets the Wi-Fi Connections by clearing any state, resetting any sockets
2370     * using the interface, stopping DHCP & disabling interface
2371     */
2372    private void handleNetworkDisconnect() {
2373        if (DBG) log("Stopping DHCP and clearing IP");
2374
2375        stopDhcp();
2376
2377        try {
2378            mNwService.clearInterfaceAddresses(mInterfaceName);
2379            mNwService.disableIpv6(mInterfaceName);
2380        } catch (Exception e) {
2381            loge("Failed to clear addresses or disable ipv6" + e);
2382        }
2383
2384        /* Reset data structures */
2385        mWifiInfo.setInetAddress(null);
2386        mWifiInfo.setBSSID(null);
2387        mWifiInfo.setSSID(null);
2388        mWifiInfo.setNetworkId(WifiConfiguration.INVALID_NETWORK_ID);
2389        mWifiInfo.setRssi(MIN_RSSI);
2390        mWifiInfo.setLinkSpeed(-1);
2391        mWifiInfo.setMeteredHint(false);
2392
2393        setNetworkDetailedState(DetailedState.DISCONNECTED);
2394        mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.DISCONNECTED);
2395
2396        /* Clear network properties */
2397        clearLinkProperties();
2398
2399        /* send event to CM & network change broadcast */
2400        sendNetworkStateChangeBroadcast(mLastBssid);
2401
2402        mLastBssid= null;
2403        mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
2404    }
2405
2406    private void handleSupplicantConnectionLoss() {
2407        /* Socket connection can be lost when we do a graceful shutdown
2408        * or when the driver is hung. Ensure supplicant is stopped here.
2409        */
2410        mWifiMonitor.killSupplicant(mP2pSupported);
2411        sendSupplicantConnectionChangedBroadcast(false);
2412        setWifiState(WIFI_STATE_DISABLED);
2413    }
2414
2415    void handlePreDhcpSetup() {
2416        mDhcpActive = true;
2417        if (!mBluetoothConnectionActive) {
2418            /*
2419             * There are problems setting the Wi-Fi driver's power
2420             * mode to active when bluetooth coexistence mode is
2421             * enabled or sense.
2422             * <p>
2423             * We set Wi-Fi to active mode when
2424             * obtaining an IP address because we've found
2425             * compatibility issues with some routers with low power
2426             * mode.
2427             * <p>
2428             * In order for this active power mode to properly be set,
2429             * we disable coexistence mode until we're done with
2430             * obtaining an IP address.  One exception is if we
2431             * are currently connected to a headset, since disabling
2432             * coexistence would interrupt that connection.
2433             */
2434            // Disable the coexistence mode
2435            mWifiNative.setBluetoothCoexistenceMode(
2436                    mWifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED);
2437        }
2438
2439        /* Disable power save and suspend optimizations during DHCP */
2440        // Note: The order here is important for now. Brcm driver changes
2441        // power settings when we control suspend mode optimizations.
2442        // TODO: Remove this comment when the driver is fixed.
2443        setSuspendOptimizationsNative(SUSPEND_DUE_TO_DHCP, false);
2444        mWifiNative.setPowerSave(false);
2445
2446        stopBatchedScan();
2447
2448        /* P2p discovery breaks dhcp, shut it down in order to get through this */
2449        Message msg = new Message();
2450        msg.what = WifiP2pServiceImpl.BLOCK_DISCOVERY;
2451        msg.arg1 = WifiP2pServiceImpl.ENABLED;
2452        msg.arg2 = DhcpStateMachine.CMD_PRE_DHCP_ACTION_COMPLETE;
2453        msg.obj = mDhcpStateMachine;
2454        mWifiP2pChannel.sendMessage(msg);
2455    }
2456
2457
2458    void startDhcp() {
2459        if (mDhcpStateMachine == null) {
2460            mDhcpStateMachine = DhcpStateMachine.makeDhcpStateMachine(
2461                    mContext, WifiStateMachine.this, mInterfaceName);
2462
2463        }
2464        mDhcpStateMachine.registerForPreDhcpNotification();
2465        mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_START_DHCP);
2466    }
2467
2468    void stopDhcp() {
2469        if (mDhcpStateMachine != null) {
2470            /* In case we were in middle of DHCP operation restore back powermode */
2471            handlePostDhcpSetup();
2472            mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_STOP_DHCP);
2473        }
2474    }
2475
2476    void handlePostDhcpSetup() {
2477        /* Restore power save and suspend optimizations */
2478        setSuspendOptimizationsNative(SUSPEND_DUE_TO_DHCP, true);
2479        mWifiNative.setPowerSave(true);
2480
2481        mWifiP2pChannel.sendMessage(WifiP2pServiceImpl.BLOCK_DISCOVERY, WifiP2pServiceImpl.DISABLED);
2482
2483        // Set the coexistence mode back to its default value
2484        mWifiNative.setBluetoothCoexistenceMode(
2485                mWifiNative.BLUETOOTH_COEXISTENCE_MODE_SENSE);
2486
2487        mDhcpActive = false;
2488
2489        startBatchedScan();
2490    }
2491
2492    private void handleSuccessfulIpConfiguration(DhcpResults dhcpResults) {
2493        mLastSignalLevel = -1; // force update of signal strength
2494        mReconnectCount = 0; //Reset IP failure tracking
2495        synchronized (mDhcpResultsLock) {
2496            mDhcpResults = dhcpResults;
2497        }
2498        LinkProperties linkProperties = dhcpResults.linkProperties;
2499        mWifiConfigStore.setLinkProperties(mLastNetworkId, new LinkProperties(linkProperties));
2500        InetAddress addr = null;
2501        Iterator<InetAddress> addrs = linkProperties.getAddresses().iterator();
2502        if (addrs.hasNext()) {
2503            addr = addrs.next();
2504        }
2505        mWifiInfo.setInetAddress(addr);
2506        mWifiInfo.setMeteredHint(dhcpResults.hasMeteredHint());
2507        updateLinkProperties();
2508    }
2509
2510    private void handleFailedIpConfiguration() {
2511        loge("IP configuration failed");
2512
2513        mWifiInfo.setInetAddress(null);
2514        mWifiInfo.setMeteredHint(false);
2515        /**
2516         * If we've exceeded the maximum number of retries for DHCP
2517         * to a given network, disable the network
2518         */
2519        int maxRetries = getMaxDhcpRetries();
2520        // maxRetries == 0 means keep trying forever
2521        if (maxRetries > 0 && ++mReconnectCount > maxRetries) {
2522            loge("Failed " +
2523                    mReconnectCount + " times, Disabling " + mLastNetworkId);
2524            mWifiConfigStore.disableNetwork(mLastNetworkId,
2525                    WifiConfiguration.DISABLED_DHCP_FAILURE);
2526            mReconnectCount = 0;
2527        }
2528
2529        /* DHCP times out after about 30 seconds, we do a
2530         * disconnect and an immediate reconnect to try again
2531         */
2532        mWifiNative.disconnect();
2533        mWifiNative.reconnect();
2534    }
2535
2536    /* Current design is to not set the config on a running hostapd but instead
2537     * stop and start tethering when user changes config on a running access point
2538     *
2539     * TODO: Add control channel setup through hostapd that allows changing config
2540     * on a running daemon
2541     */
2542    private void startSoftApWithConfig(final WifiConfiguration config) {
2543        // start hostapd on a seperate thread
2544        new Thread(new Runnable() {
2545            public void run() {
2546                try {
2547                    mNwService.startAccessPoint(config, mInterfaceName);
2548                } catch (Exception e) {
2549                    loge("Exception in softap start " + e);
2550                    try {
2551                        mNwService.stopAccessPoint(mInterfaceName);
2552                        mNwService.startAccessPoint(config, mInterfaceName);
2553                    } catch (Exception e1) {
2554                        loge("Exception in softap re-start " + e1);
2555                        sendMessage(CMD_START_AP_FAILURE);
2556                        return;
2557                    }
2558                }
2559                if (DBG) log("Soft AP start successful");
2560                sendMessage(CMD_START_AP_SUCCESS);
2561            }
2562        }).start();
2563    }
2564
2565    /********************************************************
2566     * HSM states
2567     *******************************************************/
2568
2569    class DefaultState extends State {
2570        @Override
2571        public boolean processMessage(Message message) {
2572            switch (message.what) {
2573                case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
2574                    if (message.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
2575                        mWifiP2pChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
2576                    } else {
2577                        loge("WifiP2pService connection failure, error=" + message.arg1);
2578                    }
2579                    break;
2580                case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
2581                    loge("WifiP2pService channel lost, message.arg1 =" + message.arg1);
2582                    //TODO: Re-establish connection to state machine after a delay
2583                    //mWifiP2pChannel.connect(mContext, getHandler(), mWifiP2pManager.getMessenger());
2584                    break;
2585                case CMD_BLUETOOTH_ADAPTER_STATE_CHANGE:
2586                    mBluetoothConnectionActive = (message.arg1 !=
2587                            BluetoothAdapter.STATE_DISCONNECTED);
2588                    break;
2589                    /* Synchronous call returns */
2590                case CMD_PING_SUPPLICANT:
2591                case CMD_ENABLE_NETWORK:
2592                case CMD_ADD_OR_UPDATE_NETWORK:
2593                case CMD_REMOVE_NETWORK:
2594                case CMD_SAVE_CONFIG:
2595                    replyToMessage(message, message.what, FAILURE);
2596                    break;
2597                case CMD_GET_CAPABILITY_FREQ:
2598                    replyToMessage(message, message.what, null);
2599                    break;
2600                case CMD_GET_CONFIGURED_NETWORKS:
2601                    replyToMessage(message, message.what, (List<WifiConfiguration>) null);
2602                    break;
2603                case CMD_ENABLE_RSSI_POLL:
2604                    mEnableRssiPolling = (message.arg1 == 1);
2605                    break;
2606                case CMD_ENABLE_BACKGROUND_SCAN:
2607                    mEnableBackgroundScan = (message.arg1 == 1);
2608                    break;
2609                case CMD_SET_HIGH_PERF_MODE:
2610                    if (message.arg1 == 1) {
2611                        setSuspendOptimizations(SUSPEND_DUE_TO_HIGH_PERF, false);
2612                    } else {
2613                        setSuspendOptimizations(SUSPEND_DUE_TO_HIGH_PERF, true);
2614                    }
2615                    break;
2616                case CMD_BOOT_COMPLETED:
2617                    String countryCode = mPersistedCountryCode;
2618                    if (TextUtils.isEmpty(countryCode) == false) {
2619                        Settings.Global.putString(mContext.getContentResolver(),
2620                                Settings.Global.WIFI_COUNTRY_CODE,
2621                                countryCode);
2622                        // it may be that the state transition that should send this info
2623                        // to the driver happened between mPersistedCountryCode getting set
2624                        // and now, so simply persisting it here would mean we have sent
2625                        // nothing to the driver.  Send the cmd so it might be set now.
2626                        sendMessageAtFrontOfQueue(CMD_SET_COUNTRY_CODE, countryCode);
2627                    }
2628                    break;
2629                case CMD_SET_BATCHED_SCAN:
2630                    recordBatchedScanSettings(message.arg1, message.arg2, (Bundle)message.obj);
2631                    break;
2632                case CMD_POLL_BATCHED_SCAN:
2633                    handleBatchedScanPollRequest();
2634                    break;
2635                case CMD_START_NEXT_BATCHED_SCAN:
2636                    startNextBatchedScan();
2637                    break;
2638                    /* Discard */
2639                case CMD_START_SCAN:
2640                case CMD_START_SUPPLICANT:
2641                case CMD_STOP_SUPPLICANT:
2642                case CMD_STOP_SUPPLICANT_FAILED:
2643                case CMD_START_DRIVER:
2644                case CMD_STOP_DRIVER:
2645                case CMD_DELAYED_STOP_DRIVER:
2646                case CMD_DRIVER_START_TIMED_OUT:
2647                case CMD_START_AP:
2648                case CMD_START_AP_SUCCESS:
2649                case CMD_START_AP_FAILURE:
2650                case CMD_STOP_AP:
2651                case CMD_TETHER_STATE_CHANGE:
2652                case CMD_TETHER_NOTIFICATION_TIMED_OUT:
2653                case CMD_DISCONNECT:
2654                case CMD_RECONNECT:
2655                case CMD_REASSOCIATE:
2656                case CMD_RELOAD_TLS_AND_RECONNECT:
2657                case WifiMonitor.SUP_CONNECTION_EVENT:
2658                case WifiMonitor.SUP_DISCONNECTION_EVENT:
2659                case WifiMonitor.NETWORK_CONNECTION_EVENT:
2660                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
2661                case WifiMonitor.SCAN_RESULTS_EVENT:
2662                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
2663                case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
2664                case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
2665                case WifiMonitor.WPS_OVERLAP_EVENT:
2666                case CMD_BLACKLIST_NETWORK:
2667                case CMD_CLEAR_BLACKLIST:
2668                case CMD_SET_OPERATIONAL_MODE:
2669                case CMD_SET_COUNTRY_CODE:
2670                case CMD_SET_FREQUENCY_BAND:
2671                case CMD_RSSI_POLL:
2672                case CMD_ENABLE_ALL_NETWORKS:
2673                case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
2674                case DhcpStateMachine.CMD_POST_DHCP_ACTION:
2675                /* Handled by WifiApConfigStore */
2676                case CMD_SET_AP_CONFIG:
2677                case CMD_SET_AP_CONFIG_COMPLETED:
2678                case CMD_REQUEST_AP_CONFIG:
2679                case CMD_RESPONSE_AP_CONFIG:
2680                case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
2681                case WifiWatchdogStateMachine.GOOD_LINK_DETECTED:
2682                case CMD_NO_NETWORKS_PERIODIC_SCAN:
2683                case CMD_DISABLE_P2P_RSP:
2684                    break;
2685                case DhcpStateMachine.CMD_ON_QUIT:
2686                    mDhcpStateMachine = null;
2687                    break;
2688                case CMD_SET_SUSPEND_OPT_ENABLED:
2689                    if (message.arg1 == 1) {
2690                        mSuspendWakeLock.release();
2691                        setSuspendOptimizations(SUSPEND_DUE_TO_SCREEN, true);
2692                    } else {
2693                        setSuspendOptimizations(SUSPEND_DUE_TO_SCREEN, false);
2694                    }
2695                    break;
2696                case WifiMonitor.DRIVER_HUNG_EVENT:
2697                    setSupplicantRunning(false);
2698                    setSupplicantRunning(true);
2699                    break;
2700                case WifiManager.CONNECT_NETWORK:
2701                    replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
2702                            WifiManager.BUSY);
2703                    break;
2704                case WifiManager.FORGET_NETWORK:
2705                    replyToMessage(message, WifiManager.FORGET_NETWORK_FAILED,
2706                            WifiManager.BUSY);
2707                    break;
2708                case WifiManager.SAVE_NETWORK:
2709                    replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED,
2710                            WifiManager.BUSY);
2711                    break;
2712                case WifiManager.START_WPS:
2713                    replyToMessage(message, WifiManager.WPS_FAILED,
2714                            WifiManager.BUSY);
2715                    break;
2716                case WifiManager.CANCEL_WPS:
2717                    replyToMessage(message, WifiManager.CANCEL_WPS_FAILED,
2718                            WifiManager.BUSY);
2719                    break;
2720                case WifiManager.DISABLE_NETWORK:
2721                    replyToMessage(message, WifiManager.DISABLE_NETWORK_FAILED,
2722                            WifiManager.BUSY);
2723                    break;
2724                case WifiManager.RSSI_PKTCNT_FETCH:
2725                    replyToMessage(message, WifiManager.RSSI_PKTCNT_FETCH_FAILED,
2726                            WifiManager.BUSY);
2727                    break;
2728                case WifiP2pServiceImpl.P2P_CONNECTION_CHANGED:
2729                    NetworkInfo info = (NetworkInfo) message.obj;
2730                    mP2pConnected.set(info.isConnected());
2731                    break;
2732                case WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST:
2733                    mTemporarilyDisconnectWifi = (message.arg1 == 1);
2734                    replyToMessage(message, WifiP2pServiceImpl.DISCONNECT_WIFI_RESPONSE);
2735                    break;
2736                case CMD_IP_ADDRESS_UPDATED:
2737                    // addLinkAddress is a no-op if called more than once with the same address.
2738                    if (mNetlinkLinkProperties.addLinkAddress((LinkAddress) message.obj)) {
2739                        updateLinkProperties();
2740                    }
2741                    break;
2742                case CMD_IP_ADDRESS_REMOVED:
2743                    if (mNetlinkLinkProperties.removeLinkAddress((LinkAddress) message.obj)) {
2744                        updateLinkProperties();
2745                    }
2746                    break;
2747                default:
2748                    loge("Error! unhandled message" + message);
2749                    break;
2750            }
2751            return HANDLED;
2752        }
2753    }
2754
2755    class InitialState extends State {
2756        @Override
2757        public void enter() {
2758            mWifiNative.unloadDriver();
2759
2760            if (mWifiP2pChannel == null) {
2761                mWifiP2pChannel = new AsyncChannel();
2762                mWifiP2pChannel.connect(mContext, getHandler(), mWifiP2pManager.getMessenger());
2763            }
2764
2765            if (mWifiApConfigChannel == null) {
2766                mWifiApConfigChannel = new AsyncChannel();
2767                WifiApConfigStore wifiApConfigStore = WifiApConfigStore.makeWifiApConfigStore(
2768                        mContext, getHandler());
2769                wifiApConfigStore.loadApConfiguration();
2770                mWifiApConfigChannel.connectSync(mContext, getHandler(),
2771                        wifiApConfigStore.getMessenger());
2772            }
2773        }
2774        @Override
2775        public boolean processMessage(Message message) {
2776            switch (message.what) {
2777                case CMD_START_SUPPLICANT:
2778                    if (mWifiNative.loadDriver()) {
2779                        try {
2780                            mNwService.wifiFirmwareReload(mInterfaceName, "STA");
2781                        } catch (Exception e) {
2782                            loge("Failed to reload STA firmware " + e);
2783                            // continue
2784                        }
2785
2786                        try {
2787                            // A runtime crash can leave the interface up and
2788                            // this affects connectivity when supplicant starts up.
2789                            // Ensure interface is down before a supplicant start.
2790                            mNwService.setInterfaceDown(mInterfaceName);
2791                            // Set privacy extensions
2792                            mNwService.setInterfaceIpv6PrivacyExtensions(mInterfaceName, true);
2793
2794                           // IPv6 is enabled only as long as access point is connected since:
2795                           // - IPv6 addresses and routes stick around after disconnection
2796                           // - kernel is unaware when connected and fails to start IPv6 negotiation
2797                           // - kernel can start autoconfiguration when 802.1x is not complete
2798                            mNwService.disableIpv6(mInterfaceName);
2799                        } catch (RemoteException re) {
2800                            loge("Unable to change interface settings: " + re);
2801                        } catch (IllegalStateException ie) {
2802                            loge("Unable to change interface settings: " + ie);
2803                        }
2804
2805                       /* Stop a running supplicant after a runtime restart
2806                        * Avoids issues with drivers that do not handle interface down
2807                        * on a running supplicant properly.
2808                        */
2809                        mWifiMonitor.killSupplicant(mP2pSupported);
2810                        if(mWifiNative.startSupplicant(mP2pSupported)) {
2811                            setWifiState(WIFI_STATE_ENABLING);
2812                            if (DBG) log("Supplicant start successful");
2813                            mWifiMonitor.startMonitoring();
2814                            transitionTo(mSupplicantStartingState);
2815                        } else {
2816                            loge("Failed to start supplicant!");
2817                        }
2818                    } else {
2819                        loge("Failed to load driver");
2820                    }
2821                    break;
2822                case CMD_START_AP:
2823                    if (mWifiNative.loadDriver()) {
2824                        setWifiApState(WIFI_AP_STATE_ENABLING);
2825                        transitionTo(mSoftApStartingState);
2826                    } else {
2827                        loge("Failed to load driver for softap");
2828                    }
2829                default:
2830                    return NOT_HANDLED;
2831            }
2832            return HANDLED;
2833        }
2834    }
2835
2836    class SupplicantStartingState extends State {
2837        private void initializeWpsDetails() {
2838            String detail;
2839            detail = SystemProperties.get("ro.product.name", "");
2840            if (!mWifiNative.setDeviceName(detail)) {
2841                loge("Failed to set device name " +  detail);
2842            }
2843            detail = SystemProperties.get("ro.product.manufacturer", "");
2844            if (!mWifiNative.setManufacturer(detail)) {
2845                loge("Failed to set manufacturer " + detail);
2846            }
2847            detail = SystemProperties.get("ro.product.model", "");
2848            if (!mWifiNative.setModelName(detail)) {
2849                loge("Failed to set model name " + detail);
2850            }
2851            detail = SystemProperties.get("ro.product.model", "");
2852            if (!mWifiNative.setModelNumber(detail)) {
2853                loge("Failed to set model number " + detail);
2854            }
2855            detail = SystemProperties.get("ro.serialno", "");
2856            if (!mWifiNative.setSerialNumber(detail)) {
2857                loge("Failed to set serial number " + detail);
2858            }
2859            if (!mWifiNative.setConfigMethods("physical_display virtual_push_button")) {
2860                loge("Failed to set WPS config methods");
2861            }
2862            if (!mWifiNative.setDeviceType(mPrimaryDeviceType)) {
2863                loge("Failed to set primary device type " + mPrimaryDeviceType);
2864            }
2865        }
2866
2867        @Override
2868        public boolean processMessage(Message message) {
2869            switch(message.what) {
2870                case WifiMonitor.SUP_CONNECTION_EVENT:
2871                    if (DBG) log("Supplicant connection established");
2872                    setWifiState(WIFI_STATE_ENABLED);
2873                    mSupplicantRestartCount = 0;
2874                    /* Reset the supplicant state to indicate the supplicant
2875                     * state is not known at this time */
2876                    mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
2877                    /* Initialize data structures */
2878                    mLastBssid = null;
2879                    mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
2880                    mLastSignalLevel = -1;
2881
2882                    mWifiInfo.setMacAddress(mWifiNative.getMacAddress());
2883                    mWifiConfigStore.loadAndEnableAllNetworks();
2884                    initializeWpsDetails();
2885
2886                    sendSupplicantConnectionChangedBroadcast(true);
2887                    transitionTo(mDriverStartedState);
2888                    break;
2889                case WifiMonitor.SUP_DISCONNECTION_EVENT:
2890                    if (++mSupplicantRestartCount <= SUPPLICANT_RESTART_TRIES) {
2891                        loge("Failed to setup control channel, restart supplicant");
2892                        mWifiMonitor.killSupplicant(mP2pSupported);
2893                        transitionTo(mInitialState);
2894                        sendMessageDelayed(CMD_START_SUPPLICANT, SUPPLICANT_RESTART_INTERVAL_MSECS);
2895                    } else {
2896                        loge("Failed " + mSupplicantRestartCount +
2897                                " times to start supplicant, unload driver");
2898                        mSupplicantRestartCount = 0;
2899                        setWifiState(WIFI_STATE_UNKNOWN);
2900                        transitionTo(mInitialState);
2901                    }
2902                    break;
2903                case CMD_START_SUPPLICANT:
2904                case CMD_STOP_SUPPLICANT:
2905                case CMD_START_AP:
2906                case CMD_STOP_AP:
2907                case CMD_START_DRIVER:
2908                case CMD_STOP_DRIVER:
2909                case CMD_SET_OPERATIONAL_MODE:
2910                case CMD_SET_COUNTRY_CODE:
2911                case CMD_SET_FREQUENCY_BAND:
2912                case CMD_START_PACKET_FILTERING:
2913                case CMD_STOP_PACKET_FILTERING:
2914                    deferMessage(message);
2915                    break;
2916                default:
2917                    return NOT_HANDLED;
2918            }
2919            return HANDLED;
2920        }
2921    }
2922
2923    class SupplicantStartedState extends State {
2924        @Override
2925        public void enter() {
2926            /* Wifi is available as long as we have a connection to supplicant */
2927            mNetworkInfo.setIsAvailable(true);
2928
2929            int defaultInterval = mContext.getResources().getInteger(
2930                    R.integer.config_wifi_supplicant_scan_interval);
2931
2932            mSupplicantScanIntervalMs = Settings.Global.getLong(mContext.getContentResolver(),
2933                    Settings.Global.WIFI_SUPPLICANT_SCAN_INTERVAL_MS,
2934                    defaultInterval);
2935
2936            mWifiNative.setScanInterval((int)mSupplicantScanIntervalMs / 1000);
2937        }
2938        @Override
2939        public boolean processMessage(Message message) {
2940            switch(message.what) {
2941                case CMD_STOP_SUPPLICANT:   /* Supplicant stopped by user */
2942                    if (mP2pSupported) {
2943                        transitionTo(mWaitForP2pDisableState);
2944                    } else {
2945                        transitionTo(mSupplicantStoppingState);
2946                    }
2947                    break;
2948                case WifiMonitor.SUP_DISCONNECTION_EVENT:  /* Supplicant connection lost */
2949                    loge("Connection lost, restart supplicant");
2950                    handleSupplicantConnectionLoss();
2951                    handleNetworkDisconnect();
2952                    mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
2953                    if (mP2pSupported) {
2954                        transitionTo(mWaitForP2pDisableState);
2955                    } else {
2956                        transitionTo(mInitialState);
2957                    }
2958                    sendMessageDelayed(CMD_START_SUPPLICANT, SUPPLICANT_RESTART_INTERVAL_MSECS);
2959                    break;
2960                case WifiMonitor.SCAN_RESULTS_EVENT:
2961                    setScanResults();
2962                    sendScanResultsAvailableBroadcast();
2963                    mIsScanOngoing = false;
2964                    mIsFullScanOngoing = false;
2965                    if (mBufferedScanMsg.size() > 0)
2966                        sendMessage(mBufferedScanMsg.remove());
2967                    break;
2968                case CMD_PING_SUPPLICANT:
2969                    boolean ok = mWifiNative.ping();
2970                    replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
2971                    break;
2972                case CMD_GET_CAPABILITY_FREQ:
2973                    String freqs = mWifiNative.getFreqCapability();
2974                    replyToMessage(message, message.what, freqs);
2975                    break;
2976                case CMD_START_AP:
2977                    /* Cannot start soft AP while in client mode */
2978                    loge("Failed to start soft AP with a running supplicant");
2979                    setWifiApState(WIFI_AP_STATE_FAILED);
2980                    break;
2981                case CMD_SET_OPERATIONAL_MODE:
2982                    mOperationalMode = message.arg1;
2983                    break;
2984                default:
2985                    return NOT_HANDLED;
2986            }
2987            return HANDLED;
2988        }
2989
2990        @Override
2991        public void exit() {
2992            mNetworkInfo.setIsAvailable(false);
2993        }
2994    }
2995
2996    class SupplicantStoppingState extends State {
2997        @Override
2998        public void enter() {
2999            /* Send any reset commands to supplicant before shutting it down */
3000            handleNetworkDisconnect();
3001            if (mDhcpStateMachine != null) {
3002                mDhcpStateMachine.doQuit();
3003            }
3004
3005            if (DBG) log("stopping supplicant");
3006            mWifiMonitor.stopSupplicant();
3007
3008            /* Send ourselves a delayed message to indicate failure after a wait time */
3009            sendMessageDelayed(obtainMessage(CMD_STOP_SUPPLICANT_FAILED,
3010                    ++mSupplicantStopFailureToken, 0), SUPPLICANT_RESTART_INTERVAL_MSECS);
3011            setWifiState(WIFI_STATE_DISABLING);
3012            mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
3013        }
3014        @Override
3015        public boolean processMessage(Message message) {
3016            switch(message.what) {
3017                case WifiMonitor.SUP_CONNECTION_EVENT:
3018                    loge("Supplicant connection received while stopping");
3019                    break;
3020                case WifiMonitor.SUP_DISCONNECTION_EVENT:
3021                    if (DBG) log("Supplicant connection lost");
3022                    handleSupplicantConnectionLoss();
3023                    transitionTo(mInitialState);
3024                    break;
3025                case CMD_STOP_SUPPLICANT_FAILED:
3026                    if (message.arg1 == mSupplicantStopFailureToken) {
3027                        loge("Timed out on a supplicant stop, kill and proceed");
3028                        handleSupplicantConnectionLoss();
3029                        transitionTo(mInitialState);
3030                    }
3031                    break;
3032                case CMD_START_SUPPLICANT:
3033                case CMD_STOP_SUPPLICANT:
3034                case CMD_START_AP:
3035                case CMD_STOP_AP:
3036                case CMD_START_DRIVER:
3037                case CMD_STOP_DRIVER:
3038                case CMD_SET_OPERATIONAL_MODE:
3039                case CMD_SET_COUNTRY_CODE:
3040                case CMD_SET_FREQUENCY_BAND:
3041                case CMD_START_PACKET_FILTERING:
3042                case CMD_STOP_PACKET_FILTERING:
3043                    deferMessage(message);
3044                    break;
3045                default:
3046                    return NOT_HANDLED;
3047            }
3048            return HANDLED;
3049        }
3050    }
3051
3052    class DriverStartingState extends State {
3053        private int mTries;
3054        @Override
3055        public void enter() {
3056            mTries = 1;
3057            /* Send ourselves a delayed message to start driver a second time */
3058            sendMessageDelayed(obtainMessage(CMD_DRIVER_START_TIMED_OUT,
3059                        ++mDriverStartToken, 0), DRIVER_START_TIME_OUT_MSECS);
3060        }
3061        @Override
3062        public boolean processMessage(Message message) {
3063            switch(message.what) {
3064               case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
3065                    SupplicantState state = handleSupplicantStateChange(message);
3066                    /* If suplicant is exiting out of INTERFACE_DISABLED state into
3067                     * a state that indicates driver has started, it is ready to
3068                     * receive driver commands
3069                     */
3070                    if (SupplicantState.isDriverActive(state)) {
3071                        transitionTo(mDriverStartedState);
3072                    }
3073                    break;
3074                case CMD_DRIVER_START_TIMED_OUT:
3075                    if (message.arg1 == mDriverStartToken) {
3076                        if (mTries >= 2) {
3077                            loge("Failed to start driver after " + mTries);
3078                            transitionTo(mDriverStoppedState);
3079                        } else {
3080                            loge("Driver start failed, retrying");
3081                            mWakeLock.acquire();
3082                            mWifiNative.startDriver();
3083                            mWakeLock.release();
3084
3085                            ++mTries;
3086                            /* Send ourselves a delayed message to start driver again */
3087                            sendMessageDelayed(obtainMessage(CMD_DRIVER_START_TIMED_OUT,
3088                                        ++mDriverStartToken, 0), DRIVER_START_TIME_OUT_MSECS);
3089                        }
3090                    }
3091                    break;
3092                    /* Queue driver commands & connection events */
3093                case CMD_START_DRIVER:
3094                case CMD_STOP_DRIVER:
3095                case WifiMonitor.NETWORK_CONNECTION_EVENT:
3096                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
3097                case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
3098                case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
3099                case WifiMonitor.WPS_OVERLAP_EVENT:
3100                case CMD_SET_COUNTRY_CODE:
3101                case CMD_SET_FREQUENCY_BAND:
3102                case CMD_START_PACKET_FILTERING:
3103                case CMD_STOP_PACKET_FILTERING:
3104                case CMD_START_SCAN:
3105                case CMD_DISCONNECT:
3106                case CMD_REASSOCIATE:
3107                case CMD_RECONNECT:
3108                    deferMessage(message);
3109                    break;
3110                default:
3111                    return NOT_HANDLED;
3112            }
3113            return HANDLED;
3114        }
3115    }
3116
3117    class DriverStartedState extends State {
3118        @Override
3119        public void enter() {
3120            mIsRunning = true;
3121            mInDelayedStop = false;
3122            mDelayedStopCounter++;
3123            updateBatteryWorkSource(null);
3124            /**
3125             * Enable bluetooth coexistence scan mode when bluetooth connection is active.
3126             * When this mode is on, some of the low-level scan parameters used by the
3127             * driver are changed to reduce interference with bluetooth
3128             */
3129            mWifiNative.setBluetoothCoexistenceScanMode(mBluetoothConnectionActive);
3130            /* set country code */
3131            setCountryCode();
3132            /* set frequency band of operation */
3133            setFrequencyBand();
3134            /* initialize network state */
3135            setNetworkDetailedState(DetailedState.DISCONNECTED);
3136
3137            /* Remove any filtering on Multicast v6 at start */
3138            mWifiNative.stopFilteringMulticastV6Packets();
3139
3140            /* Reset Multicast v4 filtering state */
3141            if (mFilteringMulticastV4Packets.get()) {
3142                mWifiNative.startFilteringMulticastV4Packets();
3143            } else {
3144                mWifiNative.stopFilteringMulticastV4Packets();
3145            }
3146
3147            mDhcpActive = false;
3148
3149            startBatchedScan();
3150
3151            if (mOperationalMode != CONNECT_MODE) {
3152                mWifiNative.disconnect();
3153                mWifiConfigStore.disableAllNetworks();
3154                if (mOperationalMode == SCAN_ONLY_WITH_WIFI_OFF_MODE) {
3155                    setWifiState(WIFI_STATE_DISABLED);
3156                }
3157                transitionTo(mScanModeState);
3158            } else {
3159                /* Driver stop may have disabled networks, enable right after start */
3160                mWifiConfigStore.enableAllNetworks();
3161
3162                if (DBG) log("Attempting to reconnect to wifi network ..");
3163                mWifiNative.reconnect();
3164
3165                // Status pulls in the current supplicant state and network connection state
3166                // events over the monitor connection. This helps framework sync up with
3167                // current supplicant state
3168                mWifiNative.status();
3169                transitionTo(mDisconnectedState);
3170            }
3171
3172            // We may have missed screen update at boot
3173            if (mScreenBroadcastReceived.get() == false) {
3174                PowerManager powerManager = (PowerManager)mContext.getSystemService(
3175                        Context.POWER_SERVICE);
3176                handleScreenStateChanged(powerManager.isScreenOn());
3177            } else {
3178                // Set the right suspend mode settings
3179                mWifiNative.setSuspendOptimizations(mSuspendOptNeedsDisabled == 0
3180                        && mUserWantsSuspendOpt.get());
3181            }
3182            mWifiNative.setPowerSave(true);
3183
3184            if (mP2pSupported) {
3185                if (mOperationalMode == CONNECT_MODE) {
3186                    mWifiP2pChannel.sendMessage(WifiStateMachine.CMD_ENABLE_P2P);
3187                } else {
3188                    // P2P statemachine starts in disabled state, and is not enabled until
3189                    // CMD_ENABLE_P2P is sent from here; so, nothing needs to be done to
3190                    // keep it disabled.
3191                }
3192            }
3193
3194            final Intent intent = new Intent(WifiManager.WIFI_SCAN_AVAILABLE);
3195            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
3196            intent.putExtra(WifiManager.EXTRA_SCAN_AVAILABLE, WIFI_STATE_ENABLED);
3197            mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
3198        }
3199
3200        @Override
3201        public boolean processMessage(Message message) {
3202            switch(message.what) {
3203                case CMD_START_SCAN:
3204                    handleScanRequest(WifiNative.SCAN_WITH_CONNECTION_SETUP, message);
3205                    break;
3206                case CMD_SET_BATCHED_SCAN:
3207                    if (recordBatchedScanSettings(message.arg1, message.arg2,
3208                            (Bundle)message.obj)) {
3209                        startBatchedScan();
3210                    }
3211                    break;
3212                case CMD_SET_COUNTRY_CODE:
3213                    String country = (String) message.obj;
3214                    if (DBG) log("set country code " + country);
3215                    if (country != null) {
3216                        country = country.toUpperCase(Locale.ROOT);
3217                        if (mLastSetCountryCode == null
3218                                || country.equals(mLastSetCountryCode) == false) {
3219                            if (mWifiNative.setCountryCode(country)) {
3220                                mLastSetCountryCode = country;
3221                            } else {
3222                                loge("Failed to set country code " + country);
3223                            }
3224                        }
3225                    }
3226                    break;
3227                case CMD_SET_FREQUENCY_BAND:
3228                    int band =  message.arg1;
3229                    if (DBG) log("set frequency band " + band);
3230                    if (mWifiNative.setBand(band)) {
3231                        mFrequencyBand.set(band);
3232                        // flush old data - like scan results
3233                        mWifiNative.bssFlush();
3234                        // fetch the latest scan results when frequency band is set
3235                        startScanNative(WifiNative.SCAN_WITH_CONNECTION_SETUP, null);
3236                    } else {
3237                        loge("Failed to set frequency band " + band);
3238                    }
3239                    break;
3240                case CMD_BLUETOOTH_ADAPTER_STATE_CHANGE:
3241                    mBluetoothConnectionActive = (message.arg1 !=
3242                            BluetoothAdapter.STATE_DISCONNECTED);
3243                    mWifiNative.setBluetoothCoexistenceScanMode(mBluetoothConnectionActive);
3244                    break;
3245                case CMD_STOP_DRIVER:
3246                    int mode = message.arg1;
3247
3248                    /* Already doing a delayed stop */
3249                    if (mInDelayedStop) {
3250                        if (DBG) log("Already in delayed stop");
3251                        break;
3252                    }
3253                    /* disconnect right now, but leave the driver running for a bit */
3254                    mWifiConfigStore.disableAllNetworks();
3255
3256                    mInDelayedStop = true;
3257                    mDelayedStopCounter++;
3258                    if (DBG) log("Delayed stop message " + mDelayedStopCounter);
3259
3260                    /* send regular delayed shut down */
3261                    Intent driverStopIntent = new Intent(ACTION_DELAYED_DRIVER_STOP, null);
3262                    driverStopIntent.putExtra(DELAYED_STOP_COUNTER, mDelayedStopCounter);
3263                    mDriverStopIntent = PendingIntent.getBroadcast(mContext,
3264                            DRIVER_STOP_REQUEST, driverStopIntent,
3265                            PendingIntent.FLAG_UPDATE_CURRENT);
3266
3267                    mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()
3268                            + mDriverStopDelayMs, mDriverStopIntent);
3269                    break;
3270                case CMD_START_DRIVER:
3271                    if (mInDelayedStop) {
3272                        mInDelayedStop = false;
3273                        mDelayedStopCounter++;
3274                        mAlarmManager.cancel(mDriverStopIntent);
3275                        if (DBG) log("Delayed stop ignored due to start");
3276                        if (mOperationalMode == CONNECT_MODE) {
3277                            mWifiConfigStore.enableAllNetworks();
3278                        }
3279                    }
3280                    break;
3281                case CMD_DELAYED_STOP_DRIVER:
3282                    if (DBG) log("delayed stop " + message.arg1 + " " + mDelayedStopCounter);
3283                    if (message.arg1 != mDelayedStopCounter) break;
3284                    if (getCurrentState() != mDisconnectedState) {
3285                        mWifiNative.disconnect();
3286                        handleNetworkDisconnect();
3287                    }
3288                    mWakeLock.acquire();
3289                    mWifiNative.stopDriver();
3290                    mWakeLock.release();
3291                    if (mP2pSupported) {
3292                        transitionTo(mWaitForP2pDisableState);
3293                    } else {
3294                        transitionTo(mDriverStoppingState);
3295                    }
3296                    break;
3297                case CMD_START_PACKET_FILTERING:
3298                    if (message.arg1 == MULTICAST_V6) {
3299                        mWifiNative.startFilteringMulticastV6Packets();
3300                    } else if (message.arg1 == MULTICAST_V4) {
3301                        mWifiNative.startFilteringMulticastV4Packets();
3302                    } else {
3303                        loge("Illegal arugments to CMD_START_PACKET_FILTERING");
3304                    }
3305                    break;
3306                case CMD_STOP_PACKET_FILTERING:
3307                    if (message.arg1 == MULTICAST_V6) {
3308                        mWifiNative.stopFilteringMulticastV6Packets();
3309                    } else if (message.arg1 == MULTICAST_V4) {
3310                        mWifiNative.stopFilteringMulticastV4Packets();
3311                    } else {
3312                        loge("Illegal arugments to CMD_STOP_PACKET_FILTERING");
3313                    }
3314                    break;
3315                case CMD_SET_SUSPEND_OPT_ENABLED:
3316                    if (message.arg1 == 1) {
3317                        setSuspendOptimizationsNative(SUSPEND_DUE_TO_SCREEN, true);
3318                        mSuspendWakeLock.release();
3319                    } else {
3320                        setSuspendOptimizationsNative(SUSPEND_DUE_TO_SCREEN, false);
3321                    }
3322                    break;
3323                case CMD_SET_HIGH_PERF_MODE:
3324                    if (message.arg1 == 1) {
3325                        setSuspendOptimizationsNative(SUSPEND_DUE_TO_HIGH_PERF, false);
3326                    } else {
3327                        setSuspendOptimizationsNative(SUSPEND_DUE_TO_HIGH_PERF, true);
3328                    }
3329                    break;
3330                case CMD_ENABLE_TDLS:
3331                    if (message.obj != null) {
3332                        String remoteAddress = (String) message.obj;
3333                        boolean enable = (message.arg1 == 1);
3334                        mWifiNative.startTdls(remoteAddress, enable);
3335                    }
3336                    break;
3337                default:
3338                    return NOT_HANDLED;
3339            }
3340            return HANDLED;
3341        }
3342        @Override
3343        public void exit() {
3344            mIsRunning = false;
3345            updateBatteryWorkSource(null);
3346            mScanResults = new ArrayList<ScanResult>();
3347
3348            stopBatchedScan();
3349
3350            final Intent intent = new Intent(WifiManager.WIFI_SCAN_AVAILABLE);
3351            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
3352            intent.putExtra(WifiManager.EXTRA_SCAN_AVAILABLE, WIFI_STATE_DISABLED);
3353            mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
3354            noteScanEnd(); // wrap up any pending request.
3355            mBufferedScanMsg.clear();
3356
3357            mLastSetCountryCode = null;
3358        }
3359    }
3360
3361    class WaitForP2pDisableState extends State {
3362        private State mTransitionToState;
3363        @Override
3364        public void enter() {
3365            switch (getCurrentMessage().what) {
3366                case WifiMonitor.SUP_DISCONNECTION_EVENT:
3367                    mTransitionToState = mInitialState;
3368                    break;
3369                case CMD_DELAYED_STOP_DRIVER:
3370                    mTransitionToState = mDriverStoppingState;
3371                    break;
3372                case CMD_STOP_SUPPLICANT:
3373                    mTransitionToState = mSupplicantStoppingState;
3374                    break;
3375                default:
3376                    mTransitionToState = mDriverStoppingState;
3377                    break;
3378            }
3379            mWifiP2pChannel.sendMessage(WifiStateMachine.CMD_DISABLE_P2P_REQ);
3380        }
3381        @Override
3382        public boolean processMessage(Message message) {
3383            switch(message.what) {
3384                case WifiStateMachine.CMD_DISABLE_P2P_RSP:
3385                    transitionTo(mTransitionToState);
3386                    break;
3387                /* Defer wifi start/shut and driver commands */
3388                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
3389                case CMD_START_SUPPLICANT:
3390                case CMD_STOP_SUPPLICANT:
3391                case CMD_START_AP:
3392                case CMD_STOP_AP:
3393                case CMD_START_DRIVER:
3394                case CMD_STOP_DRIVER:
3395                case CMD_SET_OPERATIONAL_MODE:
3396                case CMD_SET_COUNTRY_CODE:
3397                case CMD_SET_FREQUENCY_BAND:
3398                case CMD_START_PACKET_FILTERING:
3399                case CMD_STOP_PACKET_FILTERING:
3400                case CMD_START_SCAN:
3401                case CMD_DISCONNECT:
3402                case CMD_REASSOCIATE:
3403                case CMD_RECONNECT:
3404                    deferMessage(message);
3405                    break;
3406                default:
3407                    return NOT_HANDLED;
3408            }
3409            return HANDLED;
3410        }
3411    }
3412
3413    class DriverStoppingState extends State {
3414        @Override
3415        public boolean processMessage(Message message) {
3416            switch(message.what) {
3417                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
3418                    SupplicantState state = handleSupplicantStateChange(message);
3419                    if (state == SupplicantState.INTERFACE_DISABLED) {
3420                        transitionTo(mDriverStoppedState);
3421                    }
3422                    break;
3423                    /* Queue driver commands */
3424                case CMD_START_DRIVER:
3425                case CMD_STOP_DRIVER:
3426                case CMD_SET_COUNTRY_CODE:
3427                case CMD_SET_FREQUENCY_BAND:
3428                case CMD_START_PACKET_FILTERING:
3429                case CMD_STOP_PACKET_FILTERING:
3430                case CMD_START_SCAN:
3431                case CMD_DISCONNECT:
3432                case CMD_REASSOCIATE:
3433                case CMD_RECONNECT:
3434                    deferMessage(message);
3435                    break;
3436                default:
3437                    return NOT_HANDLED;
3438            }
3439            return HANDLED;
3440        }
3441    }
3442
3443    class DriverStoppedState extends State {
3444        @Override
3445        public boolean processMessage(Message message) {
3446            switch (message.what) {
3447                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
3448                    StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
3449                    SupplicantState state = stateChangeResult.state;
3450                    // A WEXT bug means that we can be back to driver started state
3451                    // unexpectedly
3452                    if (SupplicantState.isDriverActive(state)) {
3453                        transitionTo(mDriverStartedState);
3454                    }
3455                    break;
3456                case CMD_START_DRIVER:
3457                    mWakeLock.acquire();
3458                    mWifiNative.startDriver();
3459                    mWakeLock.release();
3460                    transitionTo(mDriverStartingState);
3461                    break;
3462                default:
3463                    return NOT_HANDLED;
3464            }
3465            return HANDLED;
3466        }
3467    }
3468
3469    class ScanModeState extends State {
3470        private int mLastOperationMode;
3471        @Override
3472        public void enter() {
3473            mLastOperationMode = mOperationalMode;
3474        }
3475        @Override
3476        public boolean processMessage(Message message) {
3477            switch(message.what) {
3478                case CMD_SET_OPERATIONAL_MODE:
3479                    if (message.arg1 == CONNECT_MODE) {
3480
3481                        if (mLastOperationMode == SCAN_ONLY_WITH_WIFI_OFF_MODE) {
3482                            setWifiState(WIFI_STATE_ENABLED);
3483                            // Load and re-enable networks when going back to enabled state
3484                            // This is essential for networks to show up after restore
3485                            mWifiConfigStore.loadAndEnableAllNetworks();
3486                            mWifiP2pChannel.sendMessage(CMD_ENABLE_P2P);
3487                        } else {
3488                            mWifiConfigStore.enableAllNetworks();
3489                        }
3490
3491                        mWifiNative.reconnect();
3492
3493                        mOperationalMode = CONNECT_MODE;
3494                        transitionTo(mDisconnectedState);
3495                    } else {
3496                        // Nothing to do
3497                        return HANDLED;
3498                    }
3499                    break;
3500                // Handle scan. All the connection related commands are
3501                // handled only in ConnectModeState
3502                case CMD_START_SCAN:
3503                    handleScanRequest(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, message);
3504                    break;
3505                default:
3506                    return NOT_HANDLED;
3507            }
3508            return HANDLED;
3509        }
3510    }
3511
3512    class ConnectModeState extends State {
3513        @Override
3514        public boolean processMessage(Message message) {
3515            WifiConfiguration config;
3516            boolean ok;
3517            switch(message.what) {
3518                case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
3519                    mSupplicantStateTracker.sendMessage(WifiMonitor.ASSOCIATION_REJECTION_EVENT);
3520                    break;
3521                case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
3522                    mSupplicantStateTracker.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT);
3523                    break;
3524                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
3525                    SupplicantState state = handleSupplicantStateChange(message);
3526                    // A driver/firmware hang can now put the interface in a down state.
3527                    // We detect the interface going down and recover from it
3528                    if (!SupplicantState.isDriverActive(state)) {
3529                        if (mNetworkInfo.getState() != NetworkInfo.State.DISCONNECTED) {
3530                            handleNetworkDisconnect();
3531                        }
3532                        log("Detected an interface down, restart driver");
3533                        transitionTo(mDriverStoppedState);
3534                        sendMessage(CMD_START_DRIVER);
3535                        break;
3536                    }
3537
3538                    // Supplicant can fail to report a NETWORK_DISCONNECTION_EVENT
3539                    // when authentication times out after a successful connection,
3540                    // we can figure this from the supplicant state. If supplicant
3541                    // state is DISCONNECTED, but the mNetworkInfo says we are not
3542                    // disconnected, we need to handle a disconnection
3543                    if (state == SupplicantState.DISCONNECTED &&
3544                            mNetworkInfo.getState() != NetworkInfo.State.DISCONNECTED) {
3545                        if (DBG) log("Missed CTRL-EVENT-DISCONNECTED, disconnect");
3546                        handleNetworkDisconnect();
3547                        transitionTo(mDisconnectedState);
3548                    }
3549                    break;
3550                case WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST:
3551                    if (message.arg1 == 1) {
3552                        mWifiNative.disconnect();
3553                        mTemporarilyDisconnectWifi = true;
3554                    } else {
3555                        mWifiNative.reconnect();
3556                        mTemporarilyDisconnectWifi = false;
3557                    }
3558                    break;
3559                case CMD_ADD_OR_UPDATE_NETWORK:
3560                    config = (WifiConfiguration) message.obj;
3561                    replyToMessage(message, CMD_ADD_OR_UPDATE_NETWORK,
3562                            mWifiConfigStore.addOrUpdateNetwork(config));
3563                    break;
3564                case CMD_REMOVE_NETWORK:
3565                    ok = mWifiConfigStore.removeNetwork(message.arg1);
3566                    replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
3567                    break;
3568                case CMD_ENABLE_NETWORK:
3569                    ok = mWifiConfigStore.enableNetwork(message.arg1, message.arg2 == 1);
3570                    replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
3571                    break;
3572                case CMD_ENABLE_ALL_NETWORKS:
3573                    long time =  android.os.SystemClock.elapsedRealtime();
3574                    if (time - mLastEnableAllNetworksTime > MIN_INTERVAL_ENABLE_ALL_NETWORKS_MS) {
3575                        mWifiConfigStore.enableAllNetworks();
3576                        mLastEnableAllNetworksTime = time;
3577                    }
3578                    break;
3579                case WifiManager.DISABLE_NETWORK:
3580                    if (mWifiConfigStore.disableNetwork(message.arg1,
3581                            WifiConfiguration.DISABLED_UNKNOWN_REASON) == true) {
3582                        replyToMessage(message, WifiManager.DISABLE_NETWORK_SUCCEEDED);
3583                    } else {
3584                        replyToMessage(message, WifiManager.DISABLE_NETWORK_FAILED,
3585                                WifiManager.ERROR);
3586                    }
3587                    break;
3588                case CMD_BLACKLIST_NETWORK:
3589                    mWifiNative.addToBlacklist((String)message.obj);
3590                    break;
3591                case CMD_CLEAR_BLACKLIST:
3592                    mWifiNative.clearBlacklist();
3593                    break;
3594                case CMD_SAVE_CONFIG:
3595                    ok = mWifiConfigStore.saveConfig();
3596                    replyToMessage(message, CMD_SAVE_CONFIG, ok ? SUCCESS : FAILURE);
3597
3598                    // Inform the backup manager about a data change
3599                    IBackupManager ibm = IBackupManager.Stub.asInterface(
3600                            ServiceManager.getService(Context.BACKUP_SERVICE));
3601                    if (ibm != null) {
3602                        try {
3603                            ibm.dataChanged("com.android.providers.settings");
3604                        } catch (Exception e) {
3605                            // Try again later
3606                        }
3607                    }
3608                    break;
3609                case CMD_GET_CONFIGURED_NETWORKS:
3610                    replyToMessage(message, message.what,
3611                            mWifiConfigStore.getConfiguredNetworks());
3612                    break;
3613                    /* Do a redundant disconnect without transition */
3614                case CMD_DISCONNECT:
3615                    mWifiNative.disconnect();
3616                    break;
3617                case CMD_RECONNECT:
3618                    mWifiNative.reconnect();
3619                    break;
3620                case CMD_REASSOCIATE:
3621                    mWifiNative.reassociate();
3622                    break;
3623                case CMD_RELOAD_TLS_AND_RECONNECT:
3624                    if (mWifiConfigStore.needsUnlockedKeyStore()) {
3625                        logd("Reconnecting to give a chance to un-connected TLS networks");
3626                        mWifiNative.disconnect();
3627                        mWifiNative.reconnect();
3628                    }
3629                    break;
3630                case WifiManager.CONNECT_NETWORK:
3631                    /* The connect message can contain a network id passed as arg1 on message or
3632                     * or a config passed as obj on message.
3633                     * For a new network, a config is passed to create and connect.
3634                     * For an existing network, a network id is passed
3635                     */
3636                    int netId = message.arg1;
3637                    config = (WifiConfiguration) message.obj;
3638
3639                    /* Save the network config */
3640                    if (config != null) {
3641                        NetworkUpdateResult result = mWifiConfigStore.saveNetwork(config);
3642                        netId = result.getNetworkId();
3643                    }
3644
3645                    if (mWifiConfigStore.selectNetwork(netId) &&
3646                            mWifiNative.reconnect()) {
3647                        /* The state tracker handles enabling networks upon completion/failure */
3648                        mSupplicantStateTracker.sendMessage(WifiManager.CONNECT_NETWORK);
3649                        replyToMessage(message, WifiManager.CONNECT_NETWORK_SUCCEEDED);
3650                        /* Expect a disconnection from the old connection */
3651                        transitionTo(mDisconnectingState);
3652                    } else {
3653                        loge("Failed to connect config: " + config + " netId: " + netId);
3654                        replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
3655                                WifiManager.ERROR);
3656                        break;
3657                    }
3658                    break;
3659                case WifiManager.SAVE_NETWORK:
3660                    config = (WifiConfiguration) message.obj;
3661                    NetworkUpdateResult result = mWifiConfigStore.saveNetwork(config);
3662                    if (result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID) {
3663                        replyToMessage(message, WifiManager.SAVE_NETWORK_SUCCEEDED);
3664                    } else {
3665                        loge("Failed to save network");
3666                        replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED,
3667                                WifiManager.ERROR);
3668                    }
3669                    break;
3670                case WifiManager.FORGET_NETWORK:
3671                    if (mWifiConfigStore.forgetNetwork(message.arg1)) {
3672                        replyToMessage(message, WifiManager.FORGET_NETWORK_SUCCEEDED);
3673                    } else {
3674                        loge("Failed to forget network");
3675                        replyToMessage(message, WifiManager.FORGET_NETWORK_FAILED,
3676                                WifiManager.ERROR);
3677                    }
3678                    break;
3679                case WifiManager.START_WPS:
3680                    WpsInfo wpsInfo = (WpsInfo) message.obj;
3681                    WpsResult wpsResult;
3682                    switch (wpsInfo.setup) {
3683                        case WpsInfo.PBC:
3684                            wpsResult = mWifiConfigStore.startWpsPbc(wpsInfo);
3685                            break;
3686                        case WpsInfo.KEYPAD:
3687                            wpsResult = mWifiConfigStore.startWpsWithPinFromAccessPoint(wpsInfo);
3688                            break;
3689                        case WpsInfo.DISPLAY:
3690                            wpsResult = mWifiConfigStore.startWpsWithPinFromDevice(wpsInfo);
3691                            break;
3692                        default:
3693                            wpsResult = new WpsResult(Status.FAILURE);
3694                            loge("Invalid setup for WPS");
3695                            break;
3696                    }
3697                    if (wpsResult.status == Status.SUCCESS) {
3698                        replyToMessage(message, WifiManager.START_WPS_SUCCEEDED, wpsResult);
3699                        transitionTo(mWpsRunningState);
3700                    } else {
3701                        loge("Failed to start WPS with config " + wpsInfo.toString());
3702                        replyToMessage(message, WifiManager.WPS_FAILED, WifiManager.ERROR);
3703                    }
3704                    break;
3705                case WifiMonitor.NETWORK_CONNECTION_EVENT:
3706                    if (DBG) log("Network connection established");
3707                    mLastNetworkId = message.arg1;
3708                    mLastBssid = (String) message.obj;
3709
3710                    mWifiInfo.setBSSID(mLastBssid);
3711                    mWifiInfo.setNetworkId(mLastNetworkId);
3712                    /* send event to CM & network change broadcast */
3713                    setNetworkDetailedState(DetailedState.OBTAINING_IPADDR);
3714                    sendNetworkStateChangeBroadcast(mLastBssid);
3715                    transitionTo(mObtainingIpState);
3716                    break;
3717                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
3718                    if (DBG) log("Network connection lost");
3719                    handleNetworkDisconnect();
3720                    transitionTo(mDisconnectedState);
3721                    break;
3722                default:
3723                    return NOT_HANDLED;
3724            }
3725            return HANDLED;
3726        }
3727    }
3728
3729    class L2ConnectedState extends State {
3730        @Override
3731        public void enter() {
3732            mRssiPollToken++;
3733            if (mEnableRssiPolling) {
3734                sendMessage(CMD_RSSI_POLL, mRssiPollToken, 0);
3735            }
3736        }
3737
3738        @Override
3739        public void exit() {
3740            handleNetworkDisconnect();
3741        }
3742
3743        @Override
3744        public boolean processMessage(Message message) {
3745            switch (message.what) {
3746              case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
3747                  handlePreDhcpSetup();
3748                  break;
3749              case DhcpStateMachine.CMD_POST_DHCP_ACTION:
3750                  handlePostDhcpSetup();
3751                  if (message.arg1 == DhcpStateMachine.DHCP_SUCCESS) {
3752                      if (DBG) log("DHCP successful");
3753                      handleSuccessfulIpConfiguration((DhcpResults) message.obj);
3754                      transitionTo(mVerifyingLinkState);
3755                  } else if (message.arg1 == DhcpStateMachine.DHCP_FAILURE) {
3756                      if (DBG) log("DHCP failed");
3757                      handleFailedIpConfiguration();
3758                      transitionTo(mDisconnectingState);
3759                  }
3760                  break;
3761                case CMD_DISCONNECT:
3762                    mWifiNative.disconnect();
3763                    transitionTo(mDisconnectingState);
3764                    break;
3765                case WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST:
3766                    if (message.arg1 == 1) {
3767                        mWifiNative.disconnect();
3768                        mTemporarilyDisconnectWifi = true;
3769                        transitionTo(mDisconnectingState);
3770                    }
3771                    break;
3772                case CMD_SET_OPERATIONAL_MODE:
3773                    if (message.arg1 != CONNECT_MODE) {
3774                        sendMessage(CMD_DISCONNECT);
3775                        deferMessage(message);
3776                    }
3777                    break;
3778                case CMD_START_SCAN:
3779                    /* Do not attempt to connect when we are already connected */
3780                    handleScanRequest(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, message);
3781                    break;
3782                    /* Ignore connection to same network */
3783                case WifiManager.CONNECT_NETWORK:
3784                    int netId = message.arg1;
3785                    if (mWifiInfo.getNetworkId() == netId) {
3786                        break;
3787                    }
3788                    return NOT_HANDLED;
3789                case WifiManager.SAVE_NETWORK:
3790                    WifiConfiguration config = (WifiConfiguration) message.obj;
3791                    NetworkUpdateResult result = mWifiConfigStore.saveNetwork(config);
3792                    if (mWifiInfo.getNetworkId() == result.getNetworkId()) {
3793                        if (result.hasIpChanged()) {
3794                            log("Reconfiguring IP on connection");
3795                            transitionTo(mObtainingIpState);
3796                        }
3797                        if (result.hasProxyChanged()) {
3798                            log("Reconfiguring proxy on connection");
3799                            updateLinkProperties();
3800                        }
3801                    }
3802
3803                    if (result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID) {
3804                        replyToMessage(message, WifiManager.SAVE_NETWORK_SUCCEEDED);
3805                    } else {
3806                        loge("Failed to save network");
3807                        replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED,
3808                                WifiManager.ERROR);
3809                    }
3810                    break;
3811                    /* Ignore */
3812                case WifiMonitor.NETWORK_CONNECTION_EVENT:
3813                    break;
3814                case CMD_RSSI_POLL:
3815                    if (message.arg1 == mRssiPollToken) {
3816                        // Get Info and continue polling
3817                        fetchRssiAndLinkSpeedNative();
3818                        sendMessageDelayed(obtainMessage(CMD_RSSI_POLL,
3819                                mRssiPollToken, 0), POLL_RSSI_INTERVAL_MSECS);
3820                    } else {
3821                        // Polling has completed
3822                    }
3823                    break;
3824                case CMD_ENABLE_RSSI_POLL:
3825                    mEnableRssiPolling = (message.arg1 == 1);
3826                    mRssiPollToken++;
3827                    if (mEnableRssiPolling) {
3828                        // first poll
3829                        fetchRssiAndLinkSpeedNative();
3830                        sendMessageDelayed(obtainMessage(CMD_RSSI_POLL,
3831                                mRssiPollToken, 0), POLL_RSSI_INTERVAL_MSECS);
3832                    }
3833                    break;
3834                case WifiManager.RSSI_PKTCNT_FETCH:
3835                    RssiPacketCountInfo info = new RssiPacketCountInfo();
3836                    fetchRssiAndLinkSpeedNative();
3837                    info.rssi = mWifiInfo.getRssi();
3838                    fetchPktcntNative(info);
3839                    replyToMessage(message, WifiManager.RSSI_PKTCNT_FETCH_SUCCEEDED, info);
3840                    break;
3841                default:
3842                    return NOT_HANDLED;
3843            }
3844
3845            return HANDLED;
3846        }
3847    }
3848
3849    class ObtainingIpState extends State {
3850        @Override
3851        public void enter() {
3852            if (!mWifiConfigStore.isUsingStaticIp(mLastNetworkId)) {
3853                // TODO: If we're switching between static IP configuration and DHCP, remove the
3854                // static configuration first.
3855                startDhcp();
3856            } else {
3857                // stop any running dhcp before assigning static IP
3858                stopDhcp();
3859                DhcpResults dhcpResults = new DhcpResults(
3860                        mWifiConfigStore.getLinkProperties(mLastNetworkId));
3861                InterfaceConfiguration ifcg = new InterfaceConfiguration();
3862                Iterator<LinkAddress> addrs =
3863                        dhcpResults.linkProperties.getLinkAddresses().iterator();
3864                if (!addrs.hasNext()) {
3865                    loge("Static IP lacks address");
3866                    sendMessage(CMD_STATIC_IP_FAILURE);
3867                } else {
3868                    ifcg.setLinkAddress(addrs.next());
3869                    ifcg.setInterfaceUp();
3870                    try {
3871                        mNwService.setInterfaceConfig(mInterfaceName, ifcg);
3872                        if (DBG) log("Static IP configuration succeeded");
3873                        sendMessage(CMD_STATIC_IP_SUCCESS, dhcpResults);
3874                    } catch (RemoteException re) {
3875                        loge("Static IP configuration failed: " + re);
3876                        sendMessage(CMD_STATIC_IP_FAILURE);
3877                    } catch (IllegalStateException e) {
3878                        loge("Static IP configuration failed: " + e);
3879                        sendMessage(CMD_STATIC_IP_FAILURE);
3880                    }
3881                }
3882            }
3883        }
3884      @Override
3885      public boolean processMessage(Message message) {
3886          if (DBG) log(getName() + message.toString() + "\n");
3887          switch(message.what) {
3888            case CMD_STATIC_IP_SUCCESS:
3889                  handleSuccessfulIpConfiguration((DhcpResults) message.obj);
3890                  transitionTo(mVerifyingLinkState);
3891                  break;
3892              case CMD_STATIC_IP_FAILURE:
3893                  handleFailedIpConfiguration();
3894                  transitionTo(mDisconnectingState);
3895                  break;
3896             case WifiManager.SAVE_NETWORK:
3897                  deferMessage(message);
3898                  break;
3899                  /* Defer any power mode changes since we must keep active power mode at DHCP */
3900              case CMD_SET_HIGH_PERF_MODE:
3901                  deferMessage(message);
3902                  break;
3903                  /* Defer scan request since we should not switch to other channels at DHCP */
3904              case CMD_START_SCAN:
3905                  deferMessage(message);
3906                  break;
3907              default:
3908                  return NOT_HANDLED;
3909          }
3910          return HANDLED;
3911      }
3912    }
3913
3914    class VerifyingLinkState extends State {
3915        @Override
3916        public void enter() {
3917            log(getName() + " enter");
3918            setNetworkDetailedState(DetailedState.VERIFYING_POOR_LINK);
3919            mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.VERIFYING_POOR_LINK);
3920            sendNetworkStateChangeBroadcast(mLastBssid);
3921        }
3922        @Override
3923        public boolean processMessage(Message message) {
3924            switch (message.what) {
3925                case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
3926                    //stay here
3927                    log(getName() + " POOR_LINK_DETECTED: no transition");
3928                    break;
3929                case WifiWatchdogStateMachine.GOOD_LINK_DETECTED:
3930                    log(getName() + " GOOD_LINK_DETECTED: transition to captive portal check");
3931                    // Send out a broadcast with the CAPTIVE_PORTAL_CHECK to preserve
3932                    // existing behaviour. The captive portal check really happens after we
3933                    // transition into DetailedState.CONNECTED.
3934                    setNetworkDetailedState(DetailedState.CAPTIVE_PORTAL_CHECK);
3935                    mWifiConfigStore.updateStatus(mLastNetworkId,
3936                            DetailedState.CAPTIVE_PORTAL_CHECK);
3937                    sendNetworkStateChangeBroadcast(mLastBssid);
3938
3939                    // NOTE: This might look like an odd place to enable IPV6 but this is in
3940                    // response to transitioning into GOOD_LINK_DETECTED. Similarly, we disable
3941                    // ipv6 when we transition into POOR_LINK_DETECTED in mConnectedState.
3942                    try {
3943                        mNwService.enableIpv6(mInterfaceName);
3944                    } catch (RemoteException re) {
3945                        loge("Failed to enable IPv6: " + re);
3946                    } catch (IllegalStateException e) {
3947                        loge("Failed to enable IPv6: " + e);
3948                    }
3949
3950                    log(getName() + " GOOD_LINK_DETECTED: transition to CONNECTED");
3951                    setNetworkDetailedState(DetailedState.CONNECTED);
3952                    mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.CONNECTED);
3953                    sendNetworkStateChangeBroadcast(mLastBssid);
3954                    transitionTo(mConnectedState);
3955
3956                    break;
3957                default:
3958                    if (DBG) log(getName() + " what=" + message.what + " NOT_HANDLED");
3959                    return NOT_HANDLED;
3960            }
3961            return HANDLED;
3962        }
3963    }
3964
3965    class ConnectedState extends State {
3966        @Override
3967        public boolean processMessage(Message message) {
3968            switch (message.what) {
3969               case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
3970                    if (DBG) log("Watchdog reports poor link");
3971                    try {
3972                        mNwService.disableIpv6(mInterfaceName);
3973                    } catch (RemoteException re) {
3974                        loge("Failed to disable IPv6: " + re);
3975                    } catch (IllegalStateException e) {
3976                        loge("Failed to disable IPv6: " + e);
3977                    }
3978                    /* Report a disconnect */
3979                    setNetworkDetailedState(DetailedState.DISCONNECTED);
3980                    mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.DISCONNECTED);
3981                    sendNetworkStateChangeBroadcast(mLastBssid);
3982
3983                    transitionTo(mVerifyingLinkState);
3984                    break;
3985                default:
3986                    return NOT_HANDLED;
3987            }
3988            return HANDLED;
3989        }
3990
3991        @Override
3992        public void exit() {
3993            /* Request a CS wakelock during transition to mobile */
3994            checkAndSetConnectivityInstance();
3995            mCm.requestNetworkTransitionWakelock(getName());
3996        }
3997    }
3998
3999    class DisconnectingState extends State {
4000        @Override
4001        public boolean processMessage(Message message) {
4002            switch (message.what) {
4003                case CMD_SET_OPERATIONAL_MODE:
4004                    if (message.arg1 != CONNECT_MODE) {
4005                        deferMessage(message);
4006                    }
4007                    break;
4008                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
4009                    /* If we get a SUPPLICANT_STATE_CHANGE_EVENT before NETWORK_DISCONNECTION_EVENT
4010                     * we have missed the network disconnection, transition to mDisconnectedState
4011                     * and handle the rest of the events there
4012                     */
4013                    deferMessage(message);
4014                    handleNetworkDisconnect();
4015                    transitionTo(mDisconnectedState);
4016                    break;
4017                default:
4018                    return NOT_HANDLED;
4019            }
4020            return HANDLED;
4021        }
4022    }
4023
4024    class DisconnectedState extends State {
4025        private boolean mAlarmEnabled = false;
4026        /* This is set from the overlay config file or from a secure setting.
4027         * A value of 0 disables scanning in the framework.
4028         */
4029        private long mFrameworkScanIntervalMs;
4030
4031        private void setScanAlarm(boolean enabled) {
4032            if (enabled == mAlarmEnabled) return;
4033            if (enabled) {
4034                if (mFrameworkScanIntervalMs > 0) {
4035                    mAlarmManager.setRepeating(AlarmManager.RTC_WAKEUP,
4036                            System.currentTimeMillis() + mFrameworkScanIntervalMs,
4037                            mFrameworkScanIntervalMs,
4038                            mScanIntent);
4039                    mAlarmEnabled = true;
4040                }
4041            } else {
4042                mAlarmManager.cancel(mScanIntent);
4043                mAlarmEnabled = false;
4044            }
4045        }
4046
4047        @Override
4048        public void enter() {
4049            // We dont scan frequently if this is a temporary disconnect
4050            // due to p2p
4051            if (mTemporarilyDisconnectWifi) {
4052                mWifiP2pChannel.sendMessage(WifiP2pServiceImpl.DISCONNECT_WIFI_RESPONSE);
4053                return;
4054            }
4055
4056            mFrameworkScanIntervalMs = Settings.Global.getLong(mContext.getContentResolver(),
4057                    Settings.Global.WIFI_FRAMEWORK_SCAN_INTERVAL_MS,
4058                    mDefaultFrameworkScanIntervalMs);
4059            /*
4060             * We initiate background scanning if it is enabled, otherwise we
4061             * initiate an infrequent scan that wakes up the device to ensure
4062             * a user connects to an access point on the move
4063             */
4064            if (mEnableBackgroundScan) {
4065                /* If a regular scan result is pending, do not initiate background
4066                 * scan until the scan results are returned. This is needed because
4067                 * initiating a background scan will cancel the regular scan and
4068                 * scan results will not be returned until background scanning is
4069                 * cleared
4070                 */
4071                if (!mIsScanOngoing) {
4072                    mWifiNative.enableBackgroundScan(true);
4073                }
4074            } else {
4075                setScanAlarm(true);
4076            }
4077
4078            /**
4079             * If we have no networks saved, the supplicant stops doing the periodic scan.
4080             * The scans are useful to notify the user of the presence of an open network.
4081             * Note that these are not wake up scans.
4082             */
4083            if (!mP2pConnected.get() && mWifiConfigStore.getConfiguredNetworks().size() == 0) {
4084                sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,
4085                            ++mPeriodicScanToken, 0), mSupplicantScanIntervalMs);
4086            }
4087        }
4088        @Override
4089        public boolean processMessage(Message message) {
4090            boolean ret = HANDLED;
4091            switch (message.what) {
4092                case CMD_NO_NETWORKS_PERIODIC_SCAN:
4093                    if (mP2pConnected.get()) break;
4094                    if (message.arg1 == mPeriodicScanToken &&
4095                            mWifiConfigStore.getConfiguredNetworks().size() == 0) {
4096                        startScan(UNKNOWN_SCAN_SOURCE, null, null);
4097                        sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,
4098                                    ++mPeriodicScanToken, 0), mSupplicantScanIntervalMs);
4099                    }
4100                    break;
4101                case WifiManager.FORGET_NETWORK:
4102                case CMD_REMOVE_NETWORK:
4103                    // Set up a delayed message here. After the forget/remove is handled
4104                    // the handled delayed message will determine if there is a need to
4105                    // scan and continue
4106                    sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,
4107                                ++mPeriodicScanToken, 0), mSupplicantScanIntervalMs);
4108                    ret = NOT_HANDLED;
4109                    break;
4110                case CMD_SET_OPERATIONAL_MODE:
4111                    if (message.arg1 != CONNECT_MODE) {
4112                        mOperationalMode = message.arg1;
4113
4114                        mWifiConfigStore.disableAllNetworks();
4115                        if (mOperationalMode == SCAN_ONLY_WITH_WIFI_OFF_MODE) {
4116                            mWifiP2pChannel.sendMessage(CMD_DISABLE_P2P_REQ);
4117                            setWifiState(WIFI_STATE_DISABLED);
4118                        }
4119
4120                        transitionTo(mScanModeState);
4121                    }
4122                    break;
4123                case CMD_ENABLE_BACKGROUND_SCAN:
4124                    mEnableBackgroundScan = (message.arg1 == 1);
4125                    if (mEnableBackgroundScan) {
4126                        mWifiNative.enableBackgroundScan(true);
4127                        setScanAlarm(false);
4128                    } else {
4129                        mWifiNative.enableBackgroundScan(false);
4130                        setScanAlarm(true);
4131                    }
4132                    break;
4133                    /* Ignore network disconnect */
4134                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
4135                    break;
4136                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
4137                    StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
4138                    setNetworkDetailedState(WifiInfo.getDetailedStateOf(stateChangeResult.state));
4139                    /* ConnectModeState does the rest of the handling */
4140                    ret = NOT_HANDLED;
4141                    break;
4142                case CMD_START_SCAN:
4143                    /* Disable background scan temporarily during a regular scan */
4144                    if (mEnableBackgroundScan) {
4145                        mWifiNative.enableBackgroundScan(false);
4146                    }
4147                    /* Handled in parent state */
4148                    ret = NOT_HANDLED;
4149                    break;
4150                case WifiMonitor.SCAN_RESULTS_EVENT:
4151                    /* Re-enable background scan when a pending scan result is received */
4152                    if (mEnableBackgroundScan && mIsScanOngoing) {
4153                        mWifiNative.enableBackgroundScan(true);
4154                    }
4155                    /* Handled in parent state */
4156                    ret = NOT_HANDLED;
4157                    break;
4158                case WifiP2pServiceImpl.P2P_CONNECTION_CHANGED:
4159                    NetworkInfo info = (NetworkInfo) message.obj;
4160                    mP2pConnected.set(info.isConnected());
4161                    if (mP2pConnected.get()) {
4162                        int defaultInterval = mContext.getResources().getInteger(
4163                                R.integer.config_wifi_scan_interval_p2p_connected);
4164                        long scanIntervalMs = Settings.Global.getLong(mContext.getContentResolver(),
4165                                Settings.Global.WIFI_SCAN_INTERVAL_WHEN_P2P_CONNECTED_MS,
4166                                defaultInterval);
4167                        mWifiNative.setScanInterval((int) scanIntervalMs/1000);
4168                    } else if (mWifiConfigStore.getConfiguredNetworks().size() == 0) {
4169                        if (DBG) log("Turn on scanning after p2p disconnected");
4170                        sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,
4171                                    ++mPeriodicScanToken, 0), mSupplicantScanIntervalMs);
4172                    }
4173                case CMD_RECONNECT:
4174                case CMD_REASSOCIATE:
4175                    if (mTemporarilyDisconnectWifi) {
4176                        // Drop a third party reconnect/reassociate if STA is
4177                        // temporarily disconnected for p2p
4178                        break;
4179                    } else {
4180                        // ConnectModeState handles it
4181                        ret = NOT_HANDLED;
4182                    }
4183                    break;
4184                default:
4185                    ret = NOT_HANDLED;
4186            }
4187            return ret;
4188        }
4189
4190        @Override
4191        public void exit() {
4192            /* No need for a background scan upon exit from a disconnected state */
4193            if (mEnableBackgroundScan) {
4194                mWifiNative.enableBackgroundScan(false);
4195            }
4196            setScanAlarm(false);
4197        }
4198    }
4199
4200    class WpsRunningState extends State {
4201        //Tracks the source to provide a reply
4202        private Message mSourceMessage;
4203        @Override
4204        public void enter() {
4205            mSourceMessage = Message.obtain(getCurrentMessage());
4206        }
4207        @Override
4208        public boolean processMessage(Message message) {
4209            switch (message.what) {
4210                case WifiMonitor.WPS_SUCCESS_EVENT:
4211                    // Ignore intermediate success, wait for full connection
4212                    break;
4213                case WifiMonitor.NETWORK_CONNECTION_EVENT:
4214                    replyToMessage(mSourceMessage, WifiManager.WPS_COMPLETED);
4215                    mSourceMessage.recycle();
4216                    mSourceMessage = null;
4217                    deferMessage(message);
4218                    transitionTo(mDisconnectedState);
4219                    break;
4220                case WifiMonitor.WPS_OVERLAP_EVENT:
4221                    replyToMessage(mSourceMessage, WifiManager.WPS_FAILED,
4222                            WifiManager.WPS_OVERLAP_ERROR);
4223                    mSourceMessage.recycle();
4224                    mSourceMessage = null;
4225                    transitionTo(mDisconnectedState);
4226                    break;
4227                case WifiMonitor.WPS_FAIL_EVENT:
4228                    //arg1 has the reason for the failure
4229                    replyToMessage(mSourceMessage, WifiManager.WPS_FAILED, message.arg1);
4230                    mSourceMessage.recycle();
4231                    mSourceMessage = null;
4232                    transitionTo(mDisconnectedState);
4233                    break;
4234                case WifiMonitor.WPS_TIMEOUT_EVENT:
4235                    replyToMessage(mSourceMessage, WifiManager.WPS_FAILED,
4236                            WifiManager.WPS_TIMED_OUT);
4237                    mSourceMessage.recycle();
4238                    mSourceMessage = null;
4239                    transitionTo(mDisconnectedState);
4240                    break;
4241                case WifiManager.START_WPS:
4242                    replyToMessage(message, WifiManager.WPS_FAILED, WifiManager.IN_PROGRESS);
4243                    break;
4244                case WifiManager.CANCEL_WPS:
4245                    if (mWifiNative.cancelWps()) {
4246                        replyToMessage(message, WifiManager.CANCEL_WPS_SUCCEDED);
4247                    } else {
4248                        replyToMessage(message, WifiManager.CANCEL_WPS_FAILED, WifiManager.ERROR);
4249                    }
4250                    transitionTo(mDisconnectedState);
4251                    break;
4252                /* Defer all commands that can cause connections to a different network
4253                 * or put the state machine out of connect mode
4254                 */
4255                case CMD_STOP_DRIVER:
4256                case CMD_SET_OPERATIONAL_MODE:
4257                case WifiManager.CONNECT_NETWORK:
4258                case CMD_ENABLE_NETWORK:
4259                case CMD_RECONNECT:
4260                case CMD_REASSOCIATE:
4261                    deferMessage(message);
4262                    break;
4263                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
4264                    if (DBG) log("Network connection lost");
4265                    handleNetworkDisconnect();
4266                    break;
4267                case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
4268                    if (DBG) log("Ignore Assoc reject event during WPS Connection");
4269                    break;
4270                case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
4271                    // Disregard auth failure events during WPS connection. The
4272                    // EAP sequence is retried several times, and there might be
4273                    // failures (especially for wps pin). We will get a WPS_XXX
4274                    // event at the end of the sequence anyway.
4275                    if (DBG) log("Ignore auth failure during WPS connection");
4276                    break;
4277                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
4278                    //Throw away supplicant state changes when WPS is running.
4279                    //We will start getting supplicant state changes once we get
4280                    //a WPS success or failure
4281                    break;
4282                default:
4283                    return NOT_HANDLED;
4284            }
4285            return HANDLED;
4286        }
4287
4288        @Override
4289        public void exit() {
4290            mWifiConfigStore.enableAllNetworks();
4291            mWifiConfigStore.loadConfiguredNetworks();
4292        }
4293    }
4294
4295    class SoftApStartingState extends State {
4296        @Override
4297        public void enter() {
4298            final Message message = getCurrentMessage();
4299            if (message.what == CMD_START_AP) {
4300                final WifiConfiguration config = (WifiConfiguration) message.obj;
4301
4302                if (config == null) {
4303                    mWifiApConfigChannel.sendMessage(CMD_REQUEST_AP_CONFIG);
4304                } else {
4305                    mWifiApConfigChannel.sendMessage(CMD_SET_AP_CONFIG, config);
4306                    startSoftApWithConfig(config);
4307                }
4308            } else {
4309                throw new RuntimeException("Illegal transition to SoftApStartingState: " + message);
4310            }
4311        }
4312        @Override
4313        public boolean processMessage(Message message) {
4314            switch(message.what) {
4315                case CMD_START_SUPPLICANT:
4316                case CMD_STOP_SUPPLICANT:
4317                case CMD_START_AP:
4318                case CMD_STOP_AP:
4319                case CMD_START_DRIVER:
4320                case CMD_STOP_DRIVER:
4321                case CMD_SET_OPERATIONAL_MODE:
4322                case CMD_SET_COUNTRY_CODE:
4323                case CMD_SET_FREQUENCY_BAND:
4324                case CMD_START_PACKET_FILTERING:
4325                case CMD_STOP_PACKET_FILTERING:
4326                case CMD_TETHER_STATE_CHANGE:
4327                    deferMessage(message);
4328                    break;
4329                case WifiStateMachine.CMD_RESPONSE_AP_CONFIG:
4330                    WifiConfiguration config = (WifiConfiguration) message.obj;
4331                    if (config != null) {
4332                        startSoftApWithConfig(config);
4333                    } else {
4334                        loge("Softap config is null!");
4335                        sendMessage(CMD_START_AP_FAILURE);
4336                    }
4337                    break;
4338                case CMD_START_AP_SUCCESS:
4339                    setWifiApState(WIFI_AP_STATE_ENABLED);
4340                    transitionTo(mSoftApStartedState);
4341                    break;
4342                case CMD_START_AP_FAILURE:
4343                    setWifiApState(WIFI_AP_STATE_FAILED);
4344                    transitionTo(mInitialState);
4345                    break;
4346                default:
4347                    return NOT_HANDLED;
4348            }
4349            return HANDLED;
4350        }
4351    }
4352
4353    class SoftApStartedState extends State {
4354        @Override
4355        public boolean processMessage(Message message) {
4356            switch(message.what) {
4357                case CMD_STOP_AP:
4358                    if (DBG) log("Stopping Soft AP");
4359                    /* We have not tethered at this point, so we just shutdown soft Ap */
4360                    try {
4361                        mNwService.stopAccessPoint(mInterfaceName);
4362                    } catch(Exception e) {
4363                        loge("Exception in stopAccessPoint()");
4364                    }
4365                    setWifiApState(WIFI_AP_STATE_DISABLED);
4366                    transitionTo(mInitialState);
4367                    break;
4368                case CMD_START_AP:
4369                    // Ignore a start on a running access point
4370                    break;
4371                    /* Fail client mode operation when soft AP is enabled */
4372                case CMD_START_SUPPLICANT:
4373                    loge("Cannot start supplicant with a running soft AP");
4374                    setWifiState(WIFI_STATE_UNKNOWN);
4375                    break;
4376                case CMD_TETHER_STATE_CHANGE:
4377                    TetherStateChange stateChange = (TetherStateChange) message.obj;
4378                    if (startTethering(stateChange.available)) {
4379                        transitionTo(mTetheringState);
4380                    }
4381                    break;
4382                default:
4383                    return NOT_HANDLED;
4384            }
4385            return HANDLED;
4386        }
4387    }
4388
4389    class TetheringState extends State {
4390        @Override
4391        public void enter() {
4392            /* Send ourselves a delayed message to shut down if tethering fails to notify */
4393            sendMessageDelayed(obtainMessage(CMD_TETHER_NOTIFICATION_TIMED_OUT,
4394                    ++mTetherToken, 0), TETHER_NOTIFICATION_TIME_OUT_MSECS);
4395        }
4396        @Override
4397        public boolean processMessage(Message message) {
4398            switch(message.what) {
4399                case CMD_TETHER_STATE_CHANGE:
4400                    TetherStateChange stateChange = (TetherStateChange) message.obj;
4401                    if (isWifiTethered(stateChange.active)) {
4402                        transitionTo(mTetheredState);
4403                    }
4404                    return HANDLED;
4405                case CMD_TETHER_NOTIFICATION_TIMED_OUT:
4406                    if (message.arg1 == mTetherToken) {
4407                        loge("Failed to get tether update, shutdown soft access point");
4408                        transitionTo(mSoftApStartedState);
4409                        // Needs to be first thing handled
4410                        sendMessageAtFrontOfQueue(CMD_STOP_AP);
4411                    }
4412                    break;
4413                case CMD_START_SUPPLICANT:
4414                case CMD_STOP_SUPPLICANT:
4415                case CMD_START_AP:
4416                case CMD_STOP_AP:
4417                case CMD_START_DRIVER:
4418                case CMD_STOP_DRIVER:
4419                case CMD_SET_OPERATIONAL_MODE:
4420                case CMD_SET_COUNTRY_CODE:
4421                case CMD_SET_FREQUENCY_BAND:
4422                case CMD_START_PACKET_FILTERING:
4423                case CMD_STOP_PACKET_FILTERING:
4424                    deferMessage(message);
4425                    break;
4426                default:
4427                    return NOT_HANDLED;
4428            }
4429            return HANDLED;
4430        }
4431    }
4432
4433    class TetheredState extends State {
4434        @Override
4435        public boolean processMessage(Message message) {
4436            switch(message.what) {
4437                case CMD_TETHER_STATE_CHANGE:
4438                    TetherStateChange stateChange = (TetherStateChange) message.obj;
4439                    if (!isWifiTethered(stateChange.active)) {
4440                        loge("Tethering reports wifi as untethered!, shut down soft Ap");
4441                        setHostApRunning(null, false);
4442                        setHostApRunning(null, true);
4443                    }
4444                    return HANDLED;
4445                case CMD_STOP_AP:
4446                    if (DBG) log("Untethering before stopping AP");
4447                    setWifiApState(WIFI_AP_STATE_DISABLING);
4448                    stopTethering();
4449                    transitionTo(mUntetheringState);
4450                    // More work to do after untethering
4451                    deferMessage(message);
4452                    break;
4453                default:
4454                    return NOT_HANDLED;
4455            }
4456            return HANDLED;
4457        }
4458    }
4459
4460    class UntetheringState extends State {
4461        @Override
4462        public void enter() {
4463            /* Send ourselves a delayed message to shut down if tethering fails to notify */
4464            sendMessageDelayed(obtainMessage(CMD_TETHER_NOTIFICATION_TIMED_OUT,
4465                    ++mTetherToken, 0), TETHER_NOTIFICATION_TIME_OUT_MSECS);
4466
4467        }
4468        @Override
4469        public boolean processMessage(Message message) {
4470            switch(message.what) {
4471                case CMD_TETHER_STATE_CHANGE:
4472                    TetherStateChange stateChange = (TetherStateChange) message.obj;
4473
4474                    /* Wait till wifi is untethered */
4475                    if (isWifiTethered(stateChange.active)) break;
4476
4477                    transitionTo(mSoftApStartedState);
4478                    break;
4479                case CMD_TETHER_NOTIFICATION_TIMED_OUT:
4480                    if (message.arg1 == mTetherToken) {
4481                        loge("Failed to get tether update, force stop access point");
4482                        transitionTo(mSoftApStartedState);
4483                    }
4484                    break;
4485                case CMD_START_SUPPLICANT:
4486                case CMD_STOP_SUPPLICANT:
4487                case CMD_START_AP:
4488                case CMD_STOP_AP:
4489                case CMD_START_DRIVER:
4490                case CMD_STOP_DRIVER:
4491                case CMD_SET_OPERATIONAL_MODE:
4492                case CMD_SET_COUNTRY_CODE:
4493                case CMD_SET_FREQUENCY_BAND:
4494                case CMD_START_PACKET_FILTERING:
4495                case CMD_STOP_PACKET_FILTERING:
4496                    deferMessage(message);
4497                    break;
4498                default:
4499                    return NOT_HANDLED;
4500            }
4501            return HANDLED;
4502        }
4503    }
4504
4505    //State machine initiated requests can have replyTo set to null indicating
4506    //there are no recepients, we ignore those reply actions
4507    private void replyToMessage(Message msg, int what) {
4508        if (msg.replyTo == null) return;
4509        Message dstMsg = obtainMessageWithArg2(msg);
4510        dstMsg.what = what;
4511        mReplyChannel.replyToMessage(msg, dstMsg);
4512    }
4513
4514    private void replyToMessage(Message msg, int what, int arg1) {
4515        if (msg.replyTo == null) return;
4516        Message dstMsg = obtainMessageWithArg2(msg);
4517        dstMsg.what = what;
4518        dstMsg.arg1 = arg1;
4519        mReplyChannel.replyToMessage(msg, dstMsg);
4520    }
4521
4522    private void replyToMessage(Message msg, int what, Object obj) {
4523        if (msg.replyTo == null) return;
4524        Message dstMsg = obtainMessageWithArg2(msg);
4525        dstMsg.what = what;
4526        dstMsg.obj = obj;
4527        mReplyChannel.replyToMessage(msg, dstMsg);
4528    }
4529
4530    /**
4531     * arg2 on the source message has a unique id that needs to be retained in replies
4532     * to match the request
4533
4534     * see WifiManager for details
4535     */
4536    private Message obtainMessageWithArg2(Message srcMsg) {
4537        Message msg = Message.obtain();
4538        msg.arg2 = srcMsg.arg2;
4539        return msg;
4540    }
4541}
4542