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