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