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