WifiStateMachine.java revision 2d57d860c2ddf792f4afd6101c19a6d0cf01547c
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 android.net.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: Add soft AP states as part of WIFI_STATE_XXX
27 * Retain WIFI_STATE_ENABLING that indicates driver is loading
28 * Add WIFI_STATE_AP_ENABLED to indicate soft AP has started
29 * and WIFI_STATE_FAILED for failure
30 * Deprecate WIFI_STATE_UNKNOWN
31 *
32 * Doing this will simplify the logic for sending broadcasts
33 */
34import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED;
35import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLING;
36import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
37import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLING;
38import static android.net.wifi.WifiManager.WIFI_AP_STATE_FAILED;
39
40import android.app.AlarmManager;
41import android.app.PendingIntent;
42import android.app.backup.IBackupManager;
43import android.bluetooth.BluetoothAdapter;
44import android.content.BroadcastReceiver;
45import android.content.Context;
46import android.content.Intent;
47import android.content.IntentFilter;
48import android.net.ConnectivityManager;
49import android.net.DhcpInfo;
50import android.net.DhcpInfoInternal;
51import android.net.DhcpStateMachine;
52import android.net.InterfaceConfiguration;
53import android.net.LinkAddress;
54import android.net.LinkProperties;
55import android.net.NetworkInfo;
56import android.net.NetworkInfo.DetailedState;
57import android.net.NetworkUtils;
58import android.net.wifi.WpsResult.Status;
59import android.net.wifi.p2p.WifiP2pManager;
60import android.net.wifi.p2p.WifiP2pService;
61import android.net.wifi.StateChangeResult;
62import android.os.Binder;
63import android.os.IBinder;
64import android.os.INetworkManagementService;
65import android.os.Message;
66import android.os.Messenger;
67import android.os.PowerManager;
68import android.os.Process;
69import android.os.RemoteException;
70import android.os.ServiceManager;
71import android.os.SystemClock;
72import android.os.SystemProperties;
73import android.os.WorkSource;
74import android.provider.Settings;
75import android.util.EventLog;
76import android.util.Log;
77import android.util.LruCache;
78
79import com.android.internal.app.IBatteryStats;
80import com.android.internal.util.AsyncChannel;
81import com.android.internal.util.Protocol;
82import com.android.internal.util.State;
83import com.android.internal.util.StateMachine;
84
85import java.net.InetAddress;
86import java.util.ArrayList;
87import java.util.List;
88import java.util.concurrent.atomic.AtomicInteger;
89import java.util.concurrent.atomic.AtomicBoolean;
90import java.util.regex.Pattern;
91
92/**
93 * Track the state of Wifi connectivity. All event handling is done here,
94 * and all changes in connectivity state are initiated here.
95 *
96 * Wi-Fi now supports three modes of operation: Client, Soft Ap and Direct
97 * In the current implementation, we do not support any concurrency and thus only
98 * one of Client, Soft Ap or Direct operation is supported at any time.
99 *
100 * The WifiStateMachine supports Soft Ap and Client operations while WifiP2pService
101 * handles Direct. WifiP2pService and WifiStateMachine co-ordinate to ensure only
102 * one exists at a certain time.
103 *
104 * @hide
105 */
106public class WifiStateMachine extends StateMachine {
107
108    private static final String TAG = "WifiStateMachine";
109    private static final String NETWORKTYPE = "WIFI";
110    private static final boolean DBG = false;
111
112    /* TODO: This is no more used with the hostapd code. Clean up */
113    private static final String SOFTAP_IFACE = "wl0.1";
114
115    private WifiMonitor mWifiMonitor;
116    private INetworkManagementService mNwService;
117    private ConnectivityManager mCm;
118
119    /* Scan results handling */
120    private List<ScanResult> mScanResults;
121    private static final Pattern scanResultPattern = Pattern.compile("\t+");
122    private static final int SCAN_RESULT_CACHE_SIZE = 80;
123    private final LruCache<String, ScanResult> mScanResultCache;
124
125    private String mInterfaceName;
126
127    private int mLastSignalLevel = -1;
128    private String mLastBssid;
129    private int mLastNetworkId;
130    private boolean mEnableRssiPolling = false;
131    private boolean mEnableBackgroundScan = false;
132    private int mRssiPollToken = 0;
133    private int mReconnectCount = 0;
134    private boolean mIsScanMode = false;
135    private boolean mScanResultIsPending = false;
136
137    private boolean mBluetoothConnectionActive = false;
138
139    /**
140     * Interval in milliseconds between polling for RSSI
141     * and linkspeed information
142     */
143    private static final int POLL_RSSI_INTERVAL_MSECS = 3000;
144
145    /**
146     * Delay between supplicant restarts upon failure to establish connection
147     */
148    private static final int SUPPLICANT_RESTART_INTERVAL_MSECS = 5000;
149
150    /**
151     * Number of times we attempt to restart supplicant
152     */
153    private static final int SUPPLICANT_RESTART_TRIES = 5;
154
155    private int mSupplicantRestartCount = 0;
156    /* Tracks sequence number on stop failure message */
157    private int mSupplicantStopFailureToken = 0;
158
159    private LinkProperties mLinkProperties;
160
161    // Wakelock held during wifi start/stop and driver load/unload
162    private PowerManager.WakeLock mWakeLock;
163
164    private Context mContext;
165
166    private DhcpInfoInternal mDhcpInfoInternal;
167    private WifiInfo mWifiInfo;
168    private NetworkInfo mNetworkInfo;
169    private SupplicantStateTracker mSupplicantStateTracker;
170    private WpsStateMachine mWpsStateMachine;
171    private DhcpStateMachine mDhcpStateMachine;
172
173    private AlarmManager mAlarmManager;
174    private PendingIntent mScanIntent;
175    /* Tracks current frequency mode */
176    private AtomicInteger mFrequencyBand = new AtomicInteger(WifiManager.WIFI_FREQUENCY_BAND_AUTO);
177
178    /* Tracks if we are filtering Multicast v4 packets. Default is to filter. */
179    private AtomicBoolean mFilteringMulticastV4Packets = new AtomicBoolean(true);
180
181    // Channel for sending replies.
182    private AsyncChannel mReplyChannel = new AsyncChannel();
183
184    private WifiP2pManager mWifiP2pManager;
185    //Used to initiate a connection with WifiP2pService
186    private AsyncChannel mWifiP2pChannel = new AsyncChannel();
187
188    // Event log tags (must be in sync with event-log-tags)
189    private static final int EVENTLOG_WIFI_STATE_CHANGED        = 50021;
190    private static final int EVENTLOG_WIFI_EVENT_HANDLED        = 50022;
191    private static final int EVENTLOG_SUPPLICANT_STATE_CHANGED  = 50023;
192
193    /* The base for wifi message types */
194    static final int BASE = Protocol.BASE_WIFI;
195    /* Load the driver */
196    static final int CMD_LOAD_DRIVER                      = BASE + 1;
197    /* Unload the driver */
198    static final int CMD_UNLOAD_DRIVER                    = BASE + 2;
199    /* Indicates driver load succeeded */
200    static final int CMD_LOAD_DRIVER_SUCCESS              = BASE + 3;
201    /* Indicates driver load failed */
202    static final int CMD_LOAD_DRIVER_FAILURE              = BASE + 4;
203    /* Indicates driver unload succeeded */
204    static final int CMD_UNLOAD_DRIVER_SUCCESS            = BASE + 5;
205    /* Indicates driver unload failed */
206    static final int CMD_UNLOAD_DRIVER_FAILURE            = BASE + 6;
207
208    /* Start the supplicant */
209    static final int CMD_START_SUPPLICANT                 = BASE + 11;
210    /* Stop the supplicant */
211    static final int CMD_STOP_SUPPLICANT                  = BASE + 12;
212    /* Start the driver */
213    static final int CMD_START_DRIVER                     = BASE + 13;
214    /* Start the driver */
215    static final int CMD_STOP_DRIVER                      = BASE + 14;
216    /* Indicates Static IP succeded */
217    static final int CMD_STATIC_IP_SUCCESS                = BASE + 15;
218    /* Indicates Static IP failed */
219    static final int CMD_STATIC_IP_FAILURE                = BASE + 16;
220    /* Indicates supplicant stop failed */
221    static final int CMD_STOP_SUPPLICANT_FAILED           = BASE + 17;
222
223    /* Start the soft access point */
224    static final int CMD_START_AP                         = BASE + 21;
225    /* Indicates soft ap start succeded */
226    static final int CMD_START_AP_SUCCESS                 = BASE + 22;
227    /* Indicates soft ap start failed */
228    static final int CMD_START_AP_FAILURE                 = BASE + 23;
229    /* Stop the soft access point */
230    static final int CMD_STOP_AP                          = BASE + 24;
231    /* Set the soft access point configuration */
232    static final int CMD_SET_AP_CONFIG                    = BASE + 25;
233    /* Get the soft access point configuration */
234    static final int CMD_GET_AP_CONFIG                    = BASE + 26;
235    /* Set configuration on tether interface */
236    static final int CMD_TETHER_INTERFACE                 = BASE + 27;
237
238    static final int CMD_BLUETOOTH_ADAPTER_STATE_CHANGE   = BASE + 28;
239
240    /* Supplicant commands */
241    /* Is supplicant alive ? */
242    static final int CMD_PING_SUPPLICANT                  = BASE + 51;
243    /* Add/update a network configuration */
244    static final int CMD_ADD_OR_UPDATE_NETWORK            = BASE + 52;
245    /* Delete a network */
246    static final int CMD_REMOVE_NETWORK                   = BASE + 53;
247    /* Enable a network. The device will attempt a connection to the given network. */
248    static final int CMD_ENABLE_NETWORK                   = BASE + 54;
249    /* Enable all networks */
250    static final int CMD_ENABLE_ALL_NETWORKS              = BASE + 55;
251    /* Disable a network. The device does not attempt a connection to the given network. */
252    static final int CMD_DISABLE_NETWORK                  = BASE + 56;
253    /* Blacklist network. De-prioritizes the given BSSID for connection. */
254    static final int CMD_BLACKLIST_NETWORK                = BASE + 57;
255    /* Clear the blacklist network list */
256    static final int CMD_CLEAR_BLACKLIST                  = BASE + 58;
257    /* Save configuration */
258    static final int CMD_SAVE_CONFIG                      = BASE + 59;
259
260    /* Supplicant commands after driver start*/
261    /* Initiate a scan */
262    static final int CMD_START_SCAN                       = BASE + 71;
263    /* Set scan mode. CONNECT_MODE or SCAN_ONLY_MODE */
264    static final int CMD_SET_SCAN_MODE                    = BASE + 72;
265    /* Set scan type. SCAN_ACTIVE or SCAN_PASSIVE */
266    static final int CMD_SET_SCAN_TYPE                    = BASE + 73;
267    /* Disconnect from a network */
268    static final int CMD_DISCONNECT                       = BASE + 74;
269    /* Reconnect to a network */
270    static final int CMD_RECONNECT                        = BASE + 75;
271    /* Reassociate to a network */
272    static final int CMD_REASSOCIATE                      = BASE + 76;
273    /* Controls power mode and suspend mode optimizations
274     *
275     * When high perf mode is enabled, power mode is set to
276     * POWER_MODE_ACTIVE and suspend mode optimizations are disabled
277     *
278     * When high perf mode is disabled, power mode is set to
279     * POWER_MODE_AUTO and suspend mode optimizations are enabled
280     *
281     * Suspend mode optimizations include:
282     * - packet filtering
283     * - turn off roaming
284     * - DTIM wake up settings
285     */
286    static final int CMD_SET_HIGH_PERF_MODE               = BASE + 77;
287    /* Set the country code */
288    static final int CMD_SET_COUNTRY_CODE                 = BASE + 80;
289    /* Request connectivity manager wake lock before driver stop */
290    static final int CMD_REQUEST_CM_WAKELOCK              = BASE + 81;
291    /* Enables RSSI poll */
292    static final int CMD_ENABLE_RSSI_POLL                 = BASE + 82;
293    /* RSSI poll */
294    static final int CMD_RSSI_POLL                        = BASE + 83;
295    /* Set up packet filtering */
296    static final int CMD_START_PACKET_FILTERING           = BASE + 84;
297    /* Clear packet filter */
298    static final int CMD_STOP_PACKET_FILTERING            = BASE + 85;
299
300    /* arg1 values to CMD_STOP_PACKET_FILTERING and CMD_START_PACKET_FILTERING */
301    static final int MULTICAST_V6  = 1;
302    static final int MULTICAST_V4  = 0;
303
304    /* Connect to a specified network (network id
305     * or WifiConfiguration) This involves increasing
306     * the priority of the network, enabling the network
307     * (while disabling others) and issuing a reconnect.
308     * Note that CMD_RECONNECT just does a reconnect to
309     * an existing network. All the networks get enabled
310     * upon a successful connection or a failure.
311     */
312    static final int CMD_CONNECT_NETWORK                  = BASE + 86;
313    /* Save the specified network. This involves adding
314     * an enabled network (if new) and updating the
315     * config and issuing a save on supplicant config.
316     */
317    static final int CMD_SAVE_NETWORK                     = BASE + 87;
318    /* Delete the specified network. This involves
319     * removing the network and issuing a save on
320     * supplicant config.
321     */
322    static final int CMD_FORGET_NETWORK                   = BASE + 88;
323    /* Start Wi-Fi protected setup */
324    static final int CMD_START_WPS                        = BASE + 89;
325    /* Set the frequency band */
326    static final int CMD_SET_FREQUENCY_BAND               = BASE + 90;
327    /* Enable background scan for configured networks */
328    static final int CMD_ENABLE_BACKGROUND_SCAN           = BASE + 91;
329
330    /* Commands from/to the SupplicantStateTracker */
331    /* Reset the supplicant state tracker */
332    static final int CMD_RESET_SUPPLICANT_STATE           = BASE + 111;
333
334    /* Commands/events reported by WpsStateMachine */
335    /* Indicates the completion of WPS activity */
336    static final int WPS_COMPLETED_EVENT                  = BASE + 121;
337    /* Reset the WPS state machine */
338    static final int CMD_RESET_WPS_STATE                  = BASE + 122;
339
340    /* Interaction with WifiP2pService */
341    public static final int WIFI_ENABLE_PENDING           = BASE + 131;
342    public static final int P2P_ENABLE_PROCEED            = BASE + 132;
343
344    private static final int CONNECT_MODE   = 1;
345    private static final int SCAN_ONLY_MODE = 2;
346
347    private static final int SCAN_ACTIVE = 1;
348    private static final int SCAN_PASSIVE = 2;
349
350    private static final int SUCCESS = 1;
351    private static final int FAILURE = -1;
352
353    /**
354     * The maximum number of times we will retry a connection to an access point
355     * for which we have failed in acquiring an IP address from DHCP. A value of
356     * N means that we will make N+1 connection attempts in all.
357     * <p>
358     * See {@link Settings.Secure#WIFI_MAX_DHCP_RETRY_COUNT}. This is the default
359     * value if a Settings value is not present.
360     */
361    private static final int DEFAULT_MAX_DHCP_RETRIES = 9;
362
363    static final int POWER_MODE_ACTIVE = 1;
364    static final int POWER_MODE_AUTO = 0;
365
366    /* Tracks the power mode for restoration after a DHCP request/renewal goes through */
367    private int mPowerMode = POWER_MODE_AUTO;
368
369    /**
370     * Default framework scan interval in milliseconds. This is used in the scenario in which
371     * wifi chipset does not support background scanning to set up a
372     * periodic wake up scan so that the device can connect to a new access
373     * point on the move. {@link Settings.Secure#WIFI_FRAMEWORK_SCAN_INTERVAL_MS} can
374     * override this.
375     */
376    private final int mDefaultFrameworkScanIntervalMs;
377
378    /**
379     * Default supplicant scan interval in milliseconds.
380     * {@link Settings.Secure#WIFI_SUPPLICANT_SCAN_INTERVAL_MS} can override this.
381     */
382    private final int mDefaultSupplicantScanIntervalMs;
383
384    /**
385     * Minimum time interval between enabling all networks.
386     * A device can end up repeatedly connecting to a bad network on screen on/off toggle
387     * due to enabling every time. We add a threshold to avoid this.
388     */
389    private static final int MIN_INTERVAL_ENABLE_ALL_NETWORKS_MS = 10 * 60 * 1000; /* 10 minutes */
390    private long mLastEnableAllNetworksTime;
391
392
393    private static final int MIN_RSSI = -200;
394    private static final int MAX_RSSI = 256;
395
396    /* Default parent state */
397    private State mDefaultState = new DefaultState();
398    /* Temporary initial state */
399    private State mInitialState = new InitialState();
400    /* Unloading the driver */
401    private State mDriverUnloadingState = new DriverUnloadingState();
402    /* Loading the driver */
403    private State mDriverUnloadedState = new DriverUnloadedState();
404    /* Driver load/unload failed */
405    private State mDriverFailedState = new DriverFailedState();
406    /* Driver loading */
407    private State mDriverLoadingState = new DriverLoadingState();
408    /* Driver loaded */
409    private State mDriverLoadedState = new DriverLoadedState();
410    /* Driver loaded, waiting for supplicant to start */
411    private State mSupplicantStartingState = new SupplicantStartingState();
412    /* Driver loaded and supplicant ready */
413    private State mSupplicantStartedState = new SupplicantStartedState();
414    /* Waiting for supplicant to stop and monitor to exit */
415    private State mSupplicantStoppingState = new SupplicantStoppingState();
416    /* Driver start issued, waiting for completed event */
417    private State mDriverStartingState = new DriverStartingState();
418    /* Driver started */
419    private State mDriverStartedState = new DriverStartedState();
420    /* Driver stopping */
421    private State mDriverStoppingState = new DriverStoppingState();
422    /* Driver stopped */
423    private State mDriverStoppedState = new DriverStoppedState();
424    /* Scan for networks, no connection will be established */
425    private State mScanModeState = new ScanModeState();
426    /* Connecting to an access point */
427    private State mConnectModeState = new ConnectModeState();
428    /* Fetching IP after network connection (assoc+auth complete) */
429    private State mConnectingState = new ConnectingState();
430    /* Connected with IP addr */
431    private State mConnectedState = new ConnectedState();
432    /* disconnect issued, waiting for network disconnect confirmation */
433    private State mDisconnectingState = new DisconnectingState();
434    /* Network is not connected, supplicant assoc+auth is not complete */
435    private State mDisconnectedState = new DisconnectedState();
436    /* Waiting for WPS to be completed*/
437    private State mWaitForWpsCompletionState = new WaitForWpsCompletionState();
438
439    /* Soft ap is starting up */
440    private State mSoftApStartingState = new SoftApStartingState();
441    /* Soft ap is running */
442    private State mSoftApStartedState = new SoftApStartedState();
443    /* Soft ap is running and we are tethered through connectivity service */
444    private State mTetheredState = new TetheredState();
445
446    /* Wait till p2p is disabled */
447    private State mWaitForP2pDisableState = new WaitForP2pDisableState();
448
449
450    /**
451     * One of  {@link WifiManager#WIFI_STATE_DISABLED},
452     *         {@link WifiManager#WIFI_STATE_DISABLING},
453     *         {@link WifiManager#WIFI_STATE_ENABLED},
454     *         {@link WifiManager#WIFI_STATE_ENABLING},
455     *         {@link WifiManager#WIFI_STATE_UNKNOWN}
456     *
457     */
458    private final AtomicInteger mWifiState = new AtomicInteger(WIFI_STATE_DISABLED);
459
460    /**
461     * One of  {@link WifiManager#WIFI_AP_STATE_DISABLED},
462     *         {@link WifiManager#WIFI_AP_STATE_DISABLING},
463     *         {@link WifiManager#WIFI_AP_STATE_ENABLED},
464     *         {@link WifiManager#WIFI_AP_STATE_ENABLING},
465     *         {@link WifiManager#WIFI_AP_STATE_FAILED}
466     *
467     */
468    private final AtomicInteger mWifiApState = new AtomicInteger(WIFI_AP_STATE_DISABLED);
469
470    private final AtomicInteger mLastEnableUid = new AtomicInteger(Process.myUid());
471    private final AtomicInteger mLastApEnableUid = new AtomicInteger(Process.myUid());
472
473    private static final int SCAN_REQUEST = 0;
474    private static final String ACTION_START_SCAN =
475        "com.android.server.WifiManager.action.START_SCAN";
476
477    /**
478     * Keep track of whether WIFI is running.
479     */
480    private boolean mIsRunning = false;
481
482    /**
483     * Keep track of whether we last told the battery stats we had started.
484     */
485    private boolean mReportedRunning = false;
486
487    /**
488     * Most recently set source of starting WIFI.
489     */
490    private final WorkSource mRunningWifiUids = new WorkSource();
491
492    /**
493     * The last reported UIDs that were responsible for starting WIFI.
494     */
495    private final WorkSource mLastRunningWifiUids = new WorkSource();
496
497    private final IBatteryStats mBatteryStats;
498    private boolean mNextWifiActionExplicit = false;
499    private int mLastExplicitNetworkId;
500    private long mLastNetworkChoiceTime;
501    private static final long EXPLICIT_CONNECT_ALLOWED_DELAY_MS = 2 * 60 * 1000;
502
503
504    public WifiStateMachine(Context context, String wlanInterface) {
505        super(TAG);
506
507        mContext = context;
508        mInterfaceName = wlanInterface;
509
510        mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, NETWORKTYPE, "");
511        mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo"));
512
513        IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
514        mNwService = INetworkManagementService.Stub.asInterface(b);
515
516        mWifiMonitor = new WifiMonitor(this);
517        mDhcpInfoInternal = new DhcpInfoInternal();
518        mWifiInfo = new WifiInfo();
519        mSupplicantStateTracker = new SupplicantStateTracker(context, this, getHandler());
520        mWpsStateMachine = new WpsStateMachine(context, this, getHandler());
521        mLinkProperties = new LinkProperties();
522
523        mNetworkInfo.setIsAvailable(false);
524        mLinkProperties.clear();
525        mLastBssid = null;
526        mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
527        mLastSignalLevel = -1;
528
529        mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
530        Intent scanIntent = new Intent(ACTION_START_SCAN, null);
531        mScanIntent = PendingIntent.getBroadcast(mContext, SCAN_REQUEST, scanIntent, 0);
532
533        mDefaultFrameworkScanIntervalMs = mContext.getResources().getInteger(
534                com.android.internal.R.integer.config_wifi_framework_scan_interval);
535
536        mDefaultSupplicantScanIntervalMs = mContext.getResources().getInteger(
537                com.android.internal.R.integer.config_wifi_supplicant_scan_interval);
538
539        mContext.registerReceiver(
540            new BroadcastReceiver() {
541                @Override
542                public void onReceive(Context context, Intent intent) {
543                    ArrayList<String> available = intent.getStringArrayListExtra(
544                            ConnectivityManager.EXTRA_AVAILABLE_TETHER);
545                    sendMessage(CMD_TETHER_INTERFACE, available);
546                }
547            },new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED));
548
549        mContext.registerReceiver(
550                new BroadcastReceiver() {
551                    @Override
552                    public void onReceive(Context context, Intent intent) {
553                        startScan(false);
554                    }
555                },
556                new IntentFilter(ACTION_START_SCAN));
557
558        mScanResultCache = new LruCache<String, ScanResult>(SCAN_RESULT_CACHE_SIZE);
559
560        PowerManager powerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
561        mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
562
563        addState(mDefaultState);
564            addState(mInitialState, mDefaultState);
565            addState(mDriverUnloadingState, mDefaultState);
566            addState(mDriverUnloadedState, mDefaultState);
567                addState(mDriverFailedState, mDriverUnloadedState);
568            addState(mDriverLoadingState, mDefaultState);
569            addState(mDriverLoadedState, mDefaultState);
570            addState(mSupplicantStartingState, mDefaultState);
571            addState(mSupplicantStartedState, mDefaultState);
572                addState(mDriverStartingState, mSupplicantStartedState);
573                addState(mDriverStartedState, mSupplicantStartedState);
574                    addState(mScanModeState, mDriverStartedState);
575                    addState(mConnectModeState, mDriverStartedState);
576                        addState(mConnectingState, mConnectModeState);
577                        addState(mConnectedState, mConnectModeState);
578                        addState(mDisconnectingState, mConnectModeState);
579                        addState(mDisconnectedState, mConnectModeState);
580                        addState(mWaitForWpsCompletionState, mConnectModeState);
581                addState(mDriverStoppingState, mSupplicantStartedState);
582                addState(mDriverStoppedState, mSupplicantStartedState);
583            addState(mSupplicantStoppingState, mDefaultState);
584            addState(mSoftApStartingState, mDefaultState);
585            addState(mSoftApStartedState, mDefaultState);
586                addState(mTetheredState, mSoftApStartedState);
587            addState(mWaitForP2pDisableState, mDefaultState);
588
589        setInitialState(mInitialState);
590
591        if (DBG) setDbg(true);
592
593        //start the state machine
594        start();
595    }
596
597    /*********************************************************
598     * Methods exposed for public use
599     ********************************************************/
600
601    /**
602     * TODO: doc
603     */
604    public boolean syncPingSupplicant(AsyncChannel channel) {
605        Message resultMsg = channel.sendMessageSynchronously(CMD_PING_SUPPLICANT);
606        boolean result = (resultMsg.arg1 != FAILURE);
607        resultMsg.recycle();
608        return result;
609    }
610
611    /**
612     * TODO: doc
613     */
614    public void startScan(boolean forceActive) {
615        sendMessage(obtainMessage(CMD_START_SCAN, forceActive ?
616                SCAN_ACTIVE : SCAN_PASSIVE, 0));
617    }
618
619    /**
620     * TODO: doc
621     */
622    public void setWifiEnabled(boolean enable) {
623        mLastEnableUid.set(Binder.getCallingUid());
624        if (enable) {
625            /* Argument is the state that is entered prior to load */
626            sendMessage(obtainMessage(CMD_LOAD_DRIVER, WIFI_STATE_ENABLING, 0));
627            sendMessage(CMD_START_SUPPLICANT);
628        } else {
629            sendMessage(CMD_STOP_SUPPLICANT);
630            /* Argument is the state that is entered upon success */
631            sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_STATE_DISABLED, 0));
632        }
633    }
634
635    /**
636     * TODO: doc
637     */
638    public void setWifiApEnabled(WifiConfiguration wifiConfig, boolean enable) {
639        mLastApEnableUid.set(Binder.getCallingUid());
640        if (enable) {
641            /* Argument is the state that is entered prior to load */
642            sendMessage(obtainMessage(CMD_LOAD_DRIVER, WIFI_AP_STATE_ENABLING, 0));
643            sendMessage(obtainMessage(CMD_START_AP, wifiConfig));
644        } else {
645            sendMessage(CMD_STOP_AP);
646            /* Argument is the state that is entered upon success */
647            sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_AP_STATE_DISABLED, 0));
648        }
649    }
650
651    public void setWifiApConfiguration(WifiConfiguration config) {
652        sendMessage(obtainMessage(CMD_SET_AP_CONFIG, config));
653    }
654
655    public WifiConfiguration syncGetWifiApConfiguration(AsyncChannel channel) {
656        Message resultMsg = channel.sendMessageSynchronously(CMD_GET_AP_CONFIG);
657        WifiConfiguration ret = (WifiConfiguration) resultMsg.obj;
658        resultMsg.recycle();
659        return ret;
660    }
661
662    /**
663     * TODO: doc
664     */
665    public int syncGetWifiState() {
666        return mWifiState.get();
667    }
668
669    /**
670     * TODO: doc
671     */
672    public String syncGetWifiStateByName() {
673        switch (mWifiState.get()) {
674            case WIFI_STATE_DISABLING:
675                return "disabling";
676            case WIFI_STATE_DISABLED:
677                return "disabled";
678            case WIFI_STATE_ENABLING:
679                return "enabling";
680            case WIFI_STATE_ENABLED:
681                return "enabled";
682            case WIFI_STATE_UNKNOWN:
683                return "unknown state";
684            default:
685                return "[invalid state]";
686        }
687    }
688
689    /**
690     * TODO: doc
691     */
692    public int syncGetWifiApState() {
693        return mWifiApState.get();
694    }
695
696    /**
697     * TODO: doc
698     */
699    public String syncGetWifiApStateByName() {
700        switch (mWifiApState.get()) {
701            case WIFI_AP_STATE_DISABLING:
702                return "disabling";
703            case WIFI_AP_STATE_DISABLED:
704                return "disabled";
705            case WIFI_AP_STATE_ENABLING:
706                return "enabling";
707            case WIFI_AP_STATE_ENABLED:
708                return "enabled";
709            case WIFI_AP_STATE_FAILED:
710                return "failed";
711            default:
712                return "[invalid state]";
713        }
714    }
715
716    /**
717     * Get status information for the current connection, if any.
718     * @return a {@link WifiInfo} object containing information about the current connection
719     *
720     */
721    public WifiInfo syncRequestConnectionInfo() {
722        return mWifiInfo;
723    }
724
725    public DhcpInfo syncGetDhcpInfo() {
726        synchronized (mDhcpInfoInternal) {
727            return mDhcpInfoInternal.makeDhcpInfo();
728        }
729    }
730
731    /**
732     * TODO: doc
733     */
734    public void setDriverStart(boolean enable) {
735        if (enable) {
736            sendMessage(CMD_START_DRIVER);
737        } else {
738            sendMessage(CMD_STOP_DRIVER);
739        }
740    }
741
742    /**
743     * TODO: doc
744     */
745    public void setScanOnlyMode(boolean enable) {
746      if (enable) {
747          sendMessage(obtainMessage(CMD_SET_SCAN_MODE, SCAN_ONLY_MODE, 0));
748      } else {
749          sendMessage(obtainMessage(CMD_SET_SCAN_MODE, CONNECT_MODE, 0));
750      }
751    }
752
753    /**
754     * TODO: doc
755     */
756    public void setScanType(boolean active) {
757      if (active) {
758          sendMessage(obtainMessage(CMD_SET_SCAN_TYPE, SCAN_ACTIVE, 0));
759      } else {
760          sendMessage(obtainMessage(CMD_SET_SCAN_TYPE, SCAN_PASSIVE, 0));
761      }
762    }
763
764    /**
765     * TODO: doc
766     */
767    public List<ScanResult> syncGetScanResultsList() {
768        return mScanResults;
769    }
770
771    /**
772     * Disconnect from Access Point
773     */
774    public void disconnectCommand() {
775        sendMessage(CMD_DISCONNECT);
776    }
777
778    /**
779     * Initiate a reconnection to AP
780     */
781    public void reconnectCommand() {
782        sendMessage(CMD_RECONNECT);
783    }
784
785    /**
786     * Initiate a re-association to AP
787     */
788    public void reassociateCommand() {
789        sendMessage(CMD_REASSOCIATE);
790    }
791
792    /**
793     * Add a network synchronously
794     *
795     * @return network id of the new network
796     */
797    public int syncAddOrUpdateNetwork(AsyncChannel channel, WifiConfiguration config) {
798        Message resultMsg = channel.sendMessageSynchronously(CMD_ADD_OR_UPDATE_NETWORK, config);
799        int result = resultMsg.arg1;
800        resultMsg.recycle();
801        return result;
802    }
803
804    public List<WifiConfiguration> syncGetConfiguredNetworks() {
805        return WifiConfigStore.getConfiguredNetworks();
806    }
807
808    /**
809     * Delete a network
810     *
811     * @param networkId id of the network to be removed
812     */
813    public boolean syncRemoveNetwork(AsyncChannel channel, int networkId) {
814        Message resultMsg = channel.sendMessageSynchronously(CMD_REMOVE_NETWORK, networkId);
815        boolean result = (resultMsg.arg1 != FAILURE);
816        resultMsg.recycle();
817        return result;
818    }
819
820    /**
821     * Enable a network
822     *
823     * @param netId network id of the network
824     * @param disableOthers true, if all other networks have to be disabled
825     * @return {@code true} if the operation succeeds, {@code false} otherwise
826     */
827    public boolean syncEnableNetwork(AsyncChannel channel, int netId, boolean disableOthers) {
828        Message resultMsg = channel.sendMessageSynchronously(CMD_ENABLE_NETWORK, netId,
829                disableOthers ? 1 : 0);
830        boolean result = (resultMsg.arg1 != FAILURE);
831        resultMsg.recycle();
832        return result;
833    }
834
835    /**
836     * Disable a network
837     *
838     * @param netId network id of the network
839     * @return {@code true} if the operation succeeds, {@code false} otherwise
840     */
841    public boolean syncDisableNetwork(AsyncChannel channel, int netId) {
842        Message resultMsg = channel.sendMessageSynchronously(CMD_DISABLE_NETWORK, netId,
843                WifiConfiguration.DISABLED_UNKNOWN_REASON);
844        boolean result = (resultMsg.arg1 != FAILURE);
845        resultMsg.recycle();
846        return result;
847    }
848
849    /**
850     * Blacklist a BSSID. This will avoid the AP if there are
851     * alternate APs to connect
852     *
853     * @param bssid BSSID of the network
854     */
855    public void addToBlacklist(String bssid) {
856        sendMessage(obtainMessage(CMD_BLACKLIST_NETWORK, bssid));
857    }
858
859    /**
860     * Clear the blacklist list
861     *
862     */
863    public void clearBlacklist() {
864        sendMessage(obtainMessage(CMD_CLEAR_BLACKLIST));
865    }
866
867    public void connectNetwork(int netId) {
868        sendMessage(obtainMessage(CMD_CONNECT_NETWORK, netId, 0));
869    }
870
871    public void connectNetwork(WifiConfiguration wifiConfig) {
872        /* arg1 is used to indicate netId, force a netId value of
873         * WifiConfiguration.INVALID_NETWORK_ID when we are passing
874         * a configuration since the default value of 0 is a valid netId
875         */
876        sendMessage(obtainMessage(CMD_CONNECT_NETWORK, WifiConfiguration.INVALID_NETWORK_ID,
877                0, wifiConfig));
878    }
879
880    public void saveNetwork(WifiConfiguration wifiConfig) {
881        sendMessage(obtainMessage(CMD_SAVE_NETWORK, wifiConfig));
882    }
883
884    public void forgetNetwork(int netId) {
885        sendMessage(obtainMessage(CMD_FORGET_NETWORK, netId, 0));
886    }
887
888    public void disableNetwork(Messenger replyTo, int netId, int reason) {
889        Message message = obtainMessage(CMD_DISABLE_NETWORK, netId, reason);
890        message.replyTo = replyTo;
891        sendMessage(message);
892    }
893
894    public void startWps(Messenger replyTo, WpsInfo config) {
895        Message msg = obtainMessage(CMD_START_WPS, config);
896        msg.replyTo = replyTo;
897        sendMessage(msg);
898    }
899
900    public void enableRssiPolling(boolean enabled) {
901       sendMessage(obtainMessage(CMD_ENABLE_RSSI_POLL, enabled ? 1 : 0, 0));
902    }
903
904    public void enableBackgroundScanCommand(boolean enabled) {
905       sendMessage(obtainMessage(CMD_ENABLE_BACKGROUND_SCAN, enabled ? 1 : 0, 0));
906    }
907
908    public void enableAllNetworks() {
909        sendMessage(CMD_ENABLE_ALL_NETWORKS);
910    }
911
912    /**
913     * Start filtering Multicast v4 packets
914     */
915    public void startFilteringMulticastV4Packets() {
916        mFilteringMulticastV4Packets.set(true);
917        sendMessage(obtainMessage(CMD_START_PACKET_FILTERING, MULTICAST_V4, 0));
918    }
919
920    /**
921     * Stop filtering Multicast v4 packets
922     */
923    public void stopFilteringMulticastV4Packets() {
924        mFilteringMulticastV4Packets.set(false);
925        sendMessage(obtainMessage(CMD_STOP_PACKET_FILTERING, MULTICAST_V4, 0));
926    }
927
928    /**
929     * Start filtering Multicast v4 packets
930     */
931    public void startFilteringMulticastV6Packets() {
932        sendMessage(obtainMessage(CMD_START_PACKET_FILTERING, MULTICAST_V6, 0));
933    }
934
935    /**
936     * Stop filtering Multicast v4 packets
937     */
938    public void stopFilteringMulticastV6Packets() {
939        sendMessage(obtainMessage(CMD_STOP_PACKET_FILTERING, MULTICAST_V6, 0));
940    }
941
942    /**
943     * Set high performance mode of operation.
944     * Enabling would set active power mode and disable suspend optimizations;
945     * disabling would set auto power mode and enable suspend optimizations
946     * @param enable true if enable, false otherwise
947     */
948    public void setHighPerfModeEnabled(boolean enable) {
949        sendMessage(obtainMessage(CMD_SET_HIGH_PERF_MODE, enable ? 1 : 0, 0));
950    }
951
952    /**
953     * Set the country code
954     * @param countryCode following ISO 3166 format
955     * @param persist {@code true} if the setting should be remembered.
956     */
957    public void setCountryCode(String countryCode, boolean persist) {
958        if (persist) {
959            Settings.Secure.putString(mContext.getContentResolver(),
960                    Settings.Secure.WIFI_COUNTRY_CODE,
961                    countryCode);
962        }
963        sendMessage(obtainMessage(CMD_SET_COUNTRY_CODE, countryCode));
964    }
965
966    /**
967     * Set the operational frequency band
968     * @param band
969     * @param persist {@code true} if the setting should be remembered.
970     */
971    public void setFrequencyBand(int band, boolean persist) {
972        if (persist) {
973            Settings.Secure.putInt(mContext.getContentResolver(),
974                    Settings.Secure.WIFI_FREQUENCY_BAND,
975                    band);
976        }
977        sendMessage(obtainMessage(CMD_SET_FREQUENCY_BAND, band, 0));
978    }
979
980    /**
981     * Returns the operational frequency band
982     */
983    public int getFrequencyBand() {
984        return mFrequencyBand.get();
985    }
986
987    /**
988     * Returns the wifi configuration file
989     */
990    public String getConfigFile() {
991        return WifiConfigStore.getConfigFile();
992    }
993
994    /**
995     * Send a message indicating bluetooth adapter connection state changed
996     */
997    public void sendBluetoothAdapterStateChange(int state) {
998        sendMessage(obtainMessage(CMD_BLUETOOTH_ADAPTER_STATE_CHANGE, state, 0));
999    }
1000
1001    /**
1002     * Save configuration on supplicant
1003     *
1004     * @return {@code true} if the operation succeeds, {@code false} otherwise
1005     *
1006     * TODO: deprecate this
1007     */
1008    public boolean syncSaveConfig(AsyncChannel channel) {
1009        Message resultMsg = channel.sendMessageSynchronously(CMD_SAVE_CONFIG);
1010        boolean result = (resultMsg.arg1 != FAILURE);
1011        resultMsg.recycle();
1012        return result;
1013    }
1014
1015    /**
1016     * Request a wakelock with connectivity service to
1017     * keep the device awake until we hand-off from wifi
1018     * to an alternate network
1019     */
1020    public void requestCmWakeLock() {
1021        sendMessage(CMD_REQUEST_CM_WAKELOCK);
1022    }
1023
1024    public void updateBatteryWorkSource(WorkSource newSource) {
1025        synchronized (mRunningWifiUids) {
1026            try {
1027                if (newSource != null) {
1028                    mRunningWifiUids.set(newSource);
1029                }
1030                if (mIsRunning) {
1031                    if (mReportedRunning) {
1032                        // If the work source has changed since last time, need
1033                        // to remove old work from battery stats.
1034                        if (mLastRunningWifiUids.diff(mRunningWifiUids)) {
1035                            mBatteryStats.noteWifiRunningChanged(mLastRunningWifiUids,
1036                                    mRunningWifiUids);
1037                            mLastRunningWifiUids.set(mRunningWifiUids);
1038                        }
1039                    } else {
1040                        // Now being started, report it.
1041                        mBatteryStats.noteWifiRunning(mRunningWifiUids);
1042                        mLastRunningWifiUids.set(mRunningWifiUids);
1043                        mReportedRunning = true;
1044                    }
1045                } else {
1046                    if (mReportedRunning) {
1047                        // Last reported we were running, time to stop.
1048                        mBatteryStats.noteWifiStopped(mLastRunningWifiUids);
1049                        mLastRunningWifiUids.clear();
1050                        mReportedRunning = false;
1051                    }
1052                }
1053                mWakeLock.setWorkSource(newSource);
1054            } catch (RemoteException ignore) {
1055            }
1056        }
1057    }
1058
1059    @Override
1060    public String toString() {
1061        StringBuffer sb = new StringBuffer();
1062        String LS = System.getProperty("line.separator");
1063        sb.append("current HSM state: ").append(getCurrentState().getName()).append(LS);
1064        sb.append("mLinkProperties ").append(mLinkProperties).append(LS);
1065        sb.append("mWifiInfo ").append(mWifiInfo).append(LS);
1066        sb.append("mDhcpInfoInternal ").append(mDhcpInfoInternal).append(LS);
1067        sb.append("mNetworkInfo ").append(mNetworkInfo).append(LS);
1068        sb.append("mLastSignalLevel ").append(mLastSignalLevel).append(LS);
1069        sb.append("mLastBssid ").append(mLastBssid).append(LS);
1070        sb.append("mLastNetworkId ").append(mLastNetworkId).append(LS);
1071        sb.append("mReconnectCount ").append(mReconnectCount).append(LS);
1072        sb.append("mIsScanMode ").append(mIsScanMode).append(LS);
1073        sb.append("Supplicant status").append(LS)
1074                .append(WifiNative.statusCommand()).append(LS).append(LS);
1075
1076        sb.append(WifiConfigStore.dump());
1077        return sb.toString();
1078    }
1079
1080    /*********************************************************
1081     * Internal private functions
1082     ********************************************************/
1083
1084    private void checkAndSetConnectivityInstance() {
1085        if (mCm == null) {
1086            mCm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
1087        }
1088    }
1089
1090    private boolean startTethering(ArrayList<String> available) {
1091
1092        boolean wifiAvailable = false;
1093
1094        checkAndSetConnectivityInstance();
1095
1096        String[] wifiRegexs = mCm.getTetherableWifiRegexs();
1097
1098        for (String intf : available) {
1099            for (String regex : wifiRegexs) {
1100                if (intf.matches(regex)) {
1101
1102                    InterfaceConfiguration ifcg = null;
1103                    try {
1104                        ifcg = mNwService.getInterfaceConfig(intf);
1105                        if (ifcg != null) {
1106                            /* IP/netmask: 192.168.43.1/255.255.255.0 */
1107                            ifcg.addr = new LinkAddress(NetworkUtils.numericToInetAddress(
1108                                    "192.168.43.1"), 24);
1109                            ifcg.interfaceFlags = "[up]";
1110
1111                            mNwService.setInterfaceConfig(intf, ifcg);
1112                        }
1113                    } catch (Exception e) {
1114                        loge("Error configuring interface " + intf + ", :" + e);
1115                        return false;
1116                    }
1117
1118                    if(mCm.tether(intf) != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
1119                        loge("Error tethering on " + intf);
1120                        return false;
1121                    }
1122                    return true;
1123                }
1124            }
1125        }
1126        // We found no interfaces to tether
1127        return false;
1128    }
1129
1130    private void stopTethering() {
1131
1132        checkAndSetConnectivityInstance();
1133
1134        /* Clear the interface config to allow dhcp correctly configure new
1135           ip settings */
1136        InterfaceConfiguration ifcg = null;
1137        try {
1138            ifcg = mNwService.getInterfaceConfig(mInterfaceName);
1139            if (ifcg != null) {
1140                ifcg.addr = new LinkAddress(NetworkUtils.numericToInetAddress(
1141                            "0.0.0.0"), 0);
1142                mNwService.setInterfaceConfig(mInterfaceName, ifcg);
1143            }
1144        } catch (Exception e) {
1145            loge("Error resetting interface " + mInterfaceName + ", :" + e);
1146        }
1147
1148        if (mCm.untether(mInterfaceName) != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
1149            loge("Untether initiate failed!");
1150        }
1151    }
1152
1153    /**
1154     * Set the country code from the system setting value, if any.
1155     */
1156    private void setCountryCode() {
1157        String countryCode = Settings.Secure.getString(mContext.getContentResolver(),
1158                Settings.Secure.WIFI_COUNTRY_CODE);
1159        if (countryCode != null && !countryCode.isEmpty()) {
1160            setCountryCode(countryCode, false);
1161        } else {
1162            //use driver default
1163        }
1164    }
1165
1166    /**
1167     * Set the frequency band from the system setting value, if any.
1168     */
1169    private void setFrequencyBand() {
1170        int band = Settings.Secure.getInt(mContext.getContentResolver(),
1171                Settings.Secure.WIFI_FREQUENCY_BAND, WifiManager.WIFI_FREQUENCY_BAND_AUTO);
1172        setFrequencyBand(band, false);
1173    }
1174
1175    private void setWifiState(int wifiState) {
1176        final int previousWifiState = mWifiState.get();
1177
1178        try {
1179            if (wifiState == WIFI_STATE_ENABLED) {
1180                mBatteryStats.noteWifiOn();
1181            } else if (wifiState == WIFI_STATE_DISABLED) {
1182                mBatteryStats.noteWifiOff();
1183            }
1184        } catch (RemoteException e) {
1185            loge("Failed to note battery stats in wifi");
1186        }
1187
1188        mWifiState.set(wifiState);
1189
1190        if (DBG) log("setWifiState: " + syncGetWifiStateByName());
1191
1192        final Intent intent = new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION);
1193        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1194        intent.putExtra(WifiManager.EXTRA_WIFI_STATE, wifiState);
1195        intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_STATE, previousWifiState);
1196        mContext.sendStickyBroadcast(intent);
1197    }
1198
1199    private void setWifiApState(int wifiApState) {
1200        final int previousWifiApState = mWifiApState.get();
1201
1202        try {
1203            if (wifiApState == WIFI_AP_STATE_ENABLED) {
1204                mBatteryStats.noteWifiOn();
1205            } else if (wifiApState == WIFI_AP_STATE_DISABLED) {
1206                mBatteryStats.noteWifiOff();
1207            }
1208        } catch (RemoteException e) {
1209            loge("Failed to note battery stats in wifi");
1210        }
1211
1212        // Update state
1213        mWifiApState.set(wifiApState);
1214
1215        if (DBG) log("setWifiApState: " + syncGetWifiApStateByName());
1216
1217        final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
1218        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1219        intent.putExtra(WifiManager.EXTRA_WIFI_AP_STATE, wifiApState);
1220        intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_AP_STATE, previousWifiApState);
1221        mContext.sendStickyBroadcast(intent);
1222    }
1223
1224    /**
1225     * Parse the scan result line passed to us by wpa_supplicant (helper).
1226     * @param line the line to parse
1227     * @return the {@link ScanResult} object
1228     */
1229    private ScanResult parseScanResult(String line) {
1230        ScanResult scanResult = null;
1231        if (line != null) {
1232            /*
1233             * Cache implementation (LinkedHashMap) is not synchronized, thus,
1234             * must synchronized here!
1235             */
1236            synchronized (mScanResultCache) {
1237                String[] result = scanResultPattern.split(line);
1238                if (3 <= result.length && result.length <= 5) {
1239                    String bssid = result[0];
1240                    // bssid | frequency | level | flags | ssid
1241                    int frequency;
1242                    int level;
1243                    try {
1244                        frequency = Integer.parseInt(result[1]);
1245                        level = Integer.parseInt(result[2]);
1246                        /* some implementations avoid negative values by adding 256
1247                         * so we need to adjust for that here.
1248                         */
1249                        if (level > 0) level -= 256;
1250                    } catch (NumberFormatException e) {
1251                        frequency = 0;
1252                        level = 0;
1253                    }
1254
1255                    /*
1256                     * The formatting of the results returned by
1257                     * wpa_supplicant is intended to make the fields
1258                     * line up nicely when printed,
1259                     * not to make them easy to parse. So we have to
1260                     * apply some heuristics to figure out which field
1261                     * is the SSID and which field is the flags.
1262                     */
1263                    String ssid;
1264                    String flags;
1265                    if (result.length == 4) {
1266                        if (result[3].charAt(0) == '[') {
1267                            flags = result[3];
1268                            ssid = "";
1269                        } else {
1270                            flags = "";
1271                            ssid = result[3];
1272                        }
1273                    } else if (result.length == 5) {
1274                        flags = result[3];
1275                        ssid = result[4];
1276                    } else {
1277                        // Here, we must have 3 fields: no flags and ssid
1278                        // set
1279                        flags = "";
1280                        ssid = "";
1281                    }
1282
1283                    // bssid + ssid is the hash key
1284                    String key = bssid + ssid;
1285                    scanResult = mScanResultCache.get(key);
1286                    if (scanResult != null) {
1287                        scanResult.level = level;
1288                        scanResult.SSID = ssid;
1289                        scanResult.capabilities = flags;
1290                        scanResult.frequency = frequency;
1291                    } else {
1292                        // Do not add scan results that have no SSID set
1293                        if (0 < ssid.trim().length()) {
1294                            scanResult =
1295                                new ScanResult(
1296                                    ssid, bssid, flags, level, frequency);
1297                            mScanResultCache.put(key, scanResult);
1298                        }
1299                    }
1300                } else {
1301                    loge("Misformatted scan result text with " +
1302                          result.length + " fields: " + line);
1303                }
1304            }
1305        }
1306
1307        return scanResult;
1308    }
1309
1310    /**
1311     * scanResults input format
1312     * 00:bb:cc:dd:cc:ee       2427    166     [WPA-EAP-TKIP][WPA2-EAP-CCMP]   Net1
1313     * 00:bb:cc:dd:cc:ff       2412    165     [WPA-EAP-TKIP][WPA2-EAP-CCMP]   Net2
1314     */
1315    private void setScanResults(String scanResults) {
1316        if (scanResults == null) {
1317            return;
1318        }
1319
1320        List<ScanResult> scanList = new ArrayList<ScanResult>();
1321
1322        int lineCount = 0;
1323
1324        int scanResultsLen = scanResults.length();
1325        // Parse the result string, keeping in mind that the last line does
1326        // not end with a newline.
1327        for (int lineBeg = 0, lineEnd = 0; lineEnd <= scanResultsLen; ++lineEnd) {
1328            if (lineEnd == scanResultsLen || scanResults.charAt(lineEnd) == '\n') {
1329                ++lineCount;
1330
1331                if (lineCount == 1) {
1332                    lineBeg = lineEnd + 1;
1333                    continue;
1334                }
1335                if (lineEnd > lineBeg) {
1336                    String line = scanResults.substring(lineBeg, lineEnd);
1337                    ScanResult scanResult = parseScanResult(line);
1338                    if (scanResult != null) {
1339                        scanList.add(scanResult);
1340                    } else {
1341                        //TODO: hidden network handling
1342                    }
1343                }
1344                lineBeg = lineEnd + 1;
1345            }
1346        }
1347
1348        mScanResults = scanList;
1349    }
1350
1351    private String fetchSSID() {
1352        String status = WifiNative.statusCommand();
1353        if (status == null) {
1354            return null;
1355        }
1356        // extract ssid from a series of "name=value"
1357        String[] lines = status.split("\n");
1358        for (String line : lines) {
1359            String[] prop = line.split(" *= *");
1360            if (prop.length < 2) continue;
1361            String name = prop[0];
1362            String value = prop[1];
1363            if (name.equalsIgnoreCase("ssid")) return value;
1364        }
1365        return null;
1366    }
1367
1368    /*
1369     * Fetch RSSI and linkspeed on current connection
1370     */
1371    private void fetchRssiAndLinkSpeedNative() {
1372        int newRssi = -1;
1373        int newLinkSpeed = -1;
1374
1375        String signalPoll = WifiNative.signalPoll();
1376
1377        if (signalPoll != null) {
1378            String[] lines = signalPoll.split("\n");
1379            for (String line : lines) {
1380                String[] prop = line.split("=");
1381                if (prop.length < 2) continue;
1382                try {
1383                    if (prop[0].equals("RSSI")) {
1384                        newRssi = Integer.parseInt(prop[1]);
1385                    } else if (prop[0].equals("LINKSPEED")) {
1386                        newLinkSpeed = Integer.parseInt(prop[1]);
1387                    }
1388                } catch (NumberFormatException e) {
1389                    //Ignore, defaults on rssi and linkspeed are assigned
1390                }
1391            }
1392        }
1393
1394        if (newRssi != -1 && MIN_RSSI < newRssi && newRssi < MAX_RSSI) { // screen out invalid values
1395            /* some implementations avoid negative values by adding 256
1396             * so we need to adjust for that here.
1397             */
1398            if (newRssi > 0) newRssi -= 256;
1399            mWifiInfo.setRssi(newRssi);
1400            /*
1401             * Rather then sending the raw RSSI out every time it
1402             * changes, we precalculate the signal level that would
1403             * be displayed in the status bar, and only send the
1404             * broadcast if that much more coarse-grained number
1405             * changes. This cuts down greatly on the number of
1406             * broadcasts, at the cost of not mWifiInforming others
1407             * interested in RSSI of all the changes in signal
1408             * level.
1409             */
1410            // TODO: The second arg to the call below needs to be a symbol somewhere, but
1411            // it's actually the size of an array of icons that's private
1412            // to StatusBar Policy.
1413            int newSignalLevel = WifiManager.calculateSignalLevel(newRssi, 4);
1414            if (newSignalLevel != mLastSignalLevel) {
1415                sendRssiChangeBroadcast(newRssi);
1416            }
1417            mLastSignalLevel = newSignalLevel;
1418        } else {
1419            mWifiInfo.setRssi(MIN_RSSI);
1420        }
1421
1422        if (newLinkSpeed != -1) {
1423            mWifiInfo.setLinkSpeed(newLinkSpeed);
1424        }
1425    }
1426
1427    private void setHighPerfModeEnabledNative(boolean enable) {
1428        if(!WifiNative.setSuspendOptimizationsCommand(!enable)) {
1429            loge("set suspend optimizations failed!");
1430        }
1431        if (enable) {
1432            if (!WifiNative.setPowerModeCommand(POWER_MODE_ACTIVE)) {
1433                loge("set power mode active failed!");
1434            }
1435        } else {
1436            if (!WifiNative.setPowerModeCommand(POWER_MODE_AUTO)) {
1437                loge("set power mode auto failed!");
1438            }
1439        }
1440    }
1441
1442    private void configureLinkProperties() {
1443        if (WifiConfigStore.isUsingStaticIp(mLastNetworkId)) {
1444            mLinkProperties = WifiConfigStore.getLinkProperties(mLastNetworkId);
1445        } else {
1446            synchronized (mDhcpInfoInternal) {
1447                mLinkProperties = mDhcpInfoInternal.makeLinkProperties();
1448            }
1449            mLinkProperties.setHttpProxy(WifiConfigStore.getProxyProperties(mLastNetworkId));
1450        }
1451        mLinkProperties.setInterfaceName(mInterfaceName);
1452        if (DBG) {
1453            log("netId=" + mLastNetworkId  + " Link configured: " +
1454                    mLinkProperties.toString());
1455        }
1456    }
1457
1458    private int getMaxDhcpRetries() {
1459        return Settings.Secure.getInt(mContext.getContentResolver(),
1460                                      Settings.Secure.WIFI_MAX_DHCP_RETRY_COUNT,
1461                                      DEFAULT_MAX_DHCP_RETRIES);
1462    }
1463
1464    private void sendScanResultsAvailableBroadcast() {
1465        Intent intent = new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
1466        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1467        mContext.sendBroadcast(intent);
1468    }
1469
1470    private void sendRssiChangeBroadcast(final int newRssi) {
1471        Intent intent = new Intent(WifiManager.RSSI_CHANGED_ACTION);
1472        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1473        intent.putExtra(WifiManager.EXTRA_NEW_RSSI, newRssi);
1474        mContext.sendBroadcast(intent);
1475    }
1476
1477    private void sendNetworkStateChangeBroadcast(String bssid) {
1478        Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
1479        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
1480                | Intent.FLAG_RECEIVER_REPLACE_PENDING);
1481        intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, mNetworkInfo);
1482        intent.putExtra(WifiManager.EXTRA_LINK_PROPERTIES, new LinkProperties (mLinkProperties));
1483        if (bssid != null)
1484            intent.putExtra(WifiManager.EXTRA_BSSID, bssid);
1485        if (mNetworkInfo.getState() == NetworkInfo.State.CONNECTED)
1486            intent.putExtra(WifiManager.EXTRA_WIFI_INFO, new WifiInfo(mWifiInfo));
1487        mContext.sendStickyBroadcast(intent);
1488    }
1489
1490    private void sendErrorBroadcast(int errorCode) {
1491        Intent intent = new Intent(WifiManager.ERROR_ACTION);
1492        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1493        intent.putExtra(WifiManager.EXTRA_ERROR_CODE, errorCode);
1494        mContext.sendBroadcast(intent);
1495    }
1496
1497    private void sendLinkConfigurationChangedBroadcast() {
1498        Intent intent = new Intent(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION);
1499        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1500        intent.putExtra(WifiManager.EXTRA_LINK_PROPERTIES, new LinkProperties(mLinkProperties));
1501        mContext.sendBroadcast(intent);
1502    }
1503
1504    private void sendSupplicantConnectionChangedBroadcast(boolean connected) {
1505        Intent intent = new Intent(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
1506        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1507        intent.putExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, connected);
1508        mContext.sendBroadcast(intent);
1509    }
1510
1511    /**
1512     * Record the detailed state of a network.
1513     * @param state the new @{code DetailedState}
1514     */
1515    private void setNetworkDetailedState(NetworkInfo.DetailedState state) {
1516        if (DBG) {
1517            log("setDetailed state, old ="
1518                    + mNetworkInfo.getDetailedState() + " and new state=" + state);
1519        }
1520
1521        if (state != mNetworkInfo.getDetailedState()) {
1522            mNetworkInfo.setDetailedState(state, null, null);
1523        }
1524    }
1525
1526    private DetailedState getNetworkDetailedState() {
1527        return mNetworkInfo.getDetailedState();
1528    }
1529
1530
1531    private SupplicantState handleSupplicantStateChange(Message message) {
1532        StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
1533        SupplicantState state = stateChangeResult.state;
1534        // Supplicant state change
1535        // [31-13] Reserved for future use
1536        // [8 - 0] Supplicant state (as defined in SupplicantState.java)
1537        // 50023 supplicant_state_changed (custom|1|5)
1538        EventLog.writeEvent(EVENTLOG_SUPPLICANT_STATE_CHANGED, state.ordinal());
1539        mWifiInfo.setSupplicantState(state);
1540        // Network id is only valid when we start connecting
1541        if (SupplicantState.isConnecting(state)) {
1542            mWifiInfo.setNetworkId(stateChangeResult.networkId);
1543        } else {
1544            mWifiInfo.setNetworkId(WifiConfiguration.INVALID_NETWORK_ID);
1545        }
1546
1547        if (state == SupplicantState.ASSOCIATING) {
1548            /* BSSID is valid only in ASSOCIATING state */
1549            mWifiInfo.setBSSID(stateChangeResult.BSSID);
1550        }
1551
1552        mSupplicantStateTracker.sendMessage(Message.obtain(message));
1553        mWpsStateMachine.sendMessage(Message.obtain(message));
1554
1555        return state;
1556    }
1557
1558    /**
1559     * Resets the Wi-Fi Connections by clearing any state, resetting any sockets
1560     * using the interface, stopping DHCP & disabling interface
1561     */
1562    private void handleNetworkDisconnect() {
1563        if (DBG) log("Stopping DHCP and clearing IP");
1564
1565        /*
1566         * stop DHCP
1567         */
1568        if (mDhcpStateMachine != null) {
1569            mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_STOP_DHCP);
1570            mDhcpStateMachine.quit();
1571            mDhcpStateMachine = null;
1572        }
1573
1574        try {
1575            mNwService.clearInterfaceAddresses(mInterfaceName);
1576            mNwService.disableIpv6(mInterfaceName);
1577        } catch (Exception e) {
1578            loge("Failed to clear addresses or disable ipv6" + e);
1579        }
1580
1581        /* Reset data structures */
1582        mWifiInfo.setInetAddress(null);
1583        mWifiInfo.setBSSID(null);
1584        mWifiInfo.setSSID(null);
1585        mWifiInfo.setNetworkId(WifiConfiguration.INVALID_NETWORK_ID);
1586        mWifiInfo.setRssi(MIN_RSSI);
1587        mWifiInfo.setLinkSpeed(-1);
1588        mWifiInfo.setExplicitConnect(false);
1589
1590        /* send event to CM & network change broadcast */
1591        setNetworkDetailedState(DetailedState.DISCONNECTED);
1592        sendNetworkStateChangeBroadcast(mLastBssid);
1593
1594        /* Clear network properties */
1595        mLinkProperties.clear();
1596        /* Clear IP settings if the network used DHCP */
1597        if (!WifiConfigStore.isUsingStaticIp(mLastNetworkId)) {
1598            WifiConfigStore.clearIpConfiguration(mLastNetworkId);
1599        }
1600
1601        mLastBssid= null;
1602        mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
1603    }
1604
1605    void handlePreDhcpSetup() {
1606        if (!mBluetoothConnectionActive) {
1607            /*
1608             * There are problems setting the Wi-Fi driver's power
1609             * mode to active when bluetooth coexistence mode is
1610             * enabled or sense.
1611             * <p>
1612             * We set Wi-Fi to active mode when
1613             * obtaining an IP address because we've found
1614             * compatibility issues with some routers with low power
1615             * mode.
1616             * <p>
1617             * In order for this active power mode to properly be set,
1618             * we disable coexistence mode until we're done with
1619             * obtaining an IP address.  One exception is if we
1620             * are currently connected to a headset, since disabling
1621             * coexistence would interrupt that connection.
1622             */
1623            // Disable the coexistence mode
1624            WifiNative.setBluetoothCoexistenceModeCommand(
1625                    WifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED);
1626        }
1627
1628        mPowerMode =  WifiNative.getPowerModeCommand();
1629        if (mPowerMode < 0) {
1630            // Handle the case where supplicant driver does not support
1631            // getPowerModeCommand.
1632            mPowerMode = WifiStateMachine.POWER_MODE_AUTO;
1633        }
1634        if (mPowerMode != WifiStateMachine.POWER_MODE_ACTIVE) {
1635            WifiNative.setPowerModeCommand(WifiStateMachine.POWER_MODE_ACTIVE);
1636        }
1637    }
1638
1639
1640    void handlePostDhcpSetup() {
1641        /* restore power mode */
1642        WifiNative.setPowerModeCommand(mPowerMode);
1643
1644        // Set the coexistence mode back to its default value
1645        WifiNative.setBluetoothCoexistenceModeCommand(
1646                WifiNative.BLUETOOTH_COEXISTENCE_MODE_SENSE);
1647    }
1648
1649    private void handleSuccessfulIpConfiguration(DhcpInfoInternal dhcpInfoInternal) {
1650        synchronized (mDhcpInfoInternal) {
1651            mDhcpInfoInternal = dhcpInfoInternal;
1652        }
1653        mLastSignalLevel = -1; // force update of signal strength
1654        WifiConfigStore.setIpConfiguration(mLastNetworkId, dhcpInfoInternal);
1655        InetAddress addr = NetworkUtils.numericToInetAddress(dhcpInfoInternal.ipAddress);
1656        mWifiInfo.setInetAddress(addr);
1657        if (getNetworkDetailedState() == DetailedState.CONNECTED) {
1658            //DHCP renewal in connected state
1659            LinkProperties linkProperties = dhcpInfoInternal.makeLinkProperties();
1660            linkProperties.setHttpProxy(WifiConfigStore.getProxyProperties(mLastNetworkId));
1661            linkProperties.setInterfaceName(mInterfaceName);
1662            if (!linkProperties.equals(mLinkProperties)) {
1663                if (DBG) {
1664                    log("Link configuration changed for netId: " + mLastNetworkId
1665                            + " old: " + mLinkProperties + "new: " + linkProperties);
1666                }
1667                mLinkProperties = linkProperties;
1668                sendLinkConfigurationChangedBroadcast();
1669            }
1670        } else {
1671            configureLinkProperties();
1672            setNetworkDetailedState(DetailedState.CONNECTED);
1673            sendNetworkStateChangeBroadcast(mLastBssid);
1674        }
1675    }
1676
1677    private void handleFailedIpConfiguration() {
1678        loge("IP configuration failed");
1679
1680        mWifiInfo.setInetAddress(null);
1681        /**
1682         * If we've exceeded the maximum number of retries for DHCP
1683         * to a given network, disable the network
1684         */
1685        if (++mReconnectCount > getMaxDhcpRetries()) {
1686            loge("Failed " +
1687                    mReconnectCount + " times, Disabling " + mLastNetworkId);
1688            WifiConfigStore.disableNetwork(mLastNetworkId,
1689                    WifiConfiguration.DISABLED_DHCP_FAILURE);
1690            mReconnectCount = 0;
1691        }
1692
1693        /* DHCP times out after about 30 seconds, we do a
1694         * disconnect and an immediate reconnect to try again
1695         */
1696        WifiNative.disconnectCommand();
1697        WifiNative.reconnectCommand();
1698    }
1699
1700    /* Current design is to not set the config on a running hostapd but instead
1701     * stop and start tethering when user changes config on a running access point
1702     *
1703     * TODO: Add control channel setup through hostapd that allows changing config
1704     * on a running daemon
1705     */
1706    private boolean startSoftApWithConfig(WifiConfiguration config) {
1707        if (config == null) {
1708            config = WifiApConfigStore.getApConfiguration();
1709        } else {
1710            WifiApConfigStore.setApConfiguration(config);
1711        }
1712        try {
1713            mNwService.startAccessPoint(config, mInterfaceName, SOFTAP_IFACE);
1714        } catch (Exception e) {
1715            loge("Exception in softap start " + e);
1716            try {
1717                mNwService.stopAccessPoint(mInterfaceName);
1718                mNwService.startAccessPoint(config, mInterfaceName, SOFTAP_IFACE);
1719            } catch (Exception e1) {
1720                loge("Exception in softap re-start " + e1);
1721                return false;
1722            }
1723        }
1724        return true;
1725    }
1726
1727    /********************************************************
1728     * HSM states
1729     *******************************************************/
1730
1731    class DefaultState extends State {
1732        @Override
1733        public boolean processMessage(Message message) {
1734            if (DBG) log(getName() + message.toString() + "\n");
1735            switch (message.what) {
1736                case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
1737                    if (message.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
1738                        mWifiP2pChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
1739                    } else {
1740                        loge("WifiP2pService connection failure, error=" + message.arg1);
1741                    }
1742                    break;
1743                case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
1744                    loge("WifiP2pService channel lost, message.arg1 =" + message.arg1);
1745                    //TODO: Re-establish connection to state machine after a delay
1746                    //mWifiP2pChannel.connect(mContext, getHandler(), mWifiP2pManager.getMessenger());
1747                    break;
1748                case CMD_BLUETOOTH_ADAPTER_STATE_CHANGE:
1749                    mBluetoothConnectionActive = (message.arg1 !=
1750                            BluetoothAdapter.STATE_DISCONNECTED);
1751                    break;
1752                    /* Synchronous call returns */
1753                case CMD_PING_SUPPLICANT:
1754                case CMD_ENABLE_NETWORK:
1755                case CMD_DISABLE_NETWORK:
1756                case CMD_ADD_OR_UPDATE_NETWORK:
1757                case CMD_REMOVE_NETWORK:
1758                case CMD_SAVE_CONFIG:
1759                    mReplyChannel.replyToMessage(message, message.what, FAILURE);
1760                    break;
1761                case CMD_ENABLE_RSSI_POLL:
1762                    mEnableRssiPolling = (message.arg1 == 1);
1763                    break;
1764                case CMD_ENABLE_BACKGROUND_SCAN:
1765                    mEnableBackgroundScan = (message.arg1 == 1);
1766                    break;
1767                case CMD_SET_AP_CONFIG:
1768                    WifiApConfigStore.setApConfiguration((WifiConfiguration) message.obj);
1769                    break;
1770                case CMD_GET_AP_CONFIG:
1771                    WifiConfiguration config = WifiApConfigStore.getApConfiguration();
1772                    mReplyChannel.replyToMessage(message, message.what, config);
1773                    break;
1774                    /* Discard */
1775                case CMD_LOAD_DRIVER:
1776                case CMD_UNLOAD_DRIVER:
1777                case CMD_START_SUPPLICANT:
1778                case CMD_STOP_SUPPLICANT:
1779                case CMD_STOP_SUPPLICANT_FAILED:
1780                case CMD_START_DRIVER:
1781                case CMD_STOP_DRIVER:
1782                case CMD_START_AP:
1783                case CMD_START_AP_SUCCESS:
1784                case CMD_START_AP_FAILURE:
1785                case CMD_STOP_AP:
1786                case CMD_TETHER_INTERFACE:
1787                case CMD_START_SCAN:
1788                case CMD_DISCONNECT:
1789                case CMD_RECONNECT:
1790                case CMD_REASSOCIATE:
1791                case WifiMonitor.SUP_CONNECTION_EVENT:
1792                case WifiMonitor.SUP_DISCONNECTION_EVENT:
1793                case WifiMonitor.NETWORK_CONNECTION_EVENT:
1794                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
1795                case WifiMonitor.SCAN_RESULTS_EVENT:
1796                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
1797                case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
1798                case WifiMonitor.WPS_OVERLAP_EVENT:
1799                case CMD_BLACKLIST_NETWORK:
1800                case CMD_CLEAR_BLACKLIST:
1801                case CMD_SET_SCAN_MODE:
1802                case CMD_SET_SCAN_TYPE:
1803                case CMD_SET_HIGH_PERF_MODE:
1804                case CMD_SET_COUNTRY_CODE:
1805                case CMD_SET_FREQUENCY_BAND:
1806                case CMD_REQUEST_CM_WAKELOCK:
1807                case CMD_CONNECT_NETWORK:
1808                case CMD_SAVE_NETWORK:
1809                case CMD_FORGET_NETWORK:
1810                case CMD_RSSI_POLL:
1811                case CMD_ENABLE_ALL_NETWORKS:
1812                case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
1813                case DhcpStateMachine.CMD_POST_DHCP_ACTION:
1814                    break;
1815                case WifiMonitor.DRIVER_HUNG_EVENT:
1816                    setWifiEnabled(false);
1817                    setWifiEnabled(true);
1818                    break;
1819                case CMD_START_WPS:
1820                    /* Return failure when the state machine cannot handle WPS initiation*/
1821                    mReplyChannel.replyToMessage(message, WifiManager.CMD_WPS_COMPLETED,
1822                                new WpsResult(Status.FAILURE));
1823                    break;
1824                case WifiP2pService.P2P_ENABLE_PENDING:
1825                    // turn off wifi and defer to be handled in DriverUnloadedState
1826                    setWifiEnabled(false);
1827                    deferMessage(message);
1828                    break;
1829                default:
1830                    loge("Error! unhandled message" + message);
1831                    break;
1832            }
1833            return HANDLED;
1834        }
1835    }
1836
1837    class InitialState extends State {
1838        @Override
1839        //TODO: could move logging into a common class
1840        public void enter() {
1841            if (DBG) log(getName() + "\n");
1842            // [31-8] Reserved for future use
1843            // [7 - 0] HSM state change
1844            // 50021 wifi_state_changed (custom|1|5)
1845            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
1846
1847            WifiApConfigStore.initialize(mContext);
1848
1849            if (WifiNative.isDriverLoaded()) {
1850                transitionTo(mDriverLoadedState);
1851            }
1852            else {
1853                transitionTo(mDriverUnloadedState);
1854            }
1855
1856            //Connect to WifiP2pService
1857            mWifiP2pManager = (WifiP2pManager) mContext.getSystemService(Context.WIFI_P2P_SERVICE);
1858            mWifiP2pChannel.connect(mContext, getHandler(), mWifiP2pManager.getMessenger());
1859
1860            /* IPv6 is disabled at boot time and is controlled by framework
1861             * to be enabled only as long as we are connected to an access point
1862             *
1863             * This fixes issues, a few being:
1864             * - IPv6 addresses and routes stick around after disconnection
1865             * - When connected, the kernel is unaware and can fail to start IPv6 negotiation
1866             * - The kernel sometimes starts autoconfiguration when 802.1x is not complete
1867             */
1868            try {
1869                mNwService.disableIpv6(mInterfaceName);
1870            } catch (RemoteException re) {
1871                loge("Failed to disable IPv6: " + re);
1872            } catch (IllegalStateException e) {
1873                loge("Failed to disable IPv6: " + e);
1874            }
1875        }
1876    }
1877
1878    class DriverLoadingState extends State {
1879        @Override
1880        public void enter() {
1881            if (DBG) log(getName() + "\n");
1882            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
1883
1884            final Message message = new Message();
1885            message.copyFrom(getCurrentMessage());
1886            /* TODO: add a timeout to fail when driver load is hung.
1887             * Similarly for driver unload.
1888             */
1889            new Thread(new Runnable() {
1890                public void run() {
1891                    mWakeLock.acquire();
1892                    //enabling state
1893                    switch(message.arg1) {
1894                        case WIFI_STATE_ENABLING:
1895                            setWifiState(WIFI_STATE_ENABLING);
1896                            break;
1897                        case WIFI_AP_STATE_ENABLING:
1898                            setWifiApState(WIFI_AP_STATE_ENABLING);
1899                            break;
1900                    }
1901
1902                    if(WifiNative.loadDriver()) {
1903                        if (DBG) log("Driver load successful");
1904                        sendMessage(CMD_LOAD_DRIVER_SUCCESS);
1905                    } else {
1906                        loge("Failed to load driver!");
1907                        switch(message.arg1) {
1908                            case WIFI_STATE_ENABLING:
1909                                setWifiState(WIFI_STATE_UNKNOWN);
1910                                break;
1911                            case WIFI_AP_STATE_ENABLING:
1912                                setWifiApState(WIFI_AP_STATE_FAILED);
1913                                break;
1914                        }
1915                        sendMessage(CMD_LOAD_DRIVER_FAILURE);
1916                    }
1917                    mWakeLock.release();
1918                }
1919            }).start();
1920        }
1921
1922        @Override
1923        public boolean processMessage(Message message) {
1924            if (DBG) log(getName() + message.toString() + "\n");
1925            switch (message.what) {
1926                case CMD_LOAD_DRIVER_SUCCESS:
1927                    transitionTo(mDriverLoadedState);
1928                    break;
1929                case CMD_LOAD_DRIVER_FAILURE:
1930                    transitionTo(mDriverFailedState);
1931                    break;
1932                case CMD_LOAD_DRIVER:
1933                case CMD_UNLOAD_DRIVER:
1934                case CMD_START_SUPPLICANT:
1935                case CMD_STOP_SUPPLICANT:
1936                case CMD_START_AP:
1937                case CMD_STOP_AP:
1938                case CMD_START_DRIVER:
1939                case CMD_STOP_DRIVER:
1940                case CMD_SET_SCAN_MODE:
1941                case CMD_SET_SCAN_TYPE:
1942                case CMD_SET_HIGH_PERF_MODE:
1943                case CMD_SET_COUNTRY_CODE:
1944                case CMD_SET_FREQUENCY_BAND:
1945                case CMD_START_PACKET_FILTERING:
1946                case CMD_STOP_PACKET_FILTERING:
1947                    deferMessage(message);
1948                    break;
1949                default:
1950                    return NOT_HANDLED;
1951            }
1952            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
1953            return HANDLED;
1954        }
1955    }
1956
1957    class DriverLoadedState extends State {
1958        @Override
1959        public void enter() {
1960            if (DBG) log(getName() + "\n");
1961            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
1962        }
1963        @Override
1964        public boolean processMessage(Message message) {
1965            if (DBG) log(getName() + message.toString() + "\n");
1966            switch(message.what) {
1967                case CMD_UNLOAD_DRIVER:
1968                    transitionTo(mDriverUnloadingState);
1969                    break;
1970                case CMD_START_SUPPLICANT:
1971                    try {
1972                        mNwService.wifiFirmwareReload(mInterfaceName, "STA");
1973                    } catch (Exception e) {
1974                        loge("Failed to reload STA firmware " + e);
1975                        // continue
1976                    }
1977                   try {
1978                       //A runtime crash can leave the interface up and
1979                       //this affects connectivity when supplicant starts up.
1980                       //Ensure interface is down before a supplicant start.
1981                        mNwService.setInterfaceDown(mInterfaceName);
1982                        //Set privacy extensions
1983                        mNwService.setInterfaceIpv6PrivacyExtensions(mInterfaceName, true);
1984                    } catch (RemoteException re) {
1985                        loge("Unable to change interface settings: " + re);
1986                    } catch (IllegalStateException ie) {
1987                        loge("Unable to change interface settings: " + ie);
1988                    }
1989
1990                    if(WifiNative.startSupplicant()) {
1991                        if (DBG) log("Supplicant start successful");
1992                        mWifiMonitor.startMonitoring();
1993                        transitionTo(mSupplicantStartingState);
1994                    } else {
1995                        loge("Failed to start supplicant!");
1996                        sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_STATE_UNKNOWN, 0));
1997                    }
1998                    break;
1999                case CMD_START_AP:
2000                    transitionTo(mSoftApStartingState);
2001                    break;
2002                default:
2003                    return NOT_HANDLED;
2004            }
2005            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
2006            return HANDLED;
2007        }
2008    }
2009
2010    class DriverUnloadingState extends State {
2011        @Override
2012        public void enter() {
2013            if (DBG) log(getName() + "\n");
2014            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
2015
2016            final Message message = new Message();
2017            message.copyFrom(getCurrentMessage());
2018            new Thread(new Runnable() {
2019                public void run() {
2020                    if (DBG) log(getName() + message.toString() + "\n");
2021                    mWakeLock.acquire();
2022                    if(WifiNative.unloadDriver()) {
2023                        if (DBG) log("Driver unload successful");
2024                        sendMessage(CMD_UNLOAD_DRIVER_SUCCESS);
2025
2026                        switch(message.arg1) {
2027                            case WIFI_STATE_DISABLED:
2028                            case WIFI_STATE_UNKNOWN:
2029                                setWifiState(message.arg1);
2030                                break;
2031                            case WIFI_AP_STATE_DISABLED:
2032                            case WIFI_AP_STATE_FAILED:
2033                                setWifiApState(message.arg1);
2034                                break;
2035                        }
2036                    } else {
2037                        loge("Failed to unload driver!");
2038                        sendMessage(CMD_UNLOAD_DRIVER_FAILURE);
2039
2040                        switch(message.arg1) {
2041                            case WIFI_STATE_DISABLED:
2042                            case WIFI_STATE_UNKNOWN:
2043                                setWifiState(WIFI_STATE_UNKNOWN);
2044                                break;
2045                            case WIFI_AP_STATE_DISABLED:
2046                            case WIFI_AP_STATE_FAILED:
2047                                setWifiApState(WIFI_AP_STATE_FAILED);
2048                                break;
2049                        }
2050                    }
2051                    mWakeLock.release();
2052                }
2053            }).start();
2054        }
2055
2056        @Override
2057        public boolean processMessage(Message message) {
2058            if (DBG) log(getName() + message.toString() + "\n");
2059            switch (message.what) {
2060                case CMD_UNLOAD_DRIVER_SUCCESS:
2061                    transitionTo(mDriverUnloadedState);
2062                    break;
2063                case CMD_UNLOAD_DRIVER_FAILURE:
2064                    transitionTo(mDriverFailedState);
2065                    break;
2066                case CMD_LOAD_DRIVER:
2067                case CMD_UNLOAD_DRIVER:
2068                case CMD_START_SUPPLICANT:
2069                case CMD_STOP_SUPPLICANT:
2070                case CMD_START_AP:
2071                case CMD_STOP_AP:
2072                case CMD_START_DRIVER:
2073                case CMD_STOP_DRIVER:
2074                case CMD_SET_SCAN_MODE:
2075                case CMD_SET_SCAN_TYPE:
2076                case CMD_SET_HIGH_PERF_MODE:
2077                case CMD_SET_COUNTRY_CODE:
2078                case CMD_SET_FREQUENCY_BAND:
2079                case CMD_START_PACKET_FILTERING:
2080                case CMD_STOP_PACKET_FILTERING:
2081                    deferMessage(message);
2082                    break;
2083                default:
2084                    return NOT_HANDLED;
2085            }
2086            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
2087            return HANDLED;
2088        }
2089    }
2090
2091    class DriverUnloadedState extends State {
2092        @Override
2093        public void enter() {
2094            if (DBG) log(getName() + "\n");
2095            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
2096        }
2097        @Override
2098        public boolean processMessage(Message message) {
2099            if (DBG) log(getName() + message.toString() + "\n");
2100            switch (message.what) {
2101                case CMD_LOAD_DRIVER:
2102                    mWifiP2pChannel.sendMessage(WIFI_ENABLE_PENDING);
2103                    transitionTo(mWaitForP2pDisableState);
2104                    break;
2105                case WifiP2pService.P2P_ENABLE_PENDING:
2106                    mReplyChannel.replyToMessage(message, P2P_ENABLE_PROCEED);
2107                    break;
2108                default:
2109                    return NOT_HANDLED;
2110            }
2111            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
2112            return HANDLED;
2113        }
2114    }
2115
2116    class DriverFailedState extends State {
2117        @Override
2118        public void enter() {
2119            loge(getName() + "\n");
2120            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
2121        }
2122        @Override
2123        public boolean processMessage(Message message) {
2124            if (DBG) log(getName() + message.toString() + "\n");
2125            return NOT_HANDLED;
2126        }
2127    }
2128
2129
2130    class SupplicantStartingState extends State {
2131        @Override
2132        public void enter() {
2133            if (DBG) log(getName() + "\n");
2134            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
2135        }
2136        @Override
2137        public boolean processMessage(Message message) {
2138            if (DBG) log(getName() + message.toString() + "\n");
2139            switch(message.what) {
2140                case WifiMonitor.SUP_CONNECTION_EVENT:
2141                    if (DBG) log("Supplicant connection established");
2142                    setWifiState(WIFI_STATE_ENABLED);
2143                    mSupplicantRestartCount = 0;
2144                    /* Reset the supplicant state to indicate the supplicant
2145                     * state is not known at this time */
2146                    mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
2147                    mWpsStateMachine.sendMessage(CMD_RESET_WPS_STATE);
2148                    /* Initialize data structures */
2149                    mLastBssid = null;
2150                    mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
2151                    mLastSignalLevel = -1;
2152
2153                    mWifiInfo.setMacAddress(WifiNative.getMacAddressCommand());
2154
2155                    WifiConfigStore.initialize(mContext);
2156
2157                    sendSupplicantConnectionChangedBroadcast(true);
2158                    transitionTo(mDriverStartedState);
2159                    break;
2160                case WifiMonitor.SUP_DISCONNECTION_EVENT:
2161                    if (++mSupplicantRestartCount <= SUPPLICANT_RESTART_TRIES) {
2162                        loge("Failed to setup control channel, restart supplicant");
2163                        WifiNative.killSupplicant();
2164                        transitionTo(mDriverLoadedState);
2165                        sendMessageDelayed(CMD_START_SUPPLICANT, SUPPLICANT_RESTART_INTERVAL_MSECS);
2166                    } else {
2167                        loge("Failed " + mSupplicantRestartCount +
2168                                " times to start supplicant, unload driver");
2169                        mSupplicantRestartCount = 0;
2170                        transitionTo(mDriverLoadedState);
2171                        sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_STATE_UNKNOWN, 0));
2172                    }
2173                    break;
2174                case CMD_LOAD_DRIVER:
2175                case CMD_UNLOAD_DRIVER:
2176                case CMD_START_SUPPLICANT:
2177                case CMD_STOP_SUPPLICANT:
2178                case CMD_START_AP:
2179                case CMD_STOP_AP:
2180                case CMD_START_DRIVER:
2181                case CMD_STOP_DRIVER:
2182                case CMD_SET_SCAN_MODE:
2183                case CMD_SET_SCAN_TYPE:
2184                case CMD_SET_HIGH_PERF_MODE:
2185                case CMD_SET_COUNTRY_CODE:
2186                case CMD_SET_FREQUENCY_BAND:
2187                case CMD_START_PACKET_FILTERING:
2188                case CMD_STOP_PACKET_FILTERING:
2189                    deferMessage(message);
2190                    break;
2191                default:
2192                    return NOT_HANDLED;
2193            }
2194            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
2195            return HANDLED;
2196        }
2197    }
2198
2199    class SupplicantStartedState extends State {
2200        @Override
2201        public void enter() {
2202            if (DBG) log(getName() + "\n");
2203            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
2204            /* Initialize for connect mode operation at start */
2205            mIsScanMode = false;
2206            /* Wifi is available as long as we have a connection to supplicant */
2207            mNetworkInfo.setIsAvailable(true);
2208            /* Set scan interval */
2209            long supplicantScanIntervalMs = Settings.Secure.getLong(mContext.getContentResolver(),
2210                    Settings.Secure.WIFI_SUPPLICANT_SCAN_INTERVAL_MS,
2211                    mDefaultSupplicantScanIntervalMs);
2212            WifiNative.setScanIntervalCommand((int)supplicantScanIntervalMs / 1000);
2213        }
2214        @Override
2215        public boolean processMessage(Message message) {
2216            if (DBG) log(getName() + message.toString() + "\n");
2217            WifiConfiguration config;
2218            boolean eventLoggingEnabled = true;
2219            switch(message.what) {
2220                case CMD_STOP_SUPPLICANT:   /* Supplicant stopped by user */
2221                    transitionTo(mSupplicantStoppingState);
2222                    break;
2223                case WifiMonitor.SUP_DISCONNECTION_EVENT:  /* Supplicant connection lost */
2224                    loge("Connection lost, restart supplicant");
2225                    WifiNative.killSupplicant();
2226                    WifiNative.closeSupplicantConnection();
2227                    mNetworkInfo.setIsAvailable(false);
2228                    handleNetworkDisconnect();
2229                    sendSupplicantConnectionChangedBroadcast(false);
2230                    mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
2231                    mWpsStateMachine.sendMessage(CMD_RESET_WPS_STATE);
2232                    transitionTo(mDriverLoadedState);
2233                    sendMessageDelayed(CMD_START_SUPPLICANT, SUPPLICANT_RESTART_INTERVAL_MSECS);
2234                    break;
2235                case WifiMonitor.SCAN_RESULTS_EVENT:
2236                    eventLoggingEnabled = false;
2237                    setScanResults(WifiNative.scanResultsCommand());
2238                    sendScanResultsAvailableBroadcast();
2239                    mScanResultIsPending = false;
2240                    break;
2241                case CMD_PING_SUPPLICANT:
2242                    boolean ok = WifiNative.pingCommand();
2243                    mReplyChannel.replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
2244                    break;
2245                case CMD_ADD_OR_UPDATE_NETWORK:
2246                    config = (WifiConfiguration) message.obj;
2247                    mReplyChannel.replyToMessage(message, CMD_ADD_OR_UPDATE_NETWORK,
2248                            WifiConfigStore.addOrUpdateNetwork(config));
2249                    break;
2250                case CMD_REMOVE_NETWORK:
2251                    ok = WifiConfigStore.removeNetwork(message.arg1);
2252                    mReplyChannel.replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
2253                    break;
2254                case CMD_ENABLE_NETWORK:
2255                    ok = WifiConfigStore.enableNetwork(message.arg1, message.arg2 == 1);
2256                    mReplyChannel.replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
2257                    break;
2258                case CMD_ENABLE_ALL_NETWORKS:
2259                    long time =  android.os.SystemClock.elapsedRealtime();
2260                    if (time - mLastEnableAllNetworksTime > MIN_INTERVAL_ENABLE_ALL_NETWORKS_MS) {
2261                        WifiConfigStore.enableAllNetworks();
2262                        mLastEnableAllNetworksTime = time;
2263                    }
2264                    break;
2265                case CMD_DISABLE_NETWORK:
2266                    ok = WifiConfigStore.disableNetwork(message.arg1, message.arg2);
2267                    mReplyChannel.replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
2268                    break;
2269                case CMD_BLACKLIST_NETWORK:
2270                    WifiNative.addToBlacklistCommand((String)message.obj);
2271                    break;
2272                case CMD_CLEAR_BLACKLIST:
2273                    WifiNative.clearBlacklistCommand();
2274                    break;
2275                case CMD_SAVE_CONFIG:
2276                    ok = WifiConfigStore.saveConfig();
2277                    mReplyChannel.replyToMessage(message, CMD_SAVE_CONFIG, ok ? SUCCESS : FAILURE);
2278
2279                    // Inform the backup manager about a data change
2280                    IBackupManager ibm = IBackupManager.Stub.asInterface(
2281                            ServiceManager.getService(Context.BACKUP_SERVICE));
2282                    if (ibm != null) {
2283                        try {
2284                            ibm.dataChanged("com.android.providers.settings");
2285                        } catch (Exception e) {
2286                            // Try again later
2287                        }
2288                    }
2289                    break;
2290                    /* Cannot start soft AP while in client mode */
2291                case CMD_START_AP:
2292                    loge("Failed to start soft AP with a running supplicant");
2293                    setWifiApState(WIFI_AP_STATE_FAILED);
2294                    break;
2295                case CMD_SET_SCAN_MODE:
2296                    mIsScanMode = (message.arg1 == SCAN_ONLY_MODE);
2297                    break;
2298                case CMD_SAVE_NETWORK:
2299                    config = (WifiConfiguration) message.obj;
2300                    WifiConfigStore.saveNetwork(config);
2301                    break;
2302                case CMD_FORGET_NETWORK:
2303                    WifiConfigStore.forgetNetwork(message.arg1);
2304                    break;
2305                default:
2306                    return NOT_HANDLED;
2307            }
2308            if (eventLoggingEnabled) {
2309                EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
2310            }
2311            return HANDLED;
2312        }
2313
2314        @Override
2315        public void exit() {
2316            mNetworkInfo.setIsAvailable(false);
2317        }
2318    }
2319
2320    class SupplicantStoppingState extends State {
2321        @Override
2322        public void enter() {
2323            if (DBG) log(getName() + "\n");
2324            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
2325            if (DBG) log("stopping supplicant");
2326            if (!WifiNative.stopSupplicant()) {
2327                loge("Failed to stop supplicant");
2328            }
2329
2330            /* Send ourselves a delayed message to indicate failure after a wait time */
2331            sendMessageDelayed(obtainMessage(CMD_STOP_SUPPLICANT_FAILED,
2332                    ++mSupplicantStopFailureToken, 0), SUPPLICANT_RESTART_INTERVAL_MSECS);
2333
2334            mNetworkInfo.setIsAvailable(false);
2335            handleNetworkDisconnect();
2336            setWifiState(WIFI_STATE_DISABLING);
2337            sendSupplicantConnectionChangedBroadcast(false);
2338            mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
2339            mWpsStateMachine.sendMessage(CMD_RESET_WPS_STATE);
2340        }
2341        @Override
2342        public boolean processMessage(Message message) {
2343            if (DBG) log(getName() + message.toString() + "\n");
2344            switch(message.what) {
2345                case WifiMonitor.SUP_CONNECTION_EVENT:
2346                    loge("Supplicant connection received while stopping");
2347                    break;
2348                case WifiMonitor.SUP_DISCONNECTION_EVENT:
2349                    if (DBG) log("Supplicant connection lost");
2350                    /* Socket connection can be lost when we do a graceful shutdown
2351                     * or when the driver is hung. Ensure supplicant is stopped here.
2352                     */
2353                    WifiNative.killSupplicant();
2354                    WifiNative.closeSupplicantConnection();
2355                    transitionTo(mDriverLoadedState);
2356                    break;
2357                case CMD_STOP_SUPPLICANT_FAILED:
2358                    if (message.arg1 == mSupplicantStopFailureToken) {
2359                        loge("Timed out on a supplicant stop, kill and proceed");
2360                        WifiNative.killSupplicant();
2361                        WifiNative.closeSupplicantConnection();
2362                        transitionTo(mDriverLoadedState);
2363                    }
2364                    break;
2365                case CMD_LOAD_DRIVER:
2366                case CMD_UNLOAD_DRIVER:
2367                case CMD_START_SUPPLICANT:
2368                case CMD_STOP_SUPPLICANT:
2369                case CMD_START_AP:
2370                case CMD_STOP_AP:
2371                case CMD_START_DRIVER:
2372                case CMD_STOP_DRIVER:
2373                case CMD_SET_SCAN_MODE:
2374                case CMD_SET_SCAN_TYPE:
2375                case CMD_SET_HIGH_PERF_MODE:
2376                case CMD_SET_COUNTRY_CODE:
2377                case CMD_SET_FREQUENCY_BAND:
2378                case CMD_START_PACKET_FILTERING:
2379                case CMD_STOP_PACKET_FILTERING:
2380                    deferMessage(message);
2381                    break;
2382                default:
2383                    return NOT_HANDLED;
2384            }
2385            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
2386            return HANDLED;
2387        }
2388    }
2389
2390    class DriverStartingState extends State {
2391        @Override
2392        public void enter() {
2393            if (DBG) log(getName() + "\n");
2394            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
2395        }
2396        @Override
2397        public boolean processMessage(Message message) {
2398            if (DBG) log(getName() + message.toString() + "\n");
2399            switch(message.what) {
2400               case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
2401                    SupplicantState state = handleSupplicantStateChange(message);
2402                    /* If suplicant is exiting out of INTERFACE_DISABLED state into
2403                     * a state that indicates driver has started, it is ready to
2404                     * receive driver commands
2405                     */
2406                    if (SupplicantState.isDriverActive(state)) {
2407                        transitionTo(mDriverStartedState);
2408                    }
2409                    break;
2410                    /* Queue driver commands & connection events */
2411                case CMD_START_DRIVER:
2412                case CMD_STOP_DRIVER:
2413                case WifiMonitor.NETWORK_CONNECTION_EVENT:
2414                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
2415                case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
2416                case WifiMonitor.WPS_OVERLAP_EVENT:
2417                case CMD_SET_SCAN_TYPE:
2418                case CMD_SET_HIGH_PERF_MODE:
2419                case CMD_SET_COUNTRY_CODE:
2420                case CMD_SET_FREQUENCY_BAND:
2421                case CMD_START_PACKET_FILTERING:
2422                case CMD_STOP_PACKET_FILTERING:
2423                case CMD_START_SCAN:
2424                case CMD_DISCONNECT:
2425                case CMD_REASSOCIATE:
2426                case CMD_RECONNECT:
2427                    deferMessage(message);
2428                    break;
2429                default:
2430                    return NOT_HANDLED;
2431            }
2432            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
2433            return HANDLED;
2434        }
2435    }
2436
2437    class DriverStartedState extends State {
2438        @Override
2439        public void enter() {
2440            if (DBG) log(getName() + "\n");
2441            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
2442
2443            mIsRunning = true;
2444            updateBatteryWorkSource(null);
2445
2446            /**
2447             * Enable bluetooth coexistence scan mode when bluetooth connection is active.
2448             * When this mode is on, some of the low-level scan parameters used by the
2449             * driver are changed to reduce interference with bluetooth
2450             */
2451            WifiNative.setBluetoothCoexistenceScanModeCommand(mBluetoothConnectionActive);
2452            /* set country code */
2453            setCountryCode();
2454            /* set frequency band of operation */
2455            setFrequencyBand();
2456            /* initialize network state */
2457            setNetworkDetailedState(DetailedState.DISCONNECTED);
2458
2459            /* Remove any filtering on Multicast v6 at start */
2460            WifiNative.stopFilteringMulticastV6Packets();
2461
2462            /* Reset Multicast v4 filtering state */
2463            if (mFilteringMulticastV4Packets.get()) {
2464                WifiNative.startFilteringMulticastV4Packets();
2465            } else {
2466                WifiNative.stopFilteringMulticastV4Packets();
2467            }
2468
2469            if (mIsScanMode) {
2470                WifiNative.setScanResultHandlingCommand(SCAN_ONLY_MODE);
2471                WifiNative.disconnectCommand();
2472                transitionTo(mScanModeState);
2473            } else {
2474                WifiNative.setScanResultHandlingCommand(CONNECT_MODE);
2475                WifiNative.reconnectCommand();
2476                transitionTo(mDisconnectedState);
2477            }
2478        }
2479        @Override
2480        public boolean processMessage(Message message) {
2481            if (DBG) log(getName() + message.toString() + "\n");
2482            boolean eventLoggingEnabled = true;
2483            switch(message.what) {
2484                case CMD_SET_SCAN_TYPE:
2485                    if (message.arg1 == SCAN_ACTIVE) {
2486                        WifiNative.setScanModeCommand(true);
2487                    } else {
2488                        WifiNative.setScanModeCommand(false);
2489                    }
2490                    break;
2491                case CMD_START_SCAN:
2492                    eventLoggingEnabled = false;
2493                    WifiNative.scanCommand(message.arg1 == SCAN_ACTIVE);
2494                    mScanResultIsPending = true;
2495                    break;
2496                case CMD_SET_HIGH_PERF_MODE:
2497                    setHighPerfModeEnabledNative(message.arg1 == 1);
2498                    break;
2499                case CMD_SET_COUNTRY_CODE:
2500                    String country = (String) message.obj;
2501                    if (DBG) log("set country code " + country);
2502                    if (!WifiNative.setCountryCodeCommand(country.toUpperCase())) {
2503                        loge("Failed to set country code " + country);
2504                    }
2505                    break;
2506                case CMD_SET_FREQUENCY_BAND:
2507                    int band =  message.arg1;
2508                    if (DBG) log("set frequency band " + band);
2509                    if (WifiNative.setBandCommand(band)) {
2510                        mFrequencyBand.set(band);
2511                        //Fetch the latest scan results when frequency band is set
2512                        startScan(true);
2513                    } else {
2514                        loge("Failed to set frequency band " + band);
2515                    }
2516                    break;
2517                case CMD_BLUETOOTH_ADAPTER_STATE_CHANGE:
2518                    mBluetoothConnectionActive = (message.arg1 !=
2519                            BluetoothAdapter.STATE_DISCONNECTED);
2520                    WifiNative.setBluetoothCoexistenceScanModeCommand(mBluetoothConnectionActive);
2521                    break;
2522                case CMD_STOP_DRIVER:
2523                    mWakeLock.acquire();
2524                    WifiNative.stopDriverCommand();
2525                    transitionTo(mDriverStoppingState);
2526                    mWakeLock.release();
2527                    break;
2528                case CMD_START_PACKET_FILTERING:
2529                    if (message.arg1 == MULTICAST_V6) {
2530                        WifiNative.startFilteringMulticastV6Packets();
2531                    } else if (message.arg1 == MULTICAST_V4) {
2532                        WifiNative.startFilteringMulticastV4Packets();
2533                    } else {
2534                        loge("Illegal arugments to CMD_START_PACKET_FILTERING");
2535                    }
2536                    break;
2537                case CMD_STOP_PACKET_FILTERING:
2538                    if (message.arg1 == MULTICAST_V6) {
2539                        WifiNative.stopFilteringMulticastV6Packets();
2540                    } else if (message.arg1 == MULTICAST_V4) {
2541                        WifiNative.stopFilteringMulticastV4Packets();
2542                    } else {
2543                        loge("Illegal arugments to CMD_STOP_PACKET_FILTERING");
2544                    }
2545                    break;
2546                default:
2547                    return NOT_HANDLED;
2548            }
2549            if (eventLoggingEnabled) {
2550                EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
2551            }
2552            return HANDLED;
2553        }
2554        @Override
2555        public void exit() {
2556            if (DBG) log(getName() + "\n");
2557            mIsRunning = false;
2558            updateBatteryWorkSource(null);
2559            mScanResults = null;
2560        }
2561    }
2562
2563    class DriverStoppingState extends State {
2564        @Override
2565        public void enter() {
2566            if (DBG) log(getName() + "\n");
2567            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
2568        }
2569        @Override
2570        public boolean processMessage(Message message) {
2571            if (DBG) log(getName() + message.toString() + "\n");
2572            switch(message.what) {
2573                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
2574                    SupplicantState state = handleSupplicantStateChange(message);
2575                    if (state == SupplicantState.INTERFACE_DISABLED) {
2576                        transitionTo(mDriverStoppedState);
2577                    }
2578                    break;
2579                    /* Queue driver commands */
2580                case CMD_START_DRIVER:
2581                case CMD_STOP_DRIVER:
2582                case CMD_SET_SCAN_TYPE:
2583                case CMD_SET_HIGH_PERF_MODE:
2584                case CMD_SET_COUNTRY_CODE:
2585                case CMD_SET_FREQUENCY_BAND:
2586                case CMD_START_PACKET_FILTERING:
2587                case CMD_STOP_PACKET_FILTERING:
2588                case CMD_START_SCAN:
2589                case CMD_DISCONNECT:
2590                case CMD_REASSOCIATE:
2591                case CMD_RECONNECT:
2592                    deferMessage(message);
2593                    break;
2594                default:
2595                    return NOT_HANDLED;
2596            }
2597            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
2598            return HANDLED;
2599        }
2600    }
2601
2602    class DriverStoppedState extends State {
2603        @Override
2604        public void enter() {
2605            if (DBG) log(getName() + "\n");
2606            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
2607        }
2608        @Override
2609        public boolean processMessage(Message message) {
2610            if (DBG) log(getName() + message.toString() + "\n");
2611            switch (message.what) {
2612                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
2613                    StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
2614                    SupplicantState state = stateChangeResult.state;
2615                    // A WEXT bug means that we can be back to driver started state
2616                    // unexpectedly
2617                    if (SupplicantState.isDriverActive(state)) {
2618                        transitionTo(mDriverStartedState);
2619                    }
2620                    break;
2621                case CMD_START_DRIVER:
2622                    mWakeLock.acquire();
2623                    WifiNative.startDriverCommand();
2624                    mWakeLock.release();
2625                    transitionTo(mDriverStartingState);
2626                    break;
2627                default:
2628                    return NOT_HANDLED;
2629            }
2630            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
2631            return HANDLED;
2632        }
2633    }
2634
2635    class ScanModeState extends State {
2636        @Override
2637        public void enter() {
2638            if (DBG) log(getName() + "\n");
2639            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
2640        }
2641        @Override
2642        public boolean processMessage(Message message) {
2643            if (DBG) log(getName() + message.toString() + "\n");
2644            switch(message.what) {
2645                case CMD_SET_SCAN_MODE:
2646                    if (message.arg1 == SCAN_ONLY_MODE) {
2647                        /* Ignore */
2648                        return HANDLED;
2649                    } else {
2650                        WifiNative.setScanResultHandlingCommand(message.arg1);
2651                        WifiNative.reconnectCommand();
2652                        mIsScanMode = false;
2653                        transitionTo(mDisconnectedState);
2654                    }
2655                    break;
2656                    /* Ignore */
2657                case CMD_DISCONNECT:
2658                case CMD_RECONNECT:
2659                case CMD_REASSOCIATE:
2660                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
2661                case WifiMonitor.NETWORK_CONNECTION_EVENT:
2662                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
2663                    break;
2664                default:
2665                    return NOT_HANDLED;
2666            }
2667            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
2668            return HANDLED;
2669        }
2670    }
2671
2672    class ConnectModeState extends State {
2673        @Override
2674        public void enter() {
2675            if (DBG) log(getName() + "\n");
2676            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
2677        }
2678        @Override
2679        public boolean processMessage(Message message) {
2680            if (DBG) log(getName() + message.toString() + "\n");
2681            StateChangeResult stateChangeResult;
2682            switch(message.what) {
2683                case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
2684                    mSupplicantStateTracker.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT);
2685                    break;
2686                case WifiMonitor.WPS_OVERLAP_EVENT:
2687                    /* We just need to broadcast the error */
2688                    sendErrorBroadcast(WifiManager.WPS_OVERLAP_ERROR);
2689                    break;
2690                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
2691                    SupplicantState state = handleSupplicantStateChange(message);
2692                    // Due to a WEXT bug, during the time of driver start/stop
2693                    // we can go into a driver stopped state in an unexpected way.
2694                    // The sequence eventually puts interface
2695                    // up and we should be back to a connected state
2696                    if (!SupplicantState.isDriverActive(state)) {
2697                        if (mNetworkInfo.getState() != NetworkInfo.State.DISCONNECTED) {
2698                            handleNetworkDisconnect();
2699                        }
2700                        transitionTo(mDriverStoppedState);
2701                        break;
2702                    }
2703
2704                    // Supplicant can fail to report a NETWORK_DISCONNECTION_EVENT
2705                    // when authentication times out after a successful connection,
2706                    // we can figure this from the supplicant state. If supplicant
2707                    // state is DISCONNECTED, but the mNetworkInfo says we are not
2708                    // disconnected, we need to handle a disconnection
2709                    if (state == SupplicantState.DISCONNECTED &&
2710                            mNetworkInfo.getState() != NetworkInfo.State.DISCONNECTED) {
2711                        if (DBG) log("Missed CTRL-EVENT-DISCONNECTED, disconnect");
2712                        handleNetworkDisconnect();
2713                        transitionTo(mDisconnectedState);
2714                    }
2715                    break;
2716                    /* Do a redundant disconnect without transition */
2717                case CMD_DISCONNECT:
2718                    WifiNative.disconnectCommand();
2719                    break;
2720                case CMD_RECONNECT:
2721                    WifiNative.reconnectCommand();
2722                    break;
2723                case CMD_REASSOCIATE:
2724                    WifiNative.reassociateCommand();
2725                    break;
2726                case CMD_CONNECT_NETWORK:
2727                    int netId = message.arg1;
2728                    WifiConfiguration config = (WifiConfiguration) message.obj;
2729
2730                    /* We connect to a specific network by issuing a select
2731                     * to the WifiConfigStore. This enables the network,
2732                     * while disabling all other networks in the supplicant.
2733                     * Disabling a connected network will cause a disconnection
2734                     * from the network. A reconnectCommand() will then initiate
2735                     * a connection to the enabled network.
2736                     */
2737                    if (config != null) {
2738                        netId = WifiConfigStore.selectNetwork(config);
2739                    } else {
2740                        WifiConfigStore.selectNetwork(netId);
2741                    }
2742
2743                    /* The state tracker handles enabling networks upon completion/failure */
2744                    mSupplicantStateTracker.sendMessage(CMD_CONNECT_NETWORK);
2745
2746                    WifiNative.reconnectCommand();
2747                    mLastExplicitNetworkId = netId;
2748                    mLastNetworkChoiceTime  = SystemClock.elapsedRealtime();
2749                    mNextWifiActionExplicit = true;
2750                    if (DBG) log("Setting wifi connect explicit for netid " + netId);
2751                    /* Expect a disconnection from the old connection */
2752                    transitionTo(mDisconnectingState);
2753                    break;
2754                case CMD_START_WPS:
2755                    mWpsStateMachine.sendMessage(Message.obtain(message));
2756                    transitionTo(mWaitForWpsCompletionState);
2757                    break;
2758                case WifiMonitor.SCAN_RESULTS_EVENT:
2759                    /* Set the scan setting back to "connect" mode */
2760                    WifiNative.setScanResultHandlingCommand(CONNECT_MODE);
2761                    /* Handle scan results */
2762                    return NOT_HANDLED;
2763                case WifiMonitor.NETWORK_CONNECTION_EVENT:
2764                    if (DBG) log("Network connection established");
2765                    mLastNetworkId = message.arg1;
2766                    mLastBssid = (String) message.obj;
2767
2768                    //TODO: make supplicant modification to push this in events
2769                    mWifiInfo.setSSID(fetchSSID());
2770                    mWifiInfo.setBSSID(mLastBssid);
2771                    mWifiInfo.setNetworkId(mLastNetworkId);
2772                    if (mNextWifiActionExplicit &&
2773                        mWifiInfo.getNetworkId() == mLastExplicitNetworkId &&
2774                        SystemClock.elapsedRealtime() < mLastNetworkChoiceTime +
2775                                                            EXPLICIT_CONNECT_ALLOWED_DELAY_MS) {
2776                        mWifiInfo.setExplicitConnect(true);
2777                    }
2778                    mNextWifiActionExplicit = false;
2779                    /* send event to CM & network change broadcast */
2780                    setNetworkDetailedState(DetailedState.OBTAINING_IPADDR);
2781                    sendNetworkStateChangeBroadcast(mLastBssid);
2782                    transitionTo(mConnectingState);
2783                    break;
2784                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
2785                    if (DBG) log("Network connection lost");
2786                    handleNetworkDisconnect();
2787                    transitionTo(mDisconnectedState);
2788                    break;
2789                default:
2790                    return NOT_HANDLED;
2791            }
2792            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
2793            return HANDLED;
2794        }
2795    }
2796
2797    class ConnectingState extends State {
2798
2799        @Override
2800        public void enter() {
2801            if (DBG) log(getName() + "\n");
2802            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
2803
2804            try {
2805                mNwService.enableIpv6(mInterfaceName);
2806            } catch (RemoteException re) {
2807                loge("Failed to enable IPv6: " + re);
2808            } catch (IllegalStateException e) {
2809                loge("Failed to enable IPv6: " + e);
2810            }
2811
2812            if (!WifiConfigStore.isUsingStaticIp(mLastNetworkId)) {
2813                //start DHCP
2814                mDhcpStateMachine = DhcpStateMachine.makeDhcpStateMachine(
2815                        mContext, WifiStateMachine.this, mInterfaceName);
2816                mDhcpStateMachine.registerForPreDhcpNotification();
2817                mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_START_DHCP);
2818            } else {
2819                DhcpInfoInternal dhcpInfoInternal = WifiConfigStore.getIpConfiguration(
2820                        mLastNetworkId);
2821                InterfaceConfiguration ifcg = new InterfaceConfiguration();
2822                ifcg.addr = dhcpInfoInternal.makeLinkAddress();
2823                ifcg.interfaceFlags = "[up]";
2824                try {
2825                    mNwService.setInterfaceConfig(mInterfaceName, ifcg);
2826                    if (DBG) log("Static IP configuration succeeded");
2827                    sendMessage(CMD_STATIC_IP_SUCCESS, dhcpInfoInternal);
2828                } catch (RemoteException re) {
2829                    loge("Static IP configuration failed: " + re);
2830                    sendMessage(CMD_STATIC_IP_FAILURE);
2831                } catch (IllegalStateException e) {
2832                    loge("Static IP configuration failed: " + e);
2833                    sendMessage(CMD_STATIC_IP_FAILURE);
2834                }
2835            }
2836        }
2837      @Override
2838      public boolean processMessage(Message message) {
2839          if (DBG) log(getName() + message.toString() + "\n");
2840
2841          switch(message.what) {
2842              case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
2843                  handlePreDhcpSetup();
2844                  mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_PRE_DHCP_ACTION_COMPLETE);
2845                  break;
2846              case DhcpStateMachine.CMD_POST_DHCP_ACTION:
2847                  handlePostDhcpSetup();
2848                  if (message.arg1 == DhcpStateMachine.DHCP_SUCCESS) {
2849                      handleSuccessfulIpConfiguration((DhcpInfoInternal) message.obj);
2850                      transitionTo(mConnectedState);
2851                  } else if (message.arg1 == DhcpStateMachine.DHCP_FAILURE) {
2852                      handleFailedIpConfiguration();
2853                      transitionTo(mDisconnectingState);
2854                  }
2855                  break;
2856              case CMD_STATIC_IP_SUCCESS:
2857                  handleSuccessfulIpConfiguration((DhcpInfoInternal) message.obj);
2858                  transitionTo(mConnectedState);
2859                  break;
2860              case CMD_STATIC_IP_FAILURE:
2861                  handleFailedIpConfiguration();
2862                  transitionTo(mDisconnectingState);
2863                  break;
2864              case CMD_DISCONNECT:
2865                  WifiNative.disconnectCommand();
2866                  transitionTo(mDisconnectingState);
2867                  break;
2868                  /* Ignore connection to same network */
2869              case CMD_CONNECT_NETWORK:
2870                  int netId = message.arg1;
2871                  if (mWifiInfo.getNetworkId() == netId) {
2872                      break;
2873                  }
2874                  return NOT_HANDLED;
2875              case CMD_SAVE_NETWORK:
2876                  deferMessage(message);
2877                  break;
2878                  /* Ignore */
2879              case WifiMonitor.NETWORK_CONNECTION_EVENT:
2880                  break;
2881              case CMD_STOP_DRIVER:
2882                  sendMessage(CMD_DISCONNECT);
2883                  deferMessage(message);
2884                  break;
2885              case CMD_SET_SCAN_MODE:
2886                  if (message.arg1 == SCAN_ONLY_MODE) {
2887                      sendMessage(CMD_DISCONNECT);
2888                      deferMessage(message);
2889                  }
2890                  break;
2891                  /* Defer scan when IP is being fetched */
2892              case CMD_START_SCAN:
2893                  deferMessage(message);
2894                  break;
2895                  /* Defer any power mode changes since we must keep active power mode at DHCP */
2896              case CMD_SET_HIGH_PERF_MODE:
2897                  deferMessage(message);
2898                  break;
2899              default:
2900                return NOT_HANDLED;
2901          }
2902          EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
2903          return HANDLED;
2904      }
2905    }
2906
2907    class ConnectedState extends State {
2908        @Override
2909        public void enter() {
2910            if (DBG) log(getName() + "\n");
2911            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
2912            mRssiPollToken++;
2913            if (mEnableRssiPolling) {
2914                sendMessage(obtainMessage(WifiStateMachine.CMD_RSSI_POLL, mRssiPollToken, 0));
2915            }
2916        }
2917        @Override
2918        public boolean processMessage(Message message) {
2919            if (DBG) log(getName() + message.toString() + "\n");
2920            boolean eventLoggingEnabled = true;
2921            switch (message.what) {
2922              case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
2923                  handlePreDhcpSetup();
2924                  mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_PRE_DHCP_ACTION_COMPLETE);
2925                  break;
2926              case DhcpStateMachine.CMD_POST_DHCP_ACTION:
2927                  handlePostDhcpSetup();
2928                  if (message.arg1 == DhcpStateMachine.DHCP_SUCCESS) {
2929                      handleSuccessfulIpConfiguration((DhcpInfoInternal) message.obj);
2930                  } else if (message.arg1 == DhcpStateMachine.DHCP_FAILURE) {
2931                      handleFailedIpConfiguration();
2932                      transitionTo(mDisconnectingState);
2933                  }
2934                  break;
2935                case CMD_DISCONNECT:
2936                    WifiNative.disconnectCommand();
2937                    transitionTo(mDisconnectingState);
2938                    break;
2939                case CMD_STOP_DRIVER:
2940                    sendMessage(CMD_DISCONNECT);
2941                    deferMessage(message);
2942                    break;
2943                case CMD_REQUEST_CM_WAKELOCK:
2944                    checkAndSetConnectivityInstance();
2945                    mCm.requestNetworkTransitionWakelock(TAG);
2946                    break;
2947                case CMD_SET_SCAN_MODE:
2948                    if (message.arg1 == SCAN_ONLY_MODE) {
2949                        sendMessage(CMD_DISCONNECT);
2950                        deferMessage(message);
2951                    }
2952                    break;
2953                case CMD_START_SCAN:
2954                    eventLoggingEnabled = false;
2955                    /* When the network is connected, re-scanning can trigger
2956                     * a reconnection. Put it in scan-only mode during scan.
2957                     * When scan results are received, the mode is switched
2958                     * back to CONNECT_MODE.
2959                     */
2960                    WifiNative.setScanResultHandlingCommand(SCAN_ONLY_MODE);
2961                    /* Have the parent state handle the rest */
2962                    return NOT_HANDLED;
2963                    /* Ignore connection to same network */
2964                case CMD_CONNECT_NETWORK:
2965                    int netId = message.arg1;
2966                    if (mWifiInfo.getNetworkId() == netId) {
2967                        break;
2968                    }
2969                    return NOT_HANDLED;
2970                case CMD_SAVE_NETWORK:
2971                    WifiConfiguration config = (WifiConfiguration) message.obj;
2972                    NetworkUpdateResult result = WifiConfigStore.saveNetwork(config);
2973                    if (mWifiInfo.getNetworkId() == result.getNetworkId()) {
2974                        if (result.hasIpChanged()) {
2975                            log("Reconfiguring IP on connection");
2976                            transitionTo(mConnectingState);
2977                        }
2978                        if (result.hasProxyChanged()) {
2979                            log("Reconfiguring proxy on connection");
2980                            configureLinkProperties();
2981                            sendLinkConfigurationChangedBroadcast();
2982                        }
2983                    }
2984                    break;
2985                    /* Ignore */
2986                case WifiMonitor.NETWORK_CONNECTION_EVENT:
2987                    break;
2988                case CMD_RSSI_POLL:
2989                    eventLoggingEnabled = false;
2990                    if (message.arg1 == mRssiPollToken) {
2991                        // Get Info and continue polling
2992                        fetchRssiAndLinkSpeedNative();
2993                        sendMessageDelayed(obtainMessage(WifiStateMachine.CMD_RSSI_POLL,
2994                                mRssiPollToken, 0), POLL_RSSI_INTERVAL_MSECS);
2995                    } else {
2996                        // Polling has completed
2997                    }
2998                    break;
2999                case CMD_ENABLE_RSSI_POLL:
3000                    mEnableRssiPolling = (message.arg1 == 1);
3001                    mRssiPollToken++;
3002                    if (mEnableRssiPolling) {
3003                        // first poll
3004                        fetchRssiAndLinkSpeedNative();
3005                        sendMessageDelayed(obtainMessage(WifiStateMachine.CMD_RSSI_POLL,
3006                                mRssiPollToken, 0), POLL_RSSI_INTERVAL_MSECS);
3007                    }
3008                    break;
3009                default:
3010                    return NOT_HANDLED;
3011            }
3012            if (eventLoggingEnabled) {
3013                EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
3014            }
3015            return HANDLED;
3016        }
3017        @Override
3018        public void exit() {
3019            /* If a scan result is pending in connected state, the supplicant
3020             * is in SCAN_ONLY_MODE. Restore CONNECT_MODE on exit
3021             */
3022            if (mScanResultIsPending) {
3023                WifiNative.setScanResultHandlingCommand(CONNECT_MODE);
3024            }
3025        }
3026    }
3027
3028    class DisconnectingState extends State {
3029        @Override
3030        public void enter() {
3031            if (DBG) log(getName() + "\n");
3032            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
3033        }
3034        @Override
3035        public boolean processMessage(Message message) {
3036            if (DBG) log(getName() + message.toString() + "\n");
3037            switch (message.what) {
3038                case CMD_STOP_DRIVER: /* Stop driver only after disconnect handled */
3039                    deferMessage(message);
3040                    break;
3041                case CMD_SET_SCAN_MODE:
3042                    if (message.arg1 == SCAN_ONLY_MODE) {
3043                        deferMessage(message);
3044                    }
3045                    break;
3046                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
3047                    /* If we get a SUPPLICANT_STATE_CHANGE_EVENT before NETWORK_DISCONNECTION_EVENT
3048                     * we have missed the network disconnection, transition to mDisconnectedState
3049                     * and handle the rest of the events there
3050                     */
3051                    deferMessage(message);
3052                    handleNetworkDisconnect();
3053                    transitionTo(mDisconnectedState);
3054                    break;
3055                default:
3056                    return NOT_HANDLED;
3057            }
3058            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
3059            return HANDLED;
3060        }
3061    }
3062
3063    class DisconnectedState extends State {
3064        private boolean mAlarmEnabled = false;
3065        /* This is set from the overlay config file or from a secure setting.
3066         * A value of 0 disables scanning in the framework.
3067         */
3068        private long mFrameworkScanIntervalMs;
3069
3070        private void setScanAlarm(boolean enabled) {
3071            if (enabled == mAlarmEnabled) return;
3072            if (enabled) {
3073                if (mFrameworkScanIntervalMs > 0) {
3074                    mAlarmManager.setRepeating(AlarmManager.RTC_WAKEUP,
3075                            System.currentTimeMillis() + mFrameworkScanIntervalMs,
3076                            mFrameworkScanIntervalMs,
3077                            mScanIntent);
3078                    mAlarmEnabled = true;
3079                }
3080            } else {
3081                mAlarmManager.cancel(mScanIntent);
3082                mAlarmEnabled = false;
3083            }
3084        }
3085
3086        @Override
3087        public void enter() {
3088            if (DBG) log(getName() + "\n");
3089            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
3090
3091            mFrameworkScanIntervalMs = Settings.Secure.getLong(mContext.getContentResolver(),
3092                    Settings.Secure.WIFI_FRAMEWORK_SCAN_INTERVAL_MS,
3093                    mDefaultFrameworkScanIntervalMs);
3094            /*
3095             * We initiate background scanning if it is enabled, otherwise we
3096             * initiate an infrequent scan that wakes up the device to ensure
3097             * a user connects to an access point on the move
3098             */
3099            if (mEnableBackgroundScan) {
3100                /* If a regular scan result is pending, do not initiate background
3101                 * scan until the scan results are returned. This is needed because
3102                 * initiating a background scan will cancel the regular scan and
3103                 * scan results will not be returned until background scanning is
3104                 * cleared
3105                 */
3106                if (!mScanResultIsPending) {
3107                    WifiNative.enableBackgroundScanCommand(true);
3108                }
3109            } else {
3110                setScanAlarm(true);
3111            }
3112        }
3113        @Override
3114        public boolean processMessage(Message message) {
3115            if (DBG) log(getName() + message.toString() + "\n");
3116            switch (message.what) {
3117                case CMD_SET_SCAN_MODE:
3118                    if (message.arg1 == SCAN_ONLY_MODE) {
3119                        WifiNative.setScanResultHandlingCommand(message.arg1);
3120                        //Supplicant disconnect to prevent further connects
3121                        WifiNative.disconnectCommand();
3122                        mIsScanMode = true;
3123                        transitionTo(mScanModeState);
3124                    }
3125                    break;
3126                case CMD_ENABLE_BACKGROUND_SCAN:
3127                    mEnableBackgroundScan = (message.arg1 == 1);
3128                    if (mEnableBackgroundScan) {
3129                        WifiNative.enableBackgroundScanCommand(true);
3130                        setScanAlarm(false);
3131                    } else {
3132                        WifiNative.enableBackgroundScanCommand(false);
3133                        setScanAlarm(true);
3134                    }
3135                    break;
3136                    /* Ignore network disconnect */
3137                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
3138                    break;
3139                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
3140                    StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
3141                    setNetworkDetailedState(WifiInfo.getDetailedStateOf(stateChangeResult.state));
3142                    /* ConnectModeState does the rest of the handling */
3143                    return NOT_HANDLED;
3144                case CMD_START_SCAN:
3145                    /* Disable background scan temporarily during a regular scan */
3146                    if (mEnableBackgroundScan) {
3147                        WifiNative.enableBackgroundScanCommand(false);
3148                    }
3149                    /* Handled in parent state */
3150                    return NOT_HANDLED;
3151                case WifiMonitor.SCAN_RESULTS_EVENT:
3152                    /* Re-enable background scan when a pending scan result is received */
3153                    if (mEnableBackgroundScan && mScanResultIsPending) {
3154                        WifiNative.enableBackgroundScanCommand(true);
3155                    }
3156                    /* Handled in parent state */
3157                    return NOT_HANDLED;
3158                default:
3159                    return NOT_HANDLED;
3160            }
3161            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
3162            return HANDLED;
3163        }
3164
3165        @Override
3166        public void exit() {
3167            /* No need for a background scan upon exit from a disconnected state */
3168            if (mEnableBackgroundScan) {
3169                WifiNative.enableBackgroundScanCommand(false);
3170            }
3171            setScanAlarm(false);
3172        }
3173    }
3174
3175    class WaitForWpsCompletionState extends State {
3176        @Override
3177        public void enter() {
3178            if (DBG) log(getName() + "\n");
3179            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
3180        }
3181        @Override
3182        public boolean processMessage(Message message) {
3183            if (DBG) log(getName() + message.toString() + "\n");
3184            switch (message.what) {
3185                /* Defer all commands that can cause connections to a different network
3186                 * or put the state machine out of connect mode
3187                 */
3188                case CMD_STOP_DRIVER:
3189                case CMD_SET_SCAN_MODE:
3190                case CMD_CONNECT_NETWORK:
3191                case CMD_ENABLE_NETWORK:
3192                case CMD_RECONNECT:
3193                case CMD_REASSOCIATE:
3194                case WifiMonitor.NETWORK_CONNECTION_EVENT: /* Handled after IP & proxy update */
3195                    deferMessage(message);
3196                    break;
3197                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
3198                    if (DBG) log("Network connection lost");
3199                    handleNetworkDisconnect();
3200                    break;
3201                case WPS_COMPLETED_EVENT:
3202                    /* we are still disconnected until we see a network connection
3203                     * notification */
3204                    transitionTo(mDisconnectedState);
3205                    break;
3206                default:
3207                    return NOT_HANDLED;
3208            }
3209            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
3210            return HANDLED;
3211        }
3212    }
3213
3214    class SoftApStartingState extends State {
3215        @Override
3216        public void enter() {
3217            if (DBG) log(getName() + "\n");
3218            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
3219
3220            final Message message = Message.obtain(getCurrentMessage());
3221            final WifiConfiguration config = (WifiConfiguration) message.obj;
3222
3223            // start hostapd on a seperate thread
3224            new Thread(new Runnable() {
3225                public void run() {
3226                    if (startSoftApWithConfig(config)) {
3227                        if (DBG) log("Soft AP start successful");
3228                        sendMessage(CMD_START_AP_SUCCESS);
3229                    } else {
3230                        loge("Soft AP start failed");
3231                        sendMessage(CMD_START_AP_FAILURE);
3232                    }
3233                }
3234            }).start();
3235        }
3236        @Override
3237        public boolean processMessage(Message message) {
3238            if (DBG) log(getName() + message.toString() + "\n");
3239            switch(message.what) {
3240                case CMD_LOAD_DRIVER:
3241                case CMD_UNLOAD_DRIVER:
3242                case CMD_START_SUPPLICANT:
3243                case CMD_STOP_SUPPLICANT:
3244                case CMD_START_AP:
3245                case CMD_STOP_AP:
3246                case CMD_START_DRIVER:
3247                case CMD_STOP_DRIVER:
3248                case CMD_SET_SCAN_MODE:
3249                case CMD_SET_SCAN_TYPE:
3250                case CMD_SET_HIGH_PERF_MODE:
3251                case CMD_SET_COUNTRY_CODE:
3252                case CMD_SET_FREQUENCY_BAND:
3253                case CMD_START_PACKET_FILTERING:
3254                case CMD_STOP_PACKET_FILTERING:
3255                case CMD_TETHER_INTERFACE:
3256                case WifiP2pService.P2P_ENABLE_PENDING:
3257                    deferMessage(message);
3258                    break;
3259                case CMD_START_AP_SUCCESS:
3260                    setWifiApState(WIFI_AP_STATE_ENABLED);
3261                    transitionTo(mSoftApStartedState);
3262                    break;
3263                case CMD_START_AP_FAILURE:
3264                    // initiate driver unload
3265                    sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_AP_STATE_FAILED, 0));
3266                    break;
3267                default:
3268                    return NOT_HANDLED;
3269            }
3270            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
3271            return HANDLED;
3272        }
3273    }
3274
3275    class SoftApStartedState extends State {
3276        @Override
3277        public void enter() {
3278            if (DBG) log(getName() + "\n");
3279            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
3280        }
3281        @Override
3282        public boolean processMessage(Message message) {
3283            if (DBG) log(getName() + message.toString() + "\n");
3284            switch(message.what) {
3285                case CMD_STOP_AP:
3286                    if (DBG) log("Stopping Soft AP");
3287                    setWifiApState(WIFI_AP_STATE_DISABLING);
3288                    stopTethering();
3289                    try {
3290                        mNwService.stopAccessPoint(mInterfaceName);
3291                    } catch(Exception e) {
3292                        loge("Exception in stopAccessPoint()");
3293                    }
3294                    transitionTo(mDriverLoadedState);
3295                    break;
3296                case CMD_START_AP:
3297                    // Ignore a start on a running access point
3298                    break;
3299                    /* Fail client mode operation when soft AP is enabled */
3300                case CMD_START_SUPPLICANT:
3301                   loge("Cannot start supplicant with a running soft AP");
3302                    setWifiState(WIFI_STATE_UNKNOWN);
3303                    break;
3304                case CMD_TETHER_INTERFACE:
3305                    ArrayList<String> available = (ArrayList<String>) message.obj;
3306                    if (startTethering(available)) {
3307                        transitionTo(mTetheredState);
3308                    }
3309                    break;
3310                case WifiP2pService.P2P_ENABLE_PENDING:
3311                    // turn of soft Ap and defer to be handled in DriverUnloadedState
3312                    setWifiApEnabled(null, false);
3313                    deferMessage(message);
3314                    break;
3315                default:
3316                    return NOT_HANDLED;
3317            }
3318            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
3319            return HANDLED;
3320        }
3321    }
3322
3323    class WaitForP2pDisableState extends State {
3324        private int mSavedArg;
3325        @Override
3326        public void enter() {
3327            if (DBG) log(getName() + "\n");
3328            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
3329
3330            //Preserve the argument arg1 that has information used in DriverLoadingState
3331            mSavedArg = getCurrentMessage().arg1;
3332        }
3333        @Override
3334        public boolean processMessage(Message message) {
3335            if (DBG) log(getName() + message.toString() + "\n");
3336            switch(message.what) {
3337                case WifiP2pService.WIFI_ENABLE_PROCEED:
3338                    //restore argument from original message (CMD_LOAD_DRIVER)
3339                    message.arg1 = mSavedArg;
3340                    transitionTo(mDriverLoadingState);
3341                    break;
3342                case CMD_LOAD_DRIVER:
3343                case CMD_UNLOAD_DRIVER:
3344                case CMD_START_SUPPLICANT:
3345                case CMD_STOP_SUPPLICANT:
3346                case CMD_START_AP:
3347                case CMD_STOP_AP:
3348                case CMD_START_DRIVER:
3349                case CMD_STOP_DRIVER:
3350                case CMD_SET_SCAN_MODE:
3351                case CMD_SET_SCAN_TYPE:
3352                case CMD_SET_HIGH_PERF_MODE:
3353                case CMD_SET_COUNTRY_CODE:
3354                case CMD_SET_FREQUENCY_BAND:
3355                case CMD_START_PACKET_FILTERING:
3356                case CMD_STOP_PACKET_FILTERING:
3357                    deferMessage(message);
3358                    break;
3359                default:
3360                    return NOT_HANDLED;
3361            }
3362            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
3363            return HANDLED;
3364        }
3365    }
3366
3367    class TetheredState extends State {
3368        @Override
3369        public void enter() {
3370            if (DBG) log(getName() + "\n");
3371            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
3372        }
3373        @Override
3374        public boolean processMessage(Message message) {
3375            if (DBG) log(getName() + message.toString() + "\n");
3376            switch(message.what) {
3377               case CMD_TETHER_INTERFACE:
3378                    // Ignore any duplicate interface available notifications
3379                    // when in tethered state
3380                    return HANDLED;
3381                default:
3382                    return NOT_HANDLED;
3383            }
3384        }
3385    }
3386
3387    private void log(String s) {
3388        Log.d(TAG, s);
3389    }
3390
3391    private void loge(String s) {
3392        Log.e(TAG, s);
3393    }
3394}
3395