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