WifiStateMachine.java revision 81196b207a63bce1a430752db74a65e2a2be6d80
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();
1115        mWifiConfigStore.dump(fd, pw, args);
1116    }
1117
1118    /*********************************************************
1119     * Internal private functions
1120     ********************************************************/
1121
1122    private void handleScreenStateChanged(boolean screenOn) {
1123        if (DBG) log("handleScreenStateChanged: " + screenOn);
1124        enableRssiPolling(screenOn);
1125        if (mBackgroundScanSupported) {
1126            enableBackgroundScanCommand(screenOn == false);
1127        }
1128
1129        if (screenOn) enableAllNetworks();
1130        if (mUserWantsSuspendOpt.get()) {
1131            if (screenOn) {
1132                sendMessage(CMD_SET_SUSPEND_OPT_ENABLED, 0, 0);
1133            } else {
1134                //Allow 2s for suspend optimizations to be set
1135                mSuspendWakeLock.acquire(2000);
1136                sendMessage(CMD_SET_SUSPEND_OPT_ENABLED, 1, 0);
1137            }
1138        }
1139        mScreenBroadcastReceived.set(true);
1140    }
1141
1142    private void checkAndSetConnectivityInstance() {
1143        if (mCm == null) {
1144            mCm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
1145        }
1146    }
1147
1148    private boolean startTethering(ArrayList<String> available) {
1149
1150        boolean wifiAvailable = false;
1151
1152        checkAndSetConnectivityInstance();
1153
1154        String[] wifiRegexs = mCm.getTetherableWifiRegexs();
1155
1156        for (String intf : available) {
1157            for (String regex : wifiRegexs) {
1158                if (intf.matches(regex)) {
1159
1160                    InterfaceConfiguration ifcg = null;
1161                    try {
1162                        ifcg = mNwService.getInterfaceConfig(intf);
1163                        if (ifcg != null) {
1164                            /* IP/netmask: 192.168.43.1/255.255.255.0 */
1165                            ifcg.setLinkAddress(new LinkAddress(
1166                                    NetworkUtils.numericToInetAddress("192.168.43.1"), 24));
1167                            ifcg.setInterfaceUp();
1168
1169                            mNwService.setInterfaceConfig(intf, ifcg);
1170                        }
1171                    } catch (Exception e) {
1172                        loge("Error configuring interface " + intf + ", :" + e);
1173                        return false;
1174                    }
1175
1176                    if(mCm.tether(intf) != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
1177                        loge("Error tethering on " + intf);
1178                        return false;
1179                    }
1180                    mTetherInterfaceName = intf;
1181                    return true;
1182                }
1183            }
1184        }
1185        // We found no interfaces to tether
1186        return false;
1187    }
1188
1189    private void stopTethering() {
1190
1191        checkAndSetConnectivityInstance();
1192
1193        /* Clear the interface config to allow dhcp correctly configure new
1194           ip settings */
1195        InterfaceConfiguration ifcg = null;
1196        try {
1197            ifcg = mNwService.getInterfaceConfig(mTetherInterfaceName);
1198            if (ifcg != null) {
1199                ifcg.setLinkAddress(
1200                        new LinkAddress(NetworkUtils.numericToInetAddress("0.0.0.0"), 0));
1201                mNwService.setInterfaceConfig(mTetherInterfaceName, ifcg);
1202            }
1203        } catch (Exception e) {
1204            loge("Error resetting interface " + mTetherInterfaceName + ", :" + e);
1205        }
1206
1207        if (mCm.untether(mTetherInterfaceName) != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
1208            loge("Untether initiate failed!");
1209        }
1210    }
1211
1212    private boolean isWifiTethered(ArrayList<String> active) {
1213
1214        checkAndSetConnectivityInstance();
1215
1216        String[] wifiRegexs = mCm.getTetherableWifiRegexs();
1217        for (String intf : active) {
1218            for (String regex : wifiRegexs) {
1219                if (intf.matches(regex)) {
1220                    return true;
1221                }
1222            }
1223        }
1224        // We found no interfaces that are tethered
1225        return false;
1226    }
1227
1228    /**
1229     * Set the country code from the system setting value, if any.
1230     */
1231    private void setCountryCode() {
1232        String countryCode = Settings.Global.getString(mContext.getContentResolver(),
1233                Settings.Global.WIFI_COUNTRY_CODE);
1234        if (countryCode != null && !countryCode.isEmpty()) {
1235            setCountryCode(countryCode, false);
1236        } else {
1237            //use driver default
1238        }
1239    }
1240
1241    /**
1242     * Set the frequency band from the system setting value, if any.
1243     */
1244    private void setFrequencyBand() {
1245        int band = Settings.Global.getInt(mContext.getContentResolver(),
1246                Settings.Global.WIFI_FREQUENCY_BAND, WifiManager.WIFI_FREQUENCY_BAND_AUTO);
1247        setFrequencyBand(band, false);
1248    }
1249
1250    private void setSuspendOptimizationsNative(int reason, boolean enabled) {
1251        if (DBG) log("setSuspendOptimizationsNative: " + reason + " " + enabled);
1252        if (enabled) {
1253            mSuspendOptNeedsDisabled &= ~reason;
1254            /* None of dhcp, screen or highperf need it disabled and user wants it enabled */
1255            if (mSuspendOptNeedsDisabled == 0 && mUserWantsSuspendOpt.get()) {
1256                mWifiNative.setSuspendOptimizations(true);
1257            }
1258        } else {
1259            mSuspendOptNeedsDisabled |= reason;
1260            mWifiNative.setSuspendOptimizations(false);
1261        }
1262    }
1263
1264    private void setSuspendOptimizations(int reason, boolean enabled) {
1265        if (DBG) log("setSuspendOptimizations: " + reason + " " + enabled);
1266        if (enabled) {
1267            mSuspendOptNeedsDisabled &= ~reason;
1268        } else {
1269            mSuspendOptNeedsDisabled |= reason;
1270        }
1271        if (DBG) log("mSuspendOptNeedsDisabled " + mSuspendOptNeedsDisabled);
1272    }
1273
1274    private void setWifiState(int wifiState) {
1275        final int previousWifiState = mWifiState.get();
1276
1277        try {
1278            if (wifiState == WIFI_STATE_ENABLED) {
1279                mBatteryStats.noteWifiOn();
1280            } else if (wifiState == WIFI_STATE_DISABLED) {
1281                mBatteryStats.noteWifiOff();
1282            }
1283        } catch (RemoteException e) {
1284            loge("Failed to note battery stats in wifi");
1285        }
1286
1287        mWifiState.set(wifiState);
1288
1289        if (DBG) log("setWifiState: " + syncGetWifiStateByName());
1290
1291        final Intent intent = new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION);
1292        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1293        intent.putExtra(WifiManager.EXTRA_WIFI_STATE, wifiState);
1294        intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_STATE, previousWifiState);
1295        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
1296    }
1297
1298    private void setWifiApState(int wifiApState) {
1299        final int previousWifiApState = mWifiApState.get();
1300
1301        try {
1302            if (wifiApState == WIFI_AP_STATE_ENABLED) {
1303                mBatteryStats.noteWifiOn();
1304            } else if (wifiApState == WIFI_AP_STATE_DISABLED) {
1305                mBatteryStats.noteWifiOff();
1306            }
1307        } catch (RemoteException e) {
1308            loge("Failed to note battery stats in wifi");
1309        }
1310
1311        // Update state
1312        mWifiApState.set(wifiApState);
1313
1314        if (DBG) log("setWifiApState: " + syncGetWifiApStateByName());
1315
1316        final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
1317        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1318        intent.putExtra(WifiManager.EXTRA_WIFI_AP_STATE, wifiApState);
1319        intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_AP_STATE, previousWifiApState);
1320        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
1321    }
1322
1323    private static final String ID_STR = "id=";
1324    private static final String BSSID_STR = "bssid=";
1325    private static final String FREQ_STR = "freq=";
1326    private static final String LEVEL_STR = "level=";
1327    private static final String TSF_STR = "tsf=";
1328    private static final String FLAGS_STR = "flags=";
1329    private static final String SSID_STR = "ssid=";
1330    private static final String DELIMITER_STR = "====";
1331    private static final String END_STR = "####";
1332
1333    /**
1334     * Format:
1335     *
1336     * id=1
1337     * bssid=68:7f:76:d7:1a:6e
1338     * freq=2412
1339     * level=-44
1340     * tsf=1344626243700342
1341     * flags=[WPA2-PSK-CCMP][WPS][ESS]
1342     * ssid=zfdy
1343     * ====
1344     * id=2
1345     * bssid=68:5f:74:d7:1a:6f
1346     * freq=5180
1347     * level=-73
1348     * tsf=1344626243700373
1349     * flags=[WPA2-PSK-CCMP][WPS][ESS]
1350     * ssid=zuby
1351     * ====
1352     */
1353    private void setScanResults() {
1354        String bssid = "";
1355        int level = 0;
1356        int freq = 0;
1357        long tsf = 0;
1358        String flags = "";
1359        WifiSsid wifiSsid = null;
1360        String scanResults;
1361        String tmpResults;
1362        StringBuffer scanResultsBuf = new StringBuffer();
1363        int sid = 0;
1364
1365        while (true) {
1366            tmpResults = mWifiNative.scanResults(sid);
1367            if (TextUtils.isEmpty(tmpResults)) break;
1368            scanResultsBuf.append(tmpResults);
1369            scanResultsBuf.append("\n");
1370            String[] lines = tmpResults.split("\n");
1371            sid = -1;
1372            for (int i=lines.length - 1; i >= 0; i--) {
1373                if (lines[i].startsWith(END_STR)) {
1374                    break;
1375                } else if (lines[i].startsWith(ID_STR)) {
1376                    try {
1377                        sid = Integer.parseInt(lines[i].substring(ID_STR.length())) + 1;
1378                    } catch (NumberFormatException e) {
1379                        // Nothing to do
1380                    }
1381                    break;
1382                }
1383            }
1384            if (sid == -1) break;
1385        }
1386
1387        scanResults = scanResultsBuf.toString();
1388        if (TextUtils.isEmpty(scanResults)) {
1389           return;
1390        }
1391
1392        synchronized(mScanResultCache) {
1393            mScanResults = new ArrayList<ScanResult>();
1394            String[] lines = scanResults.split("\n");
1395
1396            for (String line : lines) {
1397                if (line.startsWith(BSSID_STR)) {
1398                    bssid = line.substring(BSSID_STR.length());
1399                } else if (line.startsWith(FREQ_STR)) {
1400                    try {
1401                        freq = Integer.parseInt(line.substring(FREQ_STR.length()));
1402                    } catch (NumberFormatException e) {
1403                        freq = 0;
1404                    }
1405                } else if (line.startsWith(LEVEL_STR)) {
1406                    try {
1407                        level = Integer.parseInt(line.substring(LEVEL_STR.length()));
1408                        /* some implementations avoid negative values by adding 256
1409                         * so we need to adjust for that here.
1410                         */
1411                        if (level > 0) level -= 256;
1412                    } catch(NumberFormatException e) {
1413                        level = 0;
1414                    }
1415                } else if (line.startsWith(TSF_STR)) {
1416                    try {
1417                        tsf = Long.parseLong(line.substring(TSF_STR.length()));
1418                    } catch (NumberFormatException e) {
1419                        tsf = 0;
1420                    }
1421                } else if (line.startsWith(FLAGS_STR)) {
1422                    flags = line.substring(FLAGS_STR.length());
1423                } else if (line.startsWith(SSID_STR)) {
1424                    wifiSsid = WifiSsid.createFromAsciiEncoded(
1425                            line.substring(SSID_STR.length()));
1426                } else if (line.startsWith(DELIMITER_STR) || line.startsWith(END_STR)) {
1427                    if (bssid != null) {
1428                        String ssid = (wifiSsid != null) ? wifiSsid.toString() : WifiSsid.NONE;
1429                        String key = bssid + ssid;
1430                        ScanResult scanResult = mScanResultCache.get(key);
1431                        if (scanResult != null) {
1432                            scanResult.level = level;
1433                            scanResult.wifiSsid = wifiSsid;
1434                            // Keep existing API
1435                            scanResult.SSID = (wifiSsid != null) ? wifiSsid.toString() :
1436                                    WifiSsid.NONE;
1437                            scanResult.capabilities = flags;
1438                            scanResult.frequency = freq;
1439                            scanResult.timestamp = tsf;
1440                        } else {
1441                            scanResult =
1442                                new ScanResult(
1443                                        wifiSsid, bssid, flags, level, freq, tsf);
1444                            mScanResultCache.put(key, scanResult);
1445                        }
1446                        mScanResults.add(scanResult);
1447                    }
1448                    bssid = null;
1449                    level = 0;
1450                    freq = 0;
1451                    tsf = 0;
1452                    flags = "";
1453                    wifiSsid = null;
1454                }
1455            }
1456        }
1457    }
1458
1459    /*
1460     * Fetch RSSI and linkspeed on current connection
1461     */
1462    private void fetchRssiAndLinkSpeedNative() {
1463        int newRssi = -1;
1464        int newLinkSpeed = -1;
1465
1466        String signalPoll = mWifiNative.signalPoll();
1467
1468        if (signalPoll != null) {
1469            String[] lines = signalPoll.split("\n");
1470            for (String line : lines) {
1471                String[] prop = line.split("=");
1472                if (prop.length < 2) continue;
1473                try {
1474                    if (prop[0].equals("RSSI")) {
1475                        newRssi = Integer.parseInt(prop[1]);
1476                    } else if (prop[0].equals("LINKSPEED")) {
1477                        newLinkSpeed = Integer.parseInt(prop[1]);
1478                    }
1479                } catch (NumberFormatException e) {
1480                    //Ignore, defaults on rssi and linkspeed are assigned
1481                }
1482            }
1483        }
1484
1485        if (newRssi != -1 && MIN_RSSI < newRssi && newRssi < MAX_RSSI) { // screen out invalid values
1486            /* some implementations avoid negative values by adding 256
1487             * so we need to adjust for that here.
1488             */
1489            if (newRssi > 0) newRssi -= 256;
1490            mWifiInfo.setRssi(newRssi);
1491            /*
1492             * Rather then sending the raw RSSI out every time it
1493             * changes, we precalculate the signal level that would
1494             * be displayed in the status bar, and only send the
1495             * broadcast if that much more coarse-grained number
1496             * changes. This cuts down greatly on the number of
1497             * broadcasts, at the cost of not informing others
1498             * interested in RSSI of all the changes in signal
1499             * level.
1500             */
1501            int newSignalLevel = WifiManager.calculateSignalLevel(newRssi, WifiManager.RSSI_LEVELS);
1502            if (newSignalLevel != mLastSignalLevel) {
1503                sendRssiChangeBroadcast(newRssi);
1504            }
1505            mLastSignalLevel = newSignalLevel;
1506        } else {
1507            mWifiInfo.setRssi(MIN_RSSI);
1508        }
1509
1510        if (newLinkSpeed != -1) {
1511            mWifiInfo.setLinkSpeed(newLinkSpeed);
1512        }
1513    }
1514
1515    /*
1516     * Fetch TX packet counters on current connection
1517     */
1518    private void fetchPktcntNative(RssiPacketCountInfo info) {
1519        String pktcntPoll = mWifiNative.pktcntPoll();
1520
1521        if (pktcntPoll != null) {
1522            String[] lines = pktcntPoll.split("\n");
1523            for (String line : lines) {
1524                String[] prop = line.split("=");
1525                if (prop.length < 2) continue;
1526                try {
1527                    if (prop[0].equals("TXGOOD")) {
1528                        info.txgood = Integer.parseInt(prop[1]);
1529                    } else if (prop[0].equals("TXBAD")) {
1530                        info.txbad = Integer.parseInt(prop[1]);
1531                    }
1532                } catch (NumberFormatException e) {
1533                    //Ignore
1534                }
1535            }
1536        }
1537    }
1538
1539    private void configureLinkProperties() {
1540        if (mWifiConfigStore.isUsingStaticIp(mLastNetworkId)) {
1541            mLinkProperties = mWifiConfigStore.getLinkProperties(mLastNetworkId);
1542        } else {
1543            synchronized (mDhcpResultsLock) {
1544                if ((mDhcpResults != null) && (mDhcpResults.linkProperties != null)) {
1545                    mLinkProperties = mDhcpResults.linkProperties;
1546                }
1547            }
1548            mLinkProperties.setHttpProxy(mWifiConfigStore.getProxyProperties(mLastNetworkId));
1549        }
1550        mLinkProperties.setInterfaceName(mInterfaceName);
1551        if (DBG) log("netId=" + mLastNetworkId  + " Link configured: " + mLinkProperties);
1552    }
1553
1554    private int getMaxDhcpRetries() {
1555        return Settings.Global.getInt(mContext.getContentResolver(),
1556                                      Settings.Global.WIFI_MAX_DHCP_RETRY_COUNT,
1557                                      DEFAULT_MAX_DHCP_RETRIES);
1558    }
1559
1560    private void sendScanResultsAvailableBroadcast() {
1561        Intent intent = new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
1562        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1563        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
1564    }
1565
1566    private void sendRssiChangeBroadcast(final int newRssi) {
1567        Intent intent = new Intent(WifiManager.RSSI_CHANGED_ACTION);
1568        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1569        intent.putExtra(WifiManager.EXTRA_NEW_RSSI, newRssi);
1570        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
1571    }
1572
1573    private void sendNetworkStateChangeBroadcast(String bssid) {
1574        Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
1575        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1576        intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, new NetworkInfo(mNetworkInfo));
1577        intent.putExtra(WifiManager.EXTRA_LINK_PROPERTIES, new LinkProperties (mLinkProperties));
1578        if (bssid != null)
1579            intent.putExtra(WifiManager.EXTRA_BSSID, bssid);
1580        if (mNetworkInfo.getDetailedState() == DetailedState.VERIFYING_POOR_LINK ||
1581                mNetworkInfo.getDetailedState() == DetailedState.CONNECTED) {
1582            intent.putExtra(WifiManager.EXTRA_WIFI_INFO, new WifiInfo(mWifiInfo));
1583        }
1584        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
1585    }
1586
1587    private void sendLinkConfigurationChangedBroadcast() {
1588        Intent intent = new Intent(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION);
1589        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1590        intent.putExtra(WifiManager.EXTRA_LINK_PROPERTIES, new LinkProperties(mLinkProperties));
1591        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
1592    }
1593
1594    private void sendSupplicantConnectionChangedBroadcast(boolean connected) {
1595        Intent intent = new Intent(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
1596        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1597        intent.putExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, connected);
1598        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
1599    }
1600
1601    /**
1602     * Record the detailed state of a network.
1603     * @param state the new {@code DetailedState}
1604     */
1605    private void setNetworkDetailedState(NetworkInfo.DetailedState state) {
1606        if (DBG) {
1607            log("setDetailed state, old ="
1608                    + mNetworkInfo.getDetailedState() + " and new state=" + state);
1609        }
1610
1611        if (state != mNetworkInfo.getDetailedState()) {
1612            mNetworkInfo.setDetailedState(state, null, mWifiInfo.getSSID());
1613        }
1614    }
1615
1616    private DetailedState getNetworkDetailedState() {
1617        return mNetworkInfo.getDetailedState();
1618    }
1619
1620
1621    private SupplicantState handleSupplicantStateChange(Message message) {
1622        StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
1623        SupplicantState state = stateChangeResult.state;
1624        // Supplicant state change
1625        // [31-13] Reserved for future use
1626        // [8 - 0] Supplicant state (as defined in SupplicantState.java)
1627        // 50023 supplicant_state_changed (custom|1|5)
1628        mWifiInfo.setSupplicantState(state);
1629        // Network id is only valid when we start connecting
1630        if (SupplicantState.isConnecting(state)) {
1631            mWifiInfo.setNetworkId(stateChangeResult.networkId);
1632        } else {
1633            mWifiInfo.setNetworkId(WifiConfiguration.INVALID_NETWORK_ID);
1634        }
1635
1636        mWifiInfo.setBSSID(stateChangeResult.BSSID);
1637        mWifiInfo.setSSID(stateChangeResult.wifiSsid);
1638
1639        mSupplicantStateTracker.sendMessage(Message.obtain(message));
1640
1641        return state;
1642    }
1643
1644    /**
1645     * Resets the Wi-Fi Connections by clearing any state, resetting any sockets
1646     * using the interface, stopping DHCP & disabling interface
1647     */
1648    private void handleNetworkDisconnect() {
1649        if (DBG) log("Stopping DHCP and clearing IP");
1650
1651        /*
1652         * stop DHCP
1653         */
1654        if (mDhcpStateMachine != null) {
1655            /* In case we were in middle of DHCP operation
1656               restore back powermode */
1657            handlePostDhcpSetup();
1658            mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_STOP_DHCP);
1659        }
1660
1661        try {
1662            mNwService.clearInterfaceAddresses(mInterfaceName);
1663            mNwService.disableIpv6(mInterfaceName);
1664        } catch (Exception e) {
1665            loge("Failed to clear addresses or disable ipv6" + e);
1666        }
1667
1668        /* Reset data structures */
1669        mWifiInfo.setInetAddress(null);
1670        mWifiInfo.setBSSID(null);
1671        mWifiInfo.setSSID(null);
1672        mWifiInfo.setNetworkId(WifiConfiguration.INVALID_NETWORK_ID);
1673        mWifiInfo.setRssi(MIN_RSSI);
1674        mWifiInfo.setLinkSpeed(-1);
1675        mWifiInfo.setMeteredHint(false);
1676
1677        setNetworkDetailedState(DetailedState.DISCONNECTED);
1678        mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.DISCONNECTED);
1679
1680        /* Clear network properties */
1681        mLinkProperties.clear();
1682
1683        /* send event to CM & network change broadcast */
1684        sendNetworkStateChangeBroadcast(mLastBssid);
1685
1686        /* Clear IP settings if the network used DHCP */
1687        if (!mWifiConfigStore.isUsingStaticIp(mLastNetworkId)) {
1688            mWifiConfigStore.clearLinkProperties(mLastNetworkId);
1689        }
1690
1691        mLastBssid= null;
1692        mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
1693    }
1694
1695    private void handleSupplicantConnectionLoss() {
1696        /* Socket connection can be lost when we do a graceful shutdown
1697        * or when the driver is hung. Ensure supplicant is stopped here.
1698        */
1699        mWifiNative.killSupplicant(mP2pSupported);
1700        mWifiNative.closeSupplicantConnection();
1701        sendSupplicantConnectionChangedBroadcast(false);
1702        setWifiState(WIFI_STATE_DISABLED);
1703    }
1704
1705    void handlePreDhcpSetup() {
1706        if (!mBluetoothConnectionActive) {
1707            /*
1708             * There are problems setting the Wi-Fi driver's power
1709             * mode to active when bluetooth coexistence mode is
1710             * enabled or sense.
1711             * <p>
1712             * We set Wi-Fi to active mode when
1713             * obtaining an IP address because we've found
1714             * compatibility issues with some routers with low power
1715             * mode.
1716             * <p>
1717             * In order for this active power mode to properly be set,
1718             * we disable coexistence mode until we're done with
1719             * obtaining an IP address.  One exception is if we
1720             * are currently connected to a headset, since disabling
1721             * coexistence would interrupt that connection.
1722             */
1723            // Disable the coexistence mode
1724            mWifiNative.setBluetoothCoexistenceMode(
1725                    mWifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED);
1726        }
1727
1728        /* Disable power save and suspend optimizations during DHCP */
1729        // Note: The order here is important for now. Brcm driver changes
1730        // power settings when we control suspend mode optimizations.
1731        // TODO: Remove this comment when the driver is fixed.
1732        setSuspendOptimizationsNative(SUSPEND_DUE_TO_DHCP, false);
1733        mWifiNative.setPowerSave(false);
1734    }
1735
1736
1737    void handlePostDhcpSetup() {
1738        /* Restore power save and suspend optimizations */
1739        setSuspendOptimizationsNative(SUSPEND_DUE_TO_DHCP, true);
1740        mWifiNative.setPowerSave(true);
1741
1742        // Set the coexistence mode back to its default value
1743        mWifiNative.setBluetoothCoexistenceMode(
1744                mWifiNative.BLUETOOTH_COEXISTENCE_MODE_SENSE);
1745    }
1746
1747    private void handleSuccessfulIpConfiguration(DhcpResults dhcpResults) {
1748        mLastSignalLevel = -1; // force update of signal strength
1749        mReconnectCount = 0; //Reset IP failure tracking
1750        synchronized (mDhcpResultsLock) {
1751            mDhcpResults = dhcpResults;
1752        }
1753        LinkProperties linkProperties = dhcpResults.linkProperties;
1754        mWifiConfigStore.setLinkProperties(mLastNetworkId, linkProperties);
1755        InetAddress addr = null;
1756        Iterator<InetAddress> addrs = linkProperties.getAddresses().iterator();
1757        if (addrs.hasNext()) {
1758            addr = addrs.next();
1759        }
1760        mWifiInfo.setInetAddress(addr);
1761        mWifiInfo.setMeteredHint(dhcpResults.hasMeteredHint());
1762        if (getNetworkDetailedState() == DetailedState.CONNECTED) {
1763            //DHCP renewal in connected state
1764            linkProperties.setHttpProxy(mWifiConfigStore.getProxyProperties(mLastNetworkId));
1765            if (!linkProperties.equals(mLinkProperties)) {
1766                if (DBG) {
1767                    log("Link configuration changed for netId: " + mLastNetworkId
1768                            + " old: " + mLinkProperties + "new: " + linkProperties);
1769                }
1770                mLinkProperties = linkProperties;
1771                sendLinkConfigurationChangedBroadcast();
1772            }
1773        } else {
1774            configureLinkProperties();
1775        }
1776    }
1777
1778    private void handleFailedIpConfiguration() {
1779        loge("IP configuration failed");
1780
1781        mWifiInfo.setInetAddress(null);
1782        mWifiInfo.setMeteredHint(false);
1783        /**
1784         * If we've exceeded the maximum number of retries for DHCP
1785         * to a given network, disable the network
1786         */
1787        int maxRetries = getMaxDhcpRetries();
1788        // maxRetries == 0 means keep trying forever
1789        if (maxRetries > 0 && ++mReconnectCount > maxRetries) {
1790            loge("Failed " +
1791                    mReconnectCount + " times, Disabling " + mLastNetworkId);
1792            mWifiConfigStore.disableNetwork(mLastNetworkId,
1793                    WifiConfiguration.DISABLED_DHCP_FAILURE);
1794            mReconnectCount = 0;
1795        }
1796
1797        /* DHCP times out after about 30 seconds, we do a
1798         * disconnect and an immediate reconnect to try again
1799         */
1800        mWifiNative.disconnect();
1801        mWifiNative.reconnect();
1802    }
1803
1804    /* Current design is to not set the config on a running hostapd but instead
1805     * stop and start tethering when user changes config on a running access point
1806     *
1807     * TODO: Add control channel setup through hostapd that allows changing config
1808     * on a running daemon
1809     */
1810    private void startSoftApWithConfig(final WifiConfiguration config) {
1811        // start hostapd on a seperate thread
1812        new Thread(new Runnable() {
1813            public void run() {
1814                try {
1815                    mNwService.startAccessPoint(config, mInterfaceName);
1816                } catch (Exception e) {
1817                    loge("Exception in softap start " + e);
1818                    try {
1819                        mNwService.stopAccessPoint(mInterfaceName);
1820                        mNwService.startAccessPoint(config, mInterfaceName);
1821                    } catch (Exception e1) {
1822                        loge("Exception in softap re-start " + e1);
1823                        sendMessage(CMD_START_AP_FAILURE);
1824                        return;
1825                    }
1826                }
1827                if (DBG) log("Soft AP start successful");
1828                sendMessage(CMD_START_AP_SUCCESS);
1829            }
1830        }).start();
1831    }
1832
1833    /********************************************************
1834     * HSM states
1835     *******************************************************/
1836
1837    class DefaultState extends State {
1838        @Override
1839        public boolean processMessage(Message message) {
1840            switch (message.what) {
1841                case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
1842                    if (message.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
1843                        mWifiP2pChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
1844                    } else {
1845                        loge("WifiP2pService connection failure, error=" + message.arg1);
1846                    }
1847                    break;
1848                case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
1849                    loge("WifiP2pService channel lost, message.arg1 =" + message.arg1);
1850                    //TODO: Re-establish connection to state machine after a delay
1851                    //mWifiP2pChannel.connect(mContext, getHandler(), mWifiP2pManager.getMessenger());
1852                    break;
1853                case CMD_BLUETOOTH_ADAPTER_STATE_CHANGE:
1854                    mBluetoothConnectionActive = (message.arg1 !=
1855                            BluetoothAdapter.STATE_DISCONNECTED);
1856                    break;
1857                    /* Synchronous call returns */
1858                case CMD_PING_SUPPLICANT:
1859                case CMD_ENABLE_NETWORK:
1860                case CMD_ADD_OR_UPDATE_NETWORK:
1861                case CMD_REMOVE_NETWORK:
1862                case CMD_SAVE_CONFIG:
1863                    replyToMessage(message, message.what, FAILURE);
1864                    break;
1865                case CMD_GET_CONFIGURED_NETWORKS:
1866                    replyToMessage(message, message.what, (List<WifiConfiguration>) null);
1867                    break;
1868                case CMD_ENABLE_RSSI_POLL:
1869                    mEnableRssiPolling = (message.arg1 == 1);
1870                    break;
1871                case CMD_ENABLE_BACKGROUND_SCAN:
1872                    mEnableBackgroundScan = (message.arg1 == 1);
1873                    break;
1874                case CMD_SET_HIGH_PERF_MODE:
1875                    if (message.arg1 == 1) {
1876                        setSuspendOptimizations(SUSPEND_DUE_TO_HIGH_PERF, false);
1877                    } else {
1878                        setSuspendOptimizations(SUSPEND_DUE_TO_HIGH_PERF, true);
1879                    }
1880                    break;
1881                    /* Discard */
1882                case CMD_START_SUPPLICANT:
1883                case CMD_STOP_SUPPLICANT:
1884                case CMD_STOP_SUPPLICANT_FAILED:
1885                case CMD_START_DRIVER:
1886                case CMD_STOP_DRIVER:
1887                case CMD_DELAYED_STOP_DRIVER:
1888                case CMD_DRIVER_START_TIMED_OUT:
1889                case CMD_CAPTIVE_CHECK_COMPLETE:
1890                case CMD_START_AP:
1891                case CMD_START_AP_SUCCESS:
1892                case CMD_START_AP_FAILURE:
1893                case CMD_STOP_AP:
1894                case CMD_TETHER_STATE_CHANGE:
1895                case CMD_TETHER_NOTIFICATION_TIMED_OUT:
1896                case CMD_START_SCAN:
1897                case CMD_DISCONNECT:
1898                case CMD_RECONNECT:
1899                case CMD_REASSOCIATE:
1900                case WifiMonitor.SUP_CONNECTION_EVENT:
1901                case WifiMonitor.SUP_DISCONNECTION_EVENT:
1902                case WifiMonitor.NETWORK_CONNECTION_EVENT:
1903                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
1904                case WifiMonitor.SCAN_RESULTS_EVENT:
1905                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
1906                case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
1907                case WifiMonitor.WPS_OVERLAP_EVENT:
1908                case CMD_BLACKLIST_NETWORK:
1909                case CMD_CLEAR_BLACKLIST:
1910                case CMD_SET_OPERATIONAL_MODE:
1911                case CMD_SET_COUNTRY_CODE:
1912                case CMD_SET_FREQUENCY_BAND:
1913                case CMD_RSSI_POLL:
1914                case CMD_ENABLE_ALL_NETWORKS:
1915                case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
1916                case DhcpStateMachine.CMD_POST_DHCP_ACTION:
1917                /* Handled by WifiApConfigStore */
1918                case CMD_SET_AP_CONFIG:
1919                case CMD_SET_AP_CONFIG_COMPLETED:
1920                case CMD_REQUEST_AP_CONFIG:
1921                case CMD_RESPONSE_AP_CONFIG:
1922                case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
1923                case WifiWatchdogStateMachine.GOOD_LINK_DETECTED:
1924                case CMD_NO_NETWORKS_PERIODIC_SCAN:
1925                case CMD_DISABLE_P2P_RSP:
1926                    break;
1927                case DhcpStateMachine.CMD_ON_QUIT:
1928                    mDhcpStateMachine = null;
1929                    break;
1930                case CMD_SET_SUSPEND_OPT_ENABLED:
1931                    if (message.arg1 == 1) {
1932                        mSuspendWakeLock.release();
1933                        setSuspendOptimizations(SUSPEND_DUE_TO_SCREEN, true);
1934                    } else {
1935                        setSuspendOptimizations(SUSPEND_DUE_TO_SCREEN, false);
1936                    }
1937                    break;
1938                case WifiMonitor.DRIVER_HUNG_EVENT:
1939                    setSupplicantRunning(false);
1940                    setSupplicantRunning(true);
1941                    break;
1942                case WifiManager.CONNECT_NETWORK:
1943                    replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
1944                            WifiManager.BUSY);
1945                    break;
1946                case WifiManager.FORGET_NETWORK:
1947                    replyToMessage(message, WifiManager.FORGET_NETWORK_FAILED,
1948                            WifiManager.BUSY);
1949                    break;
1950                case WifiManager.SAVE_NETWORK:
1951                    replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED,
1952                            WifiManager.BUSY);
1953                    break;
1954                case WifiManager.START_WPS:
1955                    replyToMessage(message, WifiManager.WPS_FAILED,
1956                            WifiManager.BUSY);
1957                    break;
1958                case WifiManager.CANCEL_WPS:
1959                    replyToMessage(message, WifiManager.CANCEL_WPS_FAILED,
1960                            WifiManager.BUSY);
1961                    break;
1962                case WifiManager.DISABLE_NETWORK:
1963                    replyToMessage(message, WifiManager.DISABLE_NETWORK_FAILED,
1964                            WifiManager.BUSY);
1965                    break;
1966                case WifiManager.RSSI_PKTCNT_FETCH:
1967                    replyToMessage(message, WifiManager.RSSI_PKTCNT_FETCH_FAILED,
1968                            WifiManager.BUSY);
1969                    break;
1970                case WifiP2pService.P2P_CONNECTION_CHANGED:
1971                    NetworkInfo info = (NetworkInfo) message.obj;
1972                    mP2pConnected.set(info.isConnected());
1973                    break;
1974                case WifiP2pService.DISCONNECT_WIFI_REQUEST:
1975                    mTemporarilyDisconnectWifi = (message.arg1 == 1);
1976                    replyToMessage(message, WifiP2pService.DISCONNECT_WIFI_RESPONSE);
1977                    break;
1978                default:
1979                    loge("Error! unhandled message" + message);
1980                    break;
1981            }
1982            return HANDLED;
1983        }
1984    }
1985
1986    class InitialState extends State {
1987        @Override
1988        public void enter() {
1989            mWifiNative.unloadDriver();
1990
1991            if (mWifiP2pChannel == null) {
1992                mWifiP2pChannel = new AsyncChannel();
1993                mWifiP2pChannel.connect(mContext, getHandler(), mWifiP2pManager.getMessenger());
1994            }
1995
1996            if (mWifiApConfigChannel == null) {
1997                mWifiApConfigChannel = new AsyncChannel();
1998                WifiApConfigStore wifiApConfigStore = WifiApConfigStore.makeWifiApConfigStore(
1999                        mContext, getHandler());
2000                wifiApConfigStore.loadApConfiguration();
2001                mWifiApConfigChannel.connectSync(mContext, getHandler(),
2002                        wifiApConfigStore.getMessenger());
2003            }
2004        }
2005        @Override
2006        public boolean processMessage(Message message) {
2007            switch (message.what) {
2008                case CMD_START_SUPPLICANT:
2009                    if (mWifiNative.loadDriver()) {
2010                        try {
2011                            mNwService.wifiFirmwareReload(mInterfaceName, "STA");
2012                        } catch (Exception e) {
2013                            loge("Failed to reload STA firmware " + e);
2014                            // continue
2015                        }
2016
2017                        try {
2018                            // A runtime crash can leave the interface up and
2019                            // this affects connectivity when supplicant starts up.
2020                            // Ensure interface is down before a supplicant start.
2021                            mNwService.setInterfaceDown(mInterfaceName);
2022                            // Set privacy extensions
2023                            mNwService.setInterfaceIpv6PrivacyExtensions(mInterfaceName, true);
2024
2025                           // IPv6 is enabled only as long as access point is connected since:
2026                           // - IPv6 addresses and routes stick around after disconnection
2027                           // - kernel is unaware when connected and fails to start IPv6 negotiation
2028                           // - kernel can start autoconfiguration when 802.1x is not complete
2029                            mNwService.disableIpv6(mInterfaceName);
2030                        } catch (RemoteException re) {
2031                            loge("Unable to change interface settings: " + re);
2032                        } catch (IllegalStateException ie) {
2033                            loge("Unable to change interface settings: " + ie);
2034                        }
2035
2036                       /* Stop a running supplicant after a runtime restart
2037                        * Avoids issues with drivers that do not handle interface down
2038                        * on a running supplicant properly.
2039                        */
2040                        mWifiNative.killSupplicant(mP2pSupported);
2041                        if(mWifiNative.startSupplicant(mP2pSupported)) {
2042                            setWifiState(WIFI_STATE_ENABLING);
2043                            if (DBG) log("Supplicant start successful");
2044                            mWifiMonitor.startMonitoring();
2045                            transitionTo(mSupplicantStartingState);
2046                        } else {
2047                            loge("Failed to start supplicant!");
2048                        }
2049                    } else {
2050                        loge("Failed to load driver");
2051                    }
2052                    break;
2053                case CMD_START_AP:
2054                    if (mWifiNative.loadDriver()) {
2055                        setWifiApState(WIFI_AP_STATE_ENABLING);
2056                        transitionTo(mSoftApStartingState);
2057                    } else {
2058                        loge("Failed to load driver for softap");
2059                    }
2060                default:
2061                    return NOT_HANDLED;
2062            }
2063            return HANDLED;
2064        }
2065    }
2066
2067    class SupplicantStartingState extends State {
2068        private void initializeWpsDetails() {
2069            String detail;
2070            detail = SystemProperties.get("ro.product.name", "");
2071            if (!mWifiNative.setDeviceName(detail)) {
2072                loge("Failed to set device name " +  detail);
2073            }
2074            detail = SystemProperties.get("ro.product.manufacturer", "");
2075            if (!mWifiNative.setManufacturer(detail)) {
2076                loge("Failed to set manufacturer " + detail);
2077            }
2078            detail = SystemProperties.get("ro.product.model", "");
2079            if (!mWifiNative.setModelName(detail)) {
2080                loge("Failed to set model name " + detail);
2081            }
2082            detail = SystemProperties.get("ro.product.model", "");
2083            if (!mWifiNative.setModelNumber(detail)) {
2084                loge("Failed to set model number " + detail);
2085            }
2086            detail = SystemProperties.get("ro.serialno", "");
2087            if (!mWifiNative.setSerialNumber(detail)) {
2088                loge("Failed to set serial number " + detail);
2089            }
2090            if (!mWifiNative.setConfigMethods("physical_display virtual_push_button")) {
2091                loge("Failed to set WPS config methods");
2092            }
2093            if (!mWifiNative.setDeviceType(mPrimaryDeviceType)) {
2094                loge("Failed to set primary device type " + mPrimaryDeviceType);
2095            }
2096        }
2097
2098        @Override
2099        public boolean processMessage(Message message) {
2100            switch(message.what) {
2101                case WifiMonitor.SUP_CONNECTION_EVENT:
2102                    if (DBG) log("Supplicant connection established");
2103                    setWifiState(WIFI_STATE_ENABLED);
2104                    mSupplicantRestartCount = 0;
2105                    /* Reset the supplicant state to indicate the supplicant
2106                     * state is not known at this time */
2107                    mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
2108                    /* Initialize data structures */
2109                    mLastBssid = null;
2110                    mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
2111                    mLastSignalLevel = -1;
2112
2113                    mWifiInfo.setMacAddress(mWifiNative.getMacAddress());
2114                    mWifiConfigStore.initialize();
2115                    initializeWpsDetails();
2116
2117                    sendSupplicantConnectionChangedBroadcast(true);
2118                    transitionTo(mDriverStartedState);
2119                    break;
2120                case WifiMonitor.SUP_DISCONNECTION_EVENT:
2121                    if (++mSupplicantRestartCount <= SUPPLICANT_RESTART_TRIES) {
2122                        loge("Failed to setup control channel, restart supplicant");
2123                        mWifiNative.killSupplicant(mP2pSupported);
2124                        transitionTo(mInitialState);
2125                        sendMessageDelayed(CMD_START_SUPPLICANT, SUPPLICANT_RESTART_INTERVAL_MSECS);
2126                    } else {
2127                        loge("Failed " + mSupplicantRestartCount +
2128                                " times to start supplicant, unload driver");
2129                        mSupplicantRestartCount = 0;
2130                        setWifiState(WIFI_STATE_UNKNOWN);
2131                        transitionTo(mInitialState);
2132                    }
2133                    break;
2134                case CMD_START_SUPPLICANT:
2135                case CMD_STOP_SUPPLICANT:
2136                case CMD_START_AP:
2137                case CMD_STOP_AP:
2138                case CMD_START_DRIVER:
2139                case CMD_STOP_DRIVER:
2140                case CMD_SET_OPERATIONAL_MODE:
2141                case CMD_SET_COUNTRY_CODE:
2142                case CMD_SET_FREQUENCY_BAND:
2143                case CMD_START_PACKET_FILTERING:
2144                case CMD_STOP_PACKET_FILTERING:
2145                    deferMessage(message);
2146                    break;
2147                default:
2148                    return NOT_HANDLED;
2149            }
2150            return HANDLED;
2151        }
2152    }
2153
2154    class SupplicantStartedState extends State {
2155        @Override
2156        public void enter() {
2157            /* Wifi is available as long as we have a connection to supplicant */
2158            mNetworkInfo.setIsAvailable(true);
2159
2160            int defaultInterval = mContext.getResources().getInteger(
2161                    R.integer.config_wifi_supplicant_scan_interval);
2162
2163            mSupplicantScanIntervalMs = Settings.Global.getLong(mContext.getContentResolver(),
2164                    Settings.Global.WIFI_SUPPLICANT_SCAN_INTERVAL_MS,
2165                    defaultInterval);
2166
2167            mWifiNative.setScanInterval((int)mSupplicantScanIntervalMs / 1000);
2168        }
2169        @Override
2170        public boolean processMessage(Message message) {
2171            switch(message.what) {
2172                case CMD_STOP_SUPPLICANT:   /* Supplicant stopped by user */
2173                    if (mP2pSupported) {
2174                        transitionTo(mWaitForP2pDisableState);
2175                    } else {
2176                        transitionTo(mSupplicantStoppingState);
2177                    }
2178                    break;
2179                case WifiMonitor.SUP_DISCONNECTION_EVENT:  /* Supplicant connection lost */
2180                    loge("Connection lost, restart supplicant");
2181                    handleSupplicantConnectionLoss();
2182                    handleNetworkDisconnect();
2183                    mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
2184                    if (mP2pSupported) {
2185                        transitionTo(mWaitForP2pDisableState);
2186                    } else {
2187                        transitionTo(mInitialState);
2188                    }
2189                    sendMessageDelayed(CMD_START_SUPPLICANT, SUPPLICANT_RESTART_INTERVAL_MSECS);
2190                    break;
2191                case WifiMonitor.SCAN_RESULTS_EVENT:
2192                    setScanResults();
2193                    sendScanResultsAvailableBroadcast();
2194                    mScanResultIsPending = false;
2195                    break;
2196                case CMD_PING_SUPPLICANT:
2197                    boolean ok = mWifiNative.ping();
2198                    replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
2199                    break;
2200                    /* Cannot start soft AP while in client mode */
2201                case CMD_START_AP:
2202                    loge("Failed to start soft AP with a running supplicant");
2203                    setWifiApState(WIFI_AP_STATE_FAILED);
2204                    break;
2205                case CMD_SET_OPERATIONAL_MODE:
2206                    mOperationalMode = message.arg1;
2207                    break;
2208                default:
2209                    return NOT_HANDLED;
2210            }
2211            return HANDLED;
2212        }
2213
2214        @Override
2215        public void exit() {
2216            mNetworkInfo.setIsAvailable(false);
2217        }
2218    }
2219
2220    class SupplicantStoppingState extends State {
2221        @Override
2222        public void enter() {
2223            /* Send any reset commands to supplicant before shutting it down */
2224            handleNetworkDisconnect();
2225            if (mDhcpStateMachine != null) {
2226                mDhcpStateMachine.doQuit();
2227            }
2228
2229            if (DBG) log("stopping supplicant");
2230            if (!mWifiNative.stopSupplicant()) {
2231                loge("Failed to stop supplicant");
2232            }
2233
2234            /* Send ourselves a delayed message to indicate failure after a wait time */
2235            sendMessageDelayed(obtainMessage(CMD_STOP_SUPPLICANT_FAILED,
2236                    ++mSupplicantStopFailureToken, 0), SUPPLICANT_RESTART_INTERVAL_MSECS);
2237            setWifiState(WIFI_STATE_DISABLING);
2238            mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
2239        }
2240        @Override
2241        public boolean processMessage(Message message) {
2242            switch(message.what) {
2243                case WifiMonitor.SUP_CONNECTION_EVENT:
2244                    loge("Supplicant connection received while stopping");
2245                    break;
2246                case WifiMonitor.SUP_DISCONNECTION_EVENT:
2247                    if (DBG) log("Supplicant connection lost");
2248                    handleSupplicantConnectionLoss();
2249                    transitionTo(mInitialState);
2250                    break;
2251                case CMD_STOP_SUPPLICANT_FAILED:
2252                    if (message.arg1 == mSupplicantStopFailureToken) {
2253                        loge("Timed out on a supplicant stop, kill and proceed");
2254                        handleSupplicantConnectionLoss();
2255                        transitionTo(mInitialState);
2256                    }
2257                    break;
2258                case CMD_START_SUPPLICANT:
2259                case CMD_STOP_SUPPLICANT:
2260                case CMD_START_AP:
2261                case CMD_STOP_AP:
2262                case CMD_START_DRIVER:
2263                case CMD_STOP_DRIVER:
2264                case CMD_SET_OPERATIONAL_MODE:
2265                case CMD_SET_COUNTRY_CODE:
2266                case CMD_SET_FREQUENCY_BAND:
2267                case CMD_START_PACKET_FILTERING:
2268                case CMD_STOP_PACKET_FILTERING:
2269                    deferMessage(message);
2270                    break;
2271                default:
2272                    return NOT_HANDLED;
2273            }
2274            return HANDLED;
2275        }
2276    }
2277
2278    class DriverStartingState extends State {
2279        private int mTries;
2280        @Override
2281        public void enter() {
2282            mTries = 1;
2283            /* Send ourselves a delayed message to start driver a second time */
2284            sendMessageDelayed(obtainMessage(CMD_DRIVER_START_TIMED_OUT,
2285                        ++mDriverStartToken, 0), DRIVER_START_TIME_OUT_MSECS);
2286        }
2287        @Override
2288        public boolean processMessage(Message message) {
2289            switch(message.what) {
2290               case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
2291                    SupplicantState state = handleSupplicantStateChange(message);
2292                    /* If suplicant is exiting out of INTERFACE_DISABLED state into
2293                     * a state that indicates driver has started, it is ready to
2294                     * receive driver commands
2295                     */
2296                    if (SupplicantState.isDriverActive(state)) {
2297                        transitionTo(mDriverStartedState);
2298                    }
2299                    break;
2300                case CMD_DRIVER_START_TIMED_OUT:
2301                    if (message.arg1 == mDriverStartToken) {
2302                        if (mTries >= 2) {
2303                            loge("Failed to start driver after " + mTries);
2304                            transitionTo(mDriverStoppedState);
2305                        } else {
2306                            loge("Driver start failed, retrying");
2307                            mWakeLock.acquire();
2308                            mWifiNative.startDriver();
2309                            mWakeLock.release();
2310
2311                            ++mTries;
2312                            /* Send ourselves a delayed message to start driver again */
2313                            sendMessageDelayed(obtainMessage(CMD_DRIVER_START_TIMED_OUT,
2314                                        ++mDriverStartToken, 0), DRIVER_START_TIME_OUT_MSECS);
2315                        }
2316                    }
2317                    break;
2318                    /* Queue driver commands & connection events */
2319                case CMD_START_DRIVER:
2320                case CMD_STOP_DRIVER:
2321                case WifiMonitor.NETWORK_CONNECTION_EVENT:
2322                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
2323                case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
2324                case WifiMonitor.WPS_OVERLAP_EVENT:
2325                case CMD_SET_COUNTRY_CODE:
2326                case CMD_SET_FREQUENCY_BAND:
2327                case CMD_START_PACKET_FILTERING:
2328                case CMD_STOP_PACKET_FILTERING:
2329                case CMD_START_SCAN:
2330                case CMD_DISCONNECT:
2331                case CMD_REASSOCIATE:
2332                case CMD_RECONNECT:
2333                    deferMessage(message);
2334                    break;
2335                default:
2336                    return NOT_HANDLED;
2337            }
2338            return HANDLED;
2339        }
2340    }
2341
2342    class DriverStartedState extends State {
2343        @Override
2344        public void enter() {
2345            mIsRunning = true;
2346            mInDelayedStop = false;
2347            updateBatteryWorkSource(null);
2348
2349            /**
2350             * Enable bluetooth coexistence scan mode when bluetooth connection is active.
2351             * When this mode is on, some of the low-level scan parameters used by the
2352             * driver are changed to reduce interference with bluetooth
2353             */
2354            mWifiNative.setBluetoothCoexistenceScanMode(mBluetoothConnectionActive);
2355            /* set country code */
2356            setCountryCode();
2357            /* set frequency band of operation */
2358            setFrequencyBand();
2359            /* initialize network state */
2360            setNetworkDetailedState(DetailedState.DISCONNECTED);
2361
2362            /* Remove any filtering on Multicast v6 at start */
2363            mWifiNative.stopFilteringMulticastV6Packets();
2364
2365            /* Reset Multicast v4 filtering state */
2366            if (mFilteringMulticastV4Packets.get()) {
2367                mWifiNative.startFilteringMulticastV4Packets();
2368            } else {
2369                mWifiNative.stopFilteringMulticastV4Packets();
2370            }
2371
2372            if (mOperationalMode != CONNECT_MODE) {
2373                mWifiNative.disconnect();
2374                transitionTo(mScanModeState);
2375            } else {
2376                mWifiNative.reconnect();
2377                // Status pulls in the current supplicant state and network connection state
2378                // events over the monitor connection. This helps framework sync up with
2379                // current supplicant state
2380                mWifiNative.status();
2381                transitionTo(mDisconnectedState);
2382            }
2383
2384            // We may have missed screen update at boot
2385            if (mScreenBroadcastReceived.get() == false) {
2386                PowerManager powerManager = (PowerManager)mContext.getSystemService(
2387                        Context.POWER_SERVICE);
2388                handleScreenStateChanged(powerManager.isScreenOn());
2389            } else {
2390                // Set the right suspend mode settings
2391                mWifiNative.setSuspendOptimizations(mSuspendOptNeedsDisabled == 0
2392                        && mUserWantsSuspendOpt.get());
2393            }
2394            mWifiNative.setPowerSave(true);
2395
2396            if (mP2pSupported) mWifiP2pChannel.sendMessage(WifiStateMachine.CMD_ENABLE_P2P);
2397        }
2398        @Override
2399        public boolean processMessage(Message message) {
2400            switch(message.what) {
2401                case CMD_START_SCAN:
2402                    startScanNative(WifiNative.SCAN_WITH_CONNECTION_SETUP);
2403                    break;
2404                case CMD_SET_COUNTRY_CODE:
2405                    String country = (String) message.obj;
2406                    if (DBG) log("set country code " + country);
2407                    if (!mWifiNative.setCountryCode(country.toUpperCase())) {
2408                        loge("Failed to set country code " + country);
2409                    }
2410                    break;
2411                case CMD_SET_FREQUENCY_BAND:
2412                    int band =  message.arg1;
2413                    if (DBG) log("set frequency band " + band);
2414                    if (mWifiNative.setBand(band)) {
2415                        mFrequencyBand.set(band);
2416                        //Fetch the latest scan results when frequency band is set
2417                        startScanNative(WifiNative.SCAN_WITH_CONNECTION_SETUP);
2418                    } else {
2419                        loge("Failed to set frequency band " + band);
2420                    }
2421                    break;
2422                case CMD_BLUETOOTH_ADAPTER_STATE_CHANGE:
2423                    mBluetoothConnectionActive = (message.arg1 !=
2424                            BluetoothAdapter.STATE_DISCONNECTED);
2425                    mWifiNative.setBluetoothCoexistenceScanMode(mBluetoothConnectionActive);
2426                    break;
2427                case CMD_STOP_DRIVER:
2428                    int mode = message.arg1;
2429
2430                    /* Already doing a delayed stop */
2431                    if (mInDelayedStop) {
2432                        if (DBG) log("Already in delayed stop");
2433                        break;
2434                    }
2435                    mInDelayedStop = true;
2436                    mDelayedStopCounter++;
2437                    if (DBG) log("Delayed stop message " + mDelayedStopCounter);
2438
2439                    /* send regular delayed shut down */
2440                    Intent driverStopIntent = new Intent(ACTION_DELAYED_DRIVER_STOP, null);
2441                    driverStopIntent.putExtra(DELAYED_STOP_COUNTER, mDelayedStopCounter);
2442                    mDriverStopIntent = PendingIntent.getBroadcast(mContext,
2443                            DRIVER_STOP_REQUEST, driverStopIntent,
2444                            PendingIntent.FLAG_UPDATE_CURRENT);
2445
2446                    mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()
2447                            + mDriverStopDelayMs, mDriverStopIntent);
2448                    break;
2449                case CMD_START_DRIVER:
2450                    if (mInDelayedStop) {
2451                        mInDelayedStop = false;
2452                        mDelayedStopCounter++;
2453                        mAlarmManager.cancel(mDriverStopIntent);
2454                        if (DBG) log("Delayed stop ignored due to start");
2455                    }
2456                    break;
2457                case CMD_DELAYED_STOP_DRIVER:
2458                    if (DBG) log("delayed stop " + message.arg1 + " " + mDelayedStopCounter);
2459                    if (message.arg1 != mDelayedStopCounter) break;
2460                    if (getCurrentState() != mDisconnectedState) {
2461                        mWifiNative.disconnect();
2462                        handleNetworkDisconnect();
2463                    }
2464                    mWakeLock.acquire();
2465                    mWifiNative.stopDriver();
2466                    mWakeLock.release();
2467                    if (mP2pSupported) {
2468                        transitionTo(mWaitForP2pDisableState);
2469                    } else {
2470                        transitionTo(mDriverStoppingState);
2471                    }
2472                    break;
2473                case CMD_START_PACKET_FILTERING:
2474                    if (message.arg1 == MULTICAST_V6) {
2475                        mWifiNative.startFilteringMulticastV6Packets();
2476                    } else if (message.arg1 == MULTICAST_V4) {
2477                        mWifiNative.startFilteringMulticastV4Packets();
2478                    } else {
2479                        loge("Illegal arugments to CMD_START_PACKET_FILTERING");
2480                    }
2481                    break;
2482                case CMD_STOP_PACKET_FILTERING:
2483                    if (message.arg1 == MULTICAST_V6) {
2484                        mWifiNative.stopFilteringMulticastV6Packets();
2485                    } else if (message.arg1 == MULTICAST_V4) {
2486                        mWifiNative.stopFilteringMulticastV4Packets();
2487                    } else {
2488                        loge("Illegal arugments to CMD_STOP_PACKET_FILTERING");
2489                    }
2490                    break;
2491                case CMD_SET_SUSPEND_OPT_ENABLED:
2492                    if (message.arg1 == 1) {
2493                        setSuspendOptimizationsNative(SUSPEND_DUE_TO_SCREEN, true);
2494                        mSuspendWakeLock.release();
2495                    } else {
2496                        setSuspendOptimizationsNative(SUSPEND_DUE_TO_SCREEN, false);
2497                    }
2498                    break;
2499                case CMD_SET_HIGH_PERF_MODE:
2500                    if (message.arg1 == 1) {
2501                        setSuspendOptimizationsNative(SUSPEND_DUE_TO_HIGH_PERF, false);
2502                    } else {
2503                        setSuspendOptimizationsNative(SUSPEND_DUE_TO_HIGH_PERF, true);
2504                    }
2505                    break;
2506                default:
2507                    return NOT_HANDLED;
2508            }
2509            return HANDLED;
2510        }
2511        @Override
2512        public void exit() {
2513            mIsRunning = false;
2514            updateBatteryWorkSource(null);
2515            mScanResults = new ArrayList<ScanResult>();
2516        }
2517    }
2518
2519    class WaitForP2pDisableState extends State {
2520        private State mTransitionToState;
2521        @Override
2522        public void enter() {
2523            switch (getCurrentMessage().what) {
2524                case WifiMonitor.SUP_DISCONNECTION_EVENT:
2525                    mTransitionToState = mInitialState;
2526                    break;
2527                case CMD_DELAYED_STOP_DRIVER:
2528                    mTransitionToState = mDriverStoppingState;
2529                    break;
2530                case CMD_STOP_SUPPLICANT:
2531                    mTransitionToState = mSupplicantStoppingState;
2532                    break;
2533                default:
2534                    mTransitionToState = mDriverStoppingState;
2535                    break;
2536            }
2537            mWifiP2pChannel.sendMessage(WifiStateMachine.CMD_DISABLE_P2P_REQ);
2538        }
2539        @Override
2540        public boolean processMessage(Message message) {
2541            switch(message.what) {
2542                case WifiStateMachine.CMD_DISABLE_P2P_RSP:
2543                    transitionTo(mTransitionToState);
2544                    break;
2545                /* Defer wifi start/shut and driver commands */
2546                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
2547                case CMD_START_SUPPLICANT:
2548                case CMD_STOP_SUPPLICANT:
2549                case CMD_START_AP:
2550                case CMD_STOP_AP:
2551                case CMD_START_DRIVER:
2552                case CMD_STOP_DRIVER:
2553                case CMD_SET_OPERATIONAL_MODE:
2554                case CMD_SET_COUNTRY_CODE:
2555                case CMD_SET_FREQUENCY_BAND:
2556                case CMD_START_PACKET_FILTERING:
2557                case CMD_STOP_PACKET_FILTERING:
2558                case CMD_START_SCAN:
2559                case CMD_DISCONNECT:
2560                case CMD_REASSOCIATE:
2561                case CMD_RECONNECT:
2562                    deferMessage(message);
2563                    break;
2564                default:
2565                    return NOT_HANDLED;
2566            }
2567            return HANDLED;
2568        }
2569    }
2570
2571    class DriverStoppingState extends State {
2572        @Override
2573        public boolean processMessage(Message message) {
2574            switch(message.what) {
2575                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
2576                    SupplicantState state = handleSupplicantStateChange(message);
2577                    if (state == SupplicantState.INTERFACE_DISABLED) {
2578                        transitionTo(mDriverStoppedState);
2579                    }
2580                    break;
2581                    /* Queue driver commands */
2582                case CMD_START_DRIVER:
2583                case CMD_STOP_DRIVER:
2584                case CMD_SET_COUNTRY_CODE:
2585                case CMD_SET_FREQUENCY_BAND:
2586                case CMD_START_PACKET_FILTERING:
2587                case CMD_STOP_PACKET_FILTERING:
2588                case CMD_START_SCAN:
2589                case CMD_DISCONNECT:
2590                case CMD_REASSOCIATE:
2591                case CMD_RECONNECT:
2592                    deferMessage(message);
2593                    break;
2594                default:
2595                    return NOT_HANDLED;
2596            }
2597            return HANDLED;
2598        }
2599    }
2600
2601    class DriverStoppedState extends State {
2602        @Override
2603        public boolean processMessage(Message message) {
2604            switch (message.what) {
2605                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
2606                    StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
2607                    SupplicantState state = stateChangeResult.state;
2608                    // A WEXT bug means that we can be back to driver started state
2609                    // unexpectedly
2610                    if (SupplicantState.isDriverActive(state)) {
2611                        transitionTo(mDriverStartedState);
2612                    }
2613                    break;
2614                case CMD_START_DRIVER:
2615                    mWakeLock.acquire();
2616                    mWifiNative.startDriver();
2617                    mWakeLock.release();
2618                    transitionTo(mDriverStartingState);
2619                    break;
2620                default:
2621                    return NOT_HANDLED;
2622            }
2623            return HANDLED;
2624        }
2625    }
2626
2627    class ScanModeState extends State {
2628        private int mLastOperationMode;
2629        @Override
2630        public void enter() {
2631            mWifiConfigStore.disableAllNetworks();
2632            mLastOperationMode = mOperationalMode;
2633            if (mLastOperationMode == SCAN_ONLY_WITH_WIFI_OFF_MODE) {
2634                mWifiP2pChannel.sendMessage(CMD_DISABLE_P2P_REQ);
2635                setWifiState(WIFI_STATE_DISABLED);
2636            }
2637        }
2638        @Override
2639        public void exit() {
2640            if (mLastOperationMode == SCAN_ONLY_WITH_WIFI_OFF_MODE) {
2641                setWifiState(WIFI_STATE_ENABLED);
2642                mWifiP2pChannel.sendMessage(CMD_ENABLE_P2P);
2643            }
2644            mWifiConfigStore.enableAllNetworks();
2645            mWifiNative.reconnect();
2646        }
2647        @Override
2648        public boolean processMessage(Message message) {
2649            switch(message.what) {
2650                case CMD_SET_OPERATIONAL_MODE:
2651                    if (message.arg1 == CONNECT_MODE) {
2652                        mOperationalMode = CONNECT_MODE;
2653                        transitionTo(mDisconnectedState);
2654                    } else {
2655                        // Nothing to do
2656                        return HANDLED;
2657                    }
2658                    break;
2659                // Handle scan. All the connection related commands are
2660                // handled only in ConnectModeState
2661                case CMD_START_SCAN:
2662                    startScanNative(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP);
2663                    break;
2664                default:
2665                    return NOT_HANDLED;
2666            }
2667            return HANDLED;
2668        }
2669    }
2670
2671    class ConnectModeState extends State {
2672        @Override
2673        public boolean processMessage(Message message) {
2674            WifiConfiguration config;
2675            boolean ok;
2676            switch(message.what) {
2677                case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
2678                    mSupplicantStateTracker.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT);
2679                    break;
2680                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
2681                    SupplicantState state = handleSupplicantStateChange(message);
2682                    // A driver/firmware hang can now put the interface in a down state.
2683                    // We detect the interface going down and recover from it
2684                    if (!SupplicantState.isDriverActive(state)) {
2685                        if (mNetworkInfo.getState() != NetworkInfo.State.DISCONNECTED) {
2686                            handleNetworkDisconnect();
2687                        }
2688                        log("Detected an interface down, restart driver");
2689                        transitionTo(mDriverStoppedState);
2690                        sendMessage(CMD_START_DRIVER);
2691                        break;
2692                    }
2693
2694                    // Supplicant can fail to report a NETWORK_DISCONNECTION_EVENT
2695                    // when authentication times out after a successful connection,
2696                    // we can figure this from the supplicant state. If supplicant
2697                    // state is DISCONNECTED, but the mNetworkInfo says we are not
2698                    // disconnected, we need to handle a disconnection
2699                    if (state == SupplicantState.DISCONNECTED &&
2700                            mNetworkInfo.getState() != NetworkInfo.State.DISCONNECTED) {
2701                        if (DBG) log("Missed CTRL-EVENT-DISCONNECTED, disconnect");
2702                        handleNetworkDisconnect();
2703                        transitionTo(mDisconnectedState);
2704                    }
2705                    break;
2706                case WifiP2pService.DISCONNECT_WIFI_REQUEST:
2707                    if (message.arg1 == 1) {
2708                        mWifiNative.disconnect();
2709                        mTemporarilyDisconnectWifi = true;
2710                    } else {
2711                        mWifiNative.reconnect();
2712                        mTemporarilyDisconnectWifi = false;
2713                    }
2714                    break;
2715                case CMD_ADD_OR_UPDATE_NETWORK:
2716                    config = (WifiConfiguration) message.obj;
2717                    replyToMessage(message, CMD_ADD_OR_UPDATE_NETWORK,
2718                            mWifiConfigStore.addOrUpdateNetwork(config));
2719                    break;
2720                case CMD_REMOVE_NETWORK:
2721                    ok = mWifiConfigStore.removeNetwork(message.arg1);
2722                    replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
2723                    break;
2724                case CMD_ENABLE_NETWORK:
2725                    ok = mWifiConfigStore.enableNetwork(message.arg1, message.arg2 == 1);
2726                    replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
2727                    break;
2728                case CMD_ENABLE_ALL_NETWORKS:
2729                    long time =  android.os.SystemClock.elapsedRealtime();
2730                    if (time - mLastEnableAllNetworksTime > MIN_INTERVAL_ENABLE_ALL_NETWORKS_MS) {
2731                        mWifiConfigStore.enableAllNetworks();
2732                        mLastEnableAllNetworksTime = time;
2733                    }
2734                    break;
2735                case WifiManager.DISABLE_NETWORK:
2736                    if (mWifiConfigStore.disableNetwork(message.arg1,
2737                            WifiConfiguration.DISABLED_UNKNOWN_REASON) == true) {
2738                        replyToMessage(message, WifiManager.DISABLE_NETWORK_SUCCEEDED);
2739                    } else {
2740                        replyToMessage(message, WifiManager.DISABLE_NETWORK_FAILED,
2741                                WifiManager.ERROR);
2742                    }
2743                    break;
2744                case CMD_BLACKLIST_NETWORK:
2745                    mWifiNative.addToBlacklist((String)message.obj);
2746                    break;
2747                case CMD_CLEAR_BLACKLIST:
2748                    mWifiNative.clearBlacklist();
2749                    break;
2750                case CMD_SAVE_CONFIG:
2751                    ok = mWifiConfigStore.saveConfig();
2752                    replyToMessage(message, CMD_SAVE_CONFIG, ok ? SUCCESS : FAILURE);
2753
2754                    // Inform the backup manager about a data change
2755                    IBackupManager ibm = IBackupManager.Stub.asInterface(
2756                            ServiceManager.getService(Context.BACKUP_SERVICE));
2757                    if (ibm != null) {
2758                        try {
2759                            ibm.dataChanged("com.android.providers.settings");
2760                        } catch (Exception e) {
2761                            // Try again later
2762                        }
2763                    }
2764                    break;
2765                case CMD_GET_CONFIGURED_NETWORKS:
2766                    replyToMessage(message, message.what,
2767                            mWifiConfigStore.getConfiguredNetworks());
2768                    break;
2769                    /* Do a redundant disconnect without transition */
2770                case CMD_DISCONNECT:
2771                    mWifiNative.disconnect();
2772                    break;
2773                case CMD_RECONNECT:
2774                    mWifiNative.reconnect();
2775                    break;
2776                case CMD_REASSOCIATE:
2777                    mWifiNative.reassociate();
2778                    break;
2779                case WifiManager.CONNECT_NETWORK:
2780                    /* The connect message can contain a network id passed as arg1 on message or
2781                     * or a config passed as obj on message.
2782                     * For a new network, a config is passed to create and connect.
2783                     * For an existing network, a network id is passed
2784                     */
2785                    int netId = message.arg1;
2786                    config = (WifiConfiguration) message.obj;
2787
2788                    /* Save the network config */
2789                    if (config != null) {
2790                        NetworkUpdateResult result = mWifiConfigStore.saveNetwork(config);
2791                        netId = result.getNetworkId();
2792                    }
2793
2794                    if (mWifiConfigStore.selectNetwork(netId) &&
2795                            mWifiNative.reconnect()) {
2796                        /* The state tracker handles enabling networks upon completion/failure */
2797                        mSupplicantStateTracker.sendMessage(WifiManager.CONNECT_NETWORK);
2798                        replyToMessage(message, WifiManager.CONNECT_NETWORK_SUCCEEDED);
2799                        /* Expect a disconnection from the old connection */
2800                        transitionTo(mDisconnectingState);
2801                    } else {
2802                        loge("Failed to connect config: " + config + " netId: " + netId);
2803                        replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
2804                                WifiManager.ERROR);
2805                        break;
2806                    }
2807                    break;
2808                case WifiManager.SAVE_NETWORK:
2809                    config = (WifiConfiguration) message.obj;
2810                    NetworkUpdateResult result = mWifiConfigStore.saveNetwork(config);
2811                    if (result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID) {
2812                        replyToMessage(message, WifiManager.SAVE_NETWORK_SUCCEEDED);
2813                    } else {
2814                        loge("Failed to save network");
2815                        replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED,
2816                                WifiManager.ERROR);
2817                    }
2818                    break;
2819                case WifiManager.FORGET_NETWORK:
2820                    if (mWifiConfigStore.forgetNetwork(message.arg1)) {
2821                        replyToMessage(message, WifiManager.FORGET_NETWORK_SUCCEEDED);
2822                    } else {
2823                        loge("Failed to forget network");
2824                        replyToMessage(message, WifiManager.FORGET_NETWORK_FAILED,
2825                                WifiManager.ERROR);
2826                    }
2827                    break;
2828                case WifiManager.START_WPS:
2829                    WpsInfo wpsInfo = (WpsInfo) message.obj;
2830                    WpsResult wpsResult;
2831                    switch (wpsInfo.setup) {
2832                        case WpsInfo.PBC:
2833                            wpsResult = mWifiConfigStore.startWpsPbc(wpsInfo);
2834                            break;
2835                        case WpsInfo.KEYPAD:
2836                            wpsResult = mWifiConfigStore.startWpsWithPinFromAccessPoint(wpsInfo);
2837                            break;
2838                        case WpsInfo.DISPLAY:
2839                            wpsResult = mWifiConfigStore.startWpsWithPinFromDevice(wpsInfo);
2840                            break;
2841                        default:
2842                            wpsResult = new WpsResult(Status.FAILURE);
2843                            loge("Invalid setup for WPS");
2844                            break;
2845                    }
2846                    if (wpsResult.status == Status.SUCCESS) {
2847                        replyToMessage(message, WifiManager.START_WPS_SUCCEEDED, wpsResult);
2848                        transitionTo(mWpsRunningState);
2849                    } else {
2850                        loge("Failed to start WPS with config " + wpsInfo.toString());
2851                        replyToMessage(message, WifiManager.WPS_FAILED, WifiManager.ERROR);
2852                    }
2853                    break;
2854                case WifiMonitor.NETWORK_CONNECTION_EVENT:
2855                    if (DBG) log("Network connection established");
2856                    mLastNetworkId = message.arg1;
2857                    mLastBssid = (String) message.obj;
2858
2859                    mWifiInfo.setBSSID(mLastBssid);
2860                    mWifiInfo.setNetworkId(mLastNetworkId);
2861                    /* send event to CM & network change broadcast */
2862                    setNetworkDetailedState(DetailedState.OBTAINING_IPADDR);
2863                    sendNetworkStateChangeBroadcast(mLastBssid);
2864                    transitionTo(mObtainingIpState);
2865                    break;
2866                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
2867                    if (DBG) log("Network connection lost");
2868                    handleNetworkDisconnect();
2869                    transitionTo(mDisconnectedState);
2870                    break;
2871                default:
2872                    return NOT_HANDLED;
2873            }
2874            return HANDLED;
2875        }
2876    }
2877
2878    class L2ConnectedState extends State {
2879        @Override
2880        public void enter() {
2881            mRssiPollToken++;
2882            if (mEnableRssiPolling) {
2883                sendMessage(CMD_RSSI_POLL, mRssiPollToken, 0);
2884            }
2885        }
2886
2887        @Override
2888        public boolean processMessage(Message message) {
2889            switch (message.what) {
2890              case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
2891                  handlePreDhcpSetup();
2892                  mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_PRE_DHCP_ACTION_COMPLETE);
2893                  break;
2894              case DhcpStateMachine.CMD_POST_DHCP_ACTION:
2895                  handlePostDhcpSetup();
2896                  if (message.arg1 == DhcpStateMachine.DHCP_SUCCESS) {
2897                      if (DBG) log("DHCP successful");
2898                      handleSuccessfulIpConfiguration((DhcpResults) message.obj);
2899                      transitionTo(mVerifyingLinkState);
2900                  } else if (message.arg1 == DhcpStateMachine.DHCP_FAILURE) {
2901                      if (DBG) log("DHCP failed");
2902                      handleFailedIpConfiguration();
2903                      transitionTo(mDisconnectingState);
2904                  }
2905                  break;
2906                case CMD_DISCONNECT:
2907                    mWifiNative.disconnect();
2908                    transitionTo(mDisconnectingState);
2909                    break;
2910                case WifiP2pService.DISCONNECT_WIFI_REQUEST:
2911                    if (message.arg1 == 1) {
2912                        mWifiNative.disconnect();
2913                        mTemporarilyDisconnectWifi = true;
2914                        transitionTo(mDisconnectingState);
2915                    }
2916                    break;
2917                case CMD_SET_OPERATIONAL_MODE:
2918                    if (message.arg1 != CONNECT_MODE) {
2919                        sendMessage(CMD_DISCONNECT);
2920                        deferMessage(message);
2921                    }
2922                    break;
2923                case CMD_START_SCAN:
2924                    /* Do not attempt to connect when we are already connected */
2925                    startScanNative(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP);
2926                    break;
2927                    /* Ignore connection to same network */
2928                case WifiManager.CONNECT_NETWORK:
2929                    int netId = message.arg1;
2930                    if (mWifiInfo.getNetworkId() == netId) {
2931                        break;
2932                    }
2933                    return NOT_HANDLED;
2934                case WifiManager.SAVE_NETWORK:
2935                    WifiConfiguration config = (WifiConfiguration) message.obj;
2936                    NetworkUpdateResult result = mWifiConfigStore.saveNetwork(config);
2937                    if (mWifiInfo.getNetworkId() == result.getNetworkId()) {
2938                        if (result.hasIpChanged()) {
2939                            log("Reconfiguring IP on connection");
2940                            transitionTo(mObtainingIpState);
2941                        }
2942                        if (result.hasProxyChanged()) {
2943                            log("Reconfiguring proxy on connection");
2944                            configureLinkProperties();
2945                            sendLinkConfigurationChangedBroadcast();
2946                        }
2947                    }
2948
2949                    if (result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID) {
2950                        replyToMessage(message, WifiManager.SAVE_NETWORK_SUCCEEDED);
2951                    } else {
2952                        loge("Failed to save network");
2953                        replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED,
2954                                WifiManager.ERROR);
2955                    }
2956                    break;
2957                    /* Ignore */
2958                case WifiMonitor.NETWORK_CONNECTION_EVENT:
2959                    break;
2960                case CMD_RSSI_POLL:
2961                    if (message.arg1 == mRssiPollToken) {
2962                        // Get Info and continue polling
2963                        fetchRssiAndLinkSpeedNative();
2964                        sendMessageDelayed(obtainMessage(CMD_RSSI_POLL,
2965                                mRssiPollToken, 0), POLL_RSSI_INTERVAL_MSECS);
2966                    } else {
2967                        // Polling has completed
2968                    }
2969                    break;
2970                case CMD_ENABLE_RSSI_POLL:
2971                    mEnableRssiPolling = (message.arg1 == 1);
2972                    mRssiPollToken++;
2973                    if (mEnableRssiPolling) {
2974                        // first poll
2975                        fetchRssiAndLinkSpeedNative();
2976                        sendMessageDelayed(obtainMessage(CMD_RSSI_POLL,
2977                                mRssiPollToken, 0), POLL_RSSI_INTERVAL_MSECS);
2978                    }
2979                    break;
2980                case WifiManager.RSSI_PKTCNT_FETCH:
2981                    RssiPacketCountInfo info = new RssiPacketCountInfo();
2982                    fetchRssiAndLinkSpeedNative();
2983                    info.rssi = mWifiInfo.getRssi();
2984                    fetchPktcntNative(info);
2985                    replyToMessage(message, WifiManager.RSSI_PKTCNT_FETCH_SUCCEEDED, info);
2986                    break;
2987                default:
2988                    return NOT_HANDLED;
2989            }
2990
2991            return HANDLED;
2992        }
2993    }
2994
2995    class ObtainingIpState extends State {
2996        @Override
2997        public void enter() {
2998            if (!mWifiConfigStore.isUsingStaticIp(mLastNetworkId)) {
2999                //start DHCP
3000                if (mDhcpStateMachine == null) {
3001                    mDhcpStateMachine = DhcpStateMachine.makeDhcpStateMachine(
3002                            mContext, WifiStateMachine.this, mInterfaceName);
3003
3004                }
3005                mDhcpStateMachine.registerForPreDhcpNotification();
3006                mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_START_DHCP);
3007            } else {
3008                DhcpResults dhcpResults = new DhcpResults(
3009                        mWifiConfigStore.getLinkProperties(mLastNetworkId));
3010                dhcpResults.linkProperties.setInterfaceName(mInterfaceName);
3011                InterfaceConfiguration ifcg = new InterfaceConfiguration();
3012                Iterator<LinkAddress> addrs =
3013                        dhcpResults.linkProperties.getLinkAddresses().iterator();
3014                if (!addrs.hasNext()) {
3015                    loge("Static IP lacks address");
3016                    sendMessage(CMD_STATIC_IP_FAILURE);
3017                } else {
3018                    ifcg.setLinkAddress(addrs.next());
3019                    ifcg.setInterfaceUp();
3020                    try {
3021                        mNwService.setInterfaceConfig(mInterfaceName, ifcg);
3022                        if (DBG) log("Static IP configuration succeeded");
3023                        sendMessage(CMD_STATIC_IP_SUCCESS, dhcpResults);
3024                    } catch (RemoteException re) {
3025                        loge("Static IP configuration failed: " + re);
3026                        sendMessage(CMD_STATIC_IP_FAILURE);
3027                    } catch (IllegalStateException e) {
3028                        loge("Static IP configuration failed: " + e);
3029                        sendMessage(CMD_STATIC_IP_FAILURE);
3030                    }
3031                }
3032            }
3033        }
3034      @Override
3035      public boolean processMessage(Message message) {
3036          if (DBG) log(getName() + message.toString() + "\n");
3037          switch(message.what) {
3038            case CMD_STATIC_IP_SUCCESS:
3039                  handleSuccessfulIpConfiguration((DhcpResults) message.obj);
3040                  transitionTo(mVerifyingLinkState);
3041                  break;
3042              case CMD_STATIC_IP_FAILURE:
3043                  handleFailedIpConfiguration();
3044                  transitionTo(mDisconnectingState);
3045                  break;
3046             case WifiManager.SAVE_NETWORK:
3047                  deferMessage(message);
3048                  break;
3049                  /* Defer any power mode changes since we must keep active power mode at DHCP */
3050              case CMD_SET_HIGH_PERF_MODE:
3051                  deferMessage(message);
3052                  break;
3053                  /* Defer scan request since we should not switch to other channels at DHCP */
3054              case CMD_START_SCAN:
3055                  deferMessage(message);
3056                  break;
3057              default:
3058                  return NOT_HANDLED;
3059          }
3060          return HANDLED;
3061      }
3062    }
3063
3064    class VerifyingLinkState extends State {
3065        @Override
3066        public void enter() {
3067            setNetworkDetailedState(DetailedState.VERIFYING_POOR_LINK);
3068            mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.VERIFYING_POOR_LINK);
3069            sendNetworkStateChangeBroadcast(mLastBssid);
3070        }
3071        @Override
3072        public boolean processMessage(Message message) {
3073            switch (message.what) {
3074                case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
3075                    //stay here
3076                    break;
3077                case WifiWatchdogStateMachine.GOOD_LINK_DETECTED:
3078                    transitionTo(mCaptivePortalCheckState);
3079                    break;
3080                default:
3081                    return NOT_HANDLED;
3082            }
3083            return HANDLED;
3084        }
3085    }
3086
3087    class CaptivePortalCheckState extends State {
3088        @Override
3089        public void enter() {
3090            setNetworkDetailedState(DetailedState.CAPTIVE_PORTAL_CHECK);
3091            mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.CAPTIVE_PORTAL_CHECK);
3092            sendNetworkStateChangeBroadcast(mLastBssid);
3093        }
3094        @Override
3095        public boolean processMessage(Message message) {
3096            switch (message.what) {
3097                case CMD_CAPTIVE_CHECK_COMPLETE:
3098                    try {
3099                        mNwService.enableIpv6(mInterfaceName);
3100                    } catch (RemoteException re) {
3101                        loge("Failed to enable IPv6: " + re);
3102                    } catch (IllegalStateException e) {
3103                        loge("Failed to enable IPv6: " + e);
3104                    }
3105                    setNetworkDetailedState(DetailedState.CONNECTED);
3106                    mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.CONNECTED);
3107                    sendNetworkStateChangeBroadcast(mLastBssid);
3108                    transitionTo(mConnectedState);
3109                    break;
3110                default:
3111                    return NOT_HANDLED;
3112            }
3113            return HANDLED;
3114        }
3115    }
3116
3117    class ConnectedState extends State {
3118        @Override
3119        public boolean processMessage(Message message) {
3120            switch (message.what) {
3121               case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
3122                    if (DBG) log("Watchdog reports poor link");
3123                    try {
3124                        mNwService.disableIpv6(mInterfaceName);
3125                    } catch (RemoteException re) {
3126                        loge("Failed to disable IPv6: " + re);
3127                    } catch (IllegalStateException e) {
3128                        loge("Failed to disable IPv6: " + e);
3129                    }
3130                    /* Report a disconnect */
3131                    setNetworkDetailedState(DetailedState.DISCONNECTED);
3132                    mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.DISCONNECTED);
3133                    sendNetworkStateChangeBroadcast(mLastBssid);
3134
3135                    transitionTo(mVerifyingLinkState);
3136                    break;
3137                default:
3138                    return NOT_HANDLED;
3139            }
3140            return HANDLED;
3141        }
3142        @Override
3143        public void exit() {
3144            /* Request a CS wakelock during transition to mobile */
3145            checkAndSetConnectivityInstance();
3146            mCm.requestNetworkTransitionWakelock(getName());
3147        }
3148    }
3149
3150    class DisconnectingState extends State {
3151        @Override
3152        public boolean processMessage(Message message) {
3153            switch (message.what) {
3154                case CMD_SET_OPERATIONAL_MODE:
3155                    if (message.arg1 != CONNECT_MODE) {
3156                        deferMessage(message);
3157                    }
3158                    break;
3159                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
3160                    /* If we get a SUPPLICANT_STATE_CHANGE_EVENT before NETWORK_DISCONNECTION_EVENT
3161                     * we have missed the network disconnection, transition to mDisconnectedState
3162                     * and handle the rest of the events there
3163                     */
3164                    deferMessage(message);
3165                    handleNetworkDisconnect();
3166                    transitionTo(mDisconnectedState);
3167                    break;
3168                default:
3169                    return NOT_HANDLED;
3170            }
3171            return HANDLED;
3172        }
3173    }
3174
3175    class DisconnectedState extends State {
3176        private boolean mAlarmEnabled = false;
3177        /* This is set from the overlay config file or from a secure setting.
3178         * A value of 0 disables scanning in the framework.
3179         */
3180        private long mFrameworkScanIntervalMs;
3181
3182        private void setScanAlarm(boolean enabled) {
3183            if (enabled == mAlarmEnabled) return;
3184            if (enabled) {
3185                if (mFrameworkScanIntervalMs > 0) {
3186                    mAlarmManager.setRepeating(AlarmManager.RTC_WAKEUP,
3187                            System.currentTimeMillis() + mFrameworkScanIntervalMs,
3188                            mFrameworkScanIntervalMs,
3189                            mScanIntent);
3190                    mAlarmEnabled = true;
3191                }
3192            } else {
3193                mAlarmManager.cancel(mScanIntent);
3194                mAlarmEnabled = false;
3195            }
3196        }
3197
3198        @Override
3199        public void enter() {
3200            // We dont scan frequently if this is a temporary disconnect
3201            // due to p2p
3202            if (mTemporarilyDisconnectWifi) {
3203                mWifiP2pChannel.sendMessage(WifiP2pService.DISCONNECT_WIFI_RESPONSE);
3204                return;
3205            }
3206
3207            mFrameworkScanIntervalMs = Settings.Global.getLong(mContext.getContentResolver(),
3208                    Settings.Global.WIFI_FRAMEWORK_SCAN_INTERVAL_MS,
3209                    mDefaultFrameworkScanIntervalMs);
3210            /*
3211             * We initiate background scanning if it is enabled, otherwise we
3212             * initiate an infrequent scan that wakes up the device to ensure
3213             * a user connects to an access point on the move
3214             */
3215            if (mEnableBackgroundScan) {
3216                /* If a regular scan result is pending, do not initiate background
3217                 * scan until the scan results are returned. This is needed because
3218                 * initiating a background scan will cancel the regular scan and
3219                 * scan results will not be returned until background scanning is
3220                 * cleared
3221                 */
3222                if (!mScanResultIsPending) {
3223                    mWifiNative.enableBackgroundScan(true);
3224                }
3225            } else {
3226                setScanAlarm(true);
3227            }
3228
3229            /**
3230             * If we have no networks saved, the supplicant stops doing the periodic scan.
3231             * The scans are useful to notify the user of the presence of an open network.
3232             * Note that these are not wake up scans.
3233             */
3234            if (!mP2pConnected.get() && mWifiConfigStore.getConfiguredNetworks().size() == 0) {
3235                sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,
3236                            ++mPeriodicScanToken, 0), mSupplicantScanIntervalMs);
3237            }
3238        }
3239        @Override
3240        public boolean processMessage(Message message) {
3241            boolean ret = HANDLED;
3242            switch (message.what) {
3243                case CMD_NO_NETWORKS_PERIODIC_SCAN:
3244                    if (mP2pConnected.get()) break;
3245                    if (message.arg1 == mPeriodicScanToken &&
3246                            mWifiConfigStore.getConfiguredNetworks().size() == 0) {
3247                        sendMessage(CMD_START_SCAN);
3248                        sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,
3249                                    ++mPeriodicScanToken, 0), mSupplicantScanIntervalMs);
3250                    }
3251                    break;
3252                case WifiManager.FORGET_NETWORK:
3253                case CMD_REMOVE_NETWORK:
3254                    // Set up a delayed message here. After the forget/remove is handled
3255                    // the handled delayed message will determine if there is a need to
3256                    // scan and continue
3257                    sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,
3258                                ++mPeriodicScanToken, 0), mSupplicantScanIntervalMs);
3259                    ret = NOT_HANDLED;
3260                    break;
3261                case CMD_SET_OPERATIONAL_MODE:
3262                    if (message.arg1 != CONNECT_MODE) {
3263                        mOperationalMode = message.arg1;
3264                        transitionTo(mScanModeState);
3265                    }
3266                    break;
3267                case CMD_ENABLE_BACKGROUND_SCAN:
3268                    mEnableBackgroundScan = (message.arg1 == 1);
3269                    if (mEnableBackgroundScan) {
3270                        mWifiNative.enableBackgroundScan(true);
3271                        setScanAlarm(false);
3272                    } else {
3273                        mWifiNative.enableBackgroundScan(false);
3274                        setScanAlarm(true);
3275                    }
3276                    break;
3277                    /* Ignore network disconnect */
3278                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
3279                    break;
3280                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
3281                    StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
3282                    setNetworkDetailedState(WifiInfo.getDetailedStateOf(stateChangeResult.state));
3283                    /* ConnectModeState does the rest of the handling */
3284                    ret = NOT_HANDLED;
3285                    break;
3286                case CMD_START_SCAN:
3287                    /* Disable background scan temporarily during a regular scan */
3288                    if (mEnableBackgroundScan) {
3289                        mWifiNative.enableBackgroundScan(false);
3290                    }
3291                    /* Handled in parent state */
3292                    ret = NOT_HANDLED;
3293                    break;
3294                case WifiMonitor.SCAN_RESULTS_EVENT:
3295                    /* Re-enable background scan when a pending scan result is received */
3296                    if (mEnableBackgroundScan && mScanResultIsPending) {
3297                        mWifiNative.enableBackgroundScan(true);
3298                    }
3299                    /* Handled in parent state */
3300                    ret = NOT_HANDLED;
3301                    break;
3302                case WifiP2pService.P2P_CONNECTION_CHANGED:
3303                    NetworkInfo info = (NetworkInfo) message.obj;
3304                    mP2pConnected.set(info.isConnected());
3305                    if (mP2pConnected.get()) {
3306                        int defaultInterval = mContext.getResources().getInteger(
3307                                R.integer.config_wifi_scan_interval_p2p_connected);
3308                        long scanIntervalMs = Settings.Global.getLong(mContext.getContentResolver(),
3309                                Settings.Global.WIFI_SCAN_INTERVAL_WHEN_P2P_CONNECTED_MS,
3310                                defaultInterval);
3311                        mWifiNative.setScanInterval((int) scanIntervalMs/1000);
3312                    } else if (mWifiConfigStore.getConfiguredNetworks().size() == 0) {
3313                        if (DBG) log("Turn on scanning after p2p disconnected");
3314                        sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,
3315                                    ++mPeriodicScanToken, 0), mSupplicantScanIntervalMs);
3316                    }
3317                case CMD_RECONNECT:
3318                case CMD_REASSOCIATE:
3319                    if (mTemporarilyDisconnectWifi) {
3320                        // Drop a third party reconnect/reassociate if STA is
3321                        // temporarily disconnected for p2p
3322                        break;
3323                    } else {
3324                        // ConnectModeState handles it
3325                        ret = NOT_HANDLED;
3326                    }
3327                    break;
3328                default:
3329                    ret = NOT_HANDLED;
3330            }
3331            return ret;
3332        }
3333
3334        @Override
3335        public void exit() {
3336            /* No need for a background scan upon exit from a disconnected state */
3337            if (mEnableBackgroundScan) {
3338                mWifiNative.enableBackgroundScan(false);
3339            }
3340            setScanAlarm(false);
3341        }
3342    }
3343
3344    class WpsRunningState extends State {
3345        //Tracks the source to provide a reply
3346        private Message mSourceMessage;
3347        @Override
3348        public void enter() {
3349            mSourceMessage = Message.obtain(getCurrentMessage());
3350        }
3351        @Override
3352        public boolean processMessage(Message message) {
3353            switch (message.what) {
3354                case WifiMonitor.WPS_SUCCESS_EVENT:
3355                    replyToMessage(mSourceMessage, WifiManager.WPS_COMPLETED);
3356                    mSourceMessage.recycle();
3357                    mSourceMessage = null;
3358                    transitionTo(mDisconnectedState);
3359                    break;
3360                case WifiMonitor.WPS_OVERLAP_EVENT:
3361                    replyToMessage(mSourceMessage, WifiManager.WPS_FAILED,
3362                            WifiManager.WPS_OVERLAP_ERROR);
3363                    mSourceMessage.recycle();
3364                    mSourceMessage = null;
3365                    transitionTo(mDisconnectedState);
3366                    break;
3367                case WifiMonitor.WPS_FAIL_EVENT:
3368                    //arg1 has the reason for the failure
3369                    replyToMessage(mSourceMessage, WifiManager.WPS_FAILED, message.arg1);
3370                    mSourceMessage.recycle();
3371                    mSourceMessage = null;
3372                    transitionTo(mDisconnectedState);
3373                    break;
3374                case WifiMonitor.WPS_TIMEOUT_EVENT:
3375                    replyToMessage(mSourceMessage, WifiManager.WPS_FAILED,
3376                            WifiManager.WPS_TIMED_OUT);
3377                    mSourceMessage.recycle();
3378                    mSourceMessage = null;
3379                    transitionTo(mDisconnectedState);
3380                    break;
3381                case WifiManager.START_WPS:
3382                    replyToMessage(message, WifiManager.WPS_FAILED, WifiManager.IN_PROGRESS);
3383                    break;
3384                case WifiManager.CANCEL_WPS:
3385                    if (mWifiNative.cancelWps()) {
3386                        replyToMessage(message, WifiManager.CANCEL_WPS_SUCCEDED);
3387                    } else {
3388                        replyToMessage(message, WifiManager.CANCEL_WPS_FAILED, WifiManager.ERROR);
3389                    }
3390                    transitionTo(mDisconnectedState);
3391                    break;
3392                /* Defer all commands that can cause connections to a different network
3393                 * or put the state machine out of connect mode
3394                 */
3395                case CMD_STOP_DRIVER:
3396                case CMD_SET_OPERATIONAL_MODE:
3397                case WifiManager.CONNECT_NETWORK:
3398                case CMD_ENABLE_NETWORK:
3399                case CMD_RECONNECT:
3400                case CMD_REASSOCIATE:
3401                case WifiMonitor.NETWORK_CONNECTION_EVENT: /* Handled after exiting WPS state */
3402                    deferMessage(message);
3403                    break;
3404                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
3405                    if (DBG) log("Network connection lost");
3406                    handleNetworkDisconnect();
3407                    break;
3408                case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
3409                    // Disregard auth failure events during WPS connection. The
3410                    // EAP sequence is retried several times, and there might be
3411                    // failures (especially for wps pin). We will get a WPS_XXX
3412                    // event at the end of the sequence anyway.
3413                    if (DBG) log("Ignore auth failure during WPS connection");
3414                    break;
3415                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
3416                    //Throw away supplicant state changes when WPS is running.
3417                    //We will start getting supplicant state changes once we get
3418                    //a WPS success or failure
3419                    break;
3420                default:
3421                    return NOT_HANDLED;
3422            }
3423            return HANDLED;
3424        }
3425
3426        @Override
3427        public void exit() {
3428            mWifiConfigStore.enableAllNetworks();
3429            mWifiConfigStore.loadConfiguredNetworks();
3430        }
3431    }
3432
3433    class SoftApStartingState extends State {
3434        @Override
3435        public void enter() {
3436            final Message message = getCurrentMessage();
3437            if (message.what == CMD_START_AP) {
3438                final WifiConfiguration config = (WifiConfiguration) message.obj;
3439
3440                if (config == null) {
3441                    mWifiApConfigChannel.sendMessage(CMD_REQUEST_AP_CONFIG);
3442                } else {
3443                    mWifiApConfigChannel.sendMessage(CMD_SET_AP_CONFIG, config);
3444                    startSoftApWithConfig(config);
3445                }
3446            } else {
3447                throw new RuntimeException("Illegal transition to SoftApStartingState: " + message);
3448            }
3449        }
3450        @Override
3451        public boolean processMessage(Message message) {
3452            switch(message.what) {
3453                case CMD_START_SUPPLICANT:
3454                case CMD_STOP_SUPPLICANT:
3455                case CMD_START_AP:
3456                case CMD_STOP_AP:
3457                case CMD_START_DRIVER:
3458                case CMD_STOP_DRIVER:
3459                case CMD_SET_OPERATIONAL_MODE:
3460                case CMD_SET_COUNTRY_CODE:
3461                case CMD_SET_FREQUENCY_BAND:
3462                case CMD_START_PACKET_FILTERING:
3463                case CMD_STOP_PACKET_FILTERING:
3464                case CMD_TETHER_STATE_CHANGE:
3465                    deferMessage(message);
3466                    break;
3467                case WifiStateMachine.CMD_RESPONSE_AP_CONFIG:
3468                    WifiConfiguration config = (WifiConfiguration) message.obj;
3469                    if (config != null) {
3470                        startSoftApWithConfig(config);
3471                    } else {
3472                        loge("Softap config is null!");
3473                        sendMessage(CMD_START_AP_FAILURE);
3474                    }
3475                    break;
3476                case CMD_START_AP_SUCCESS:
3477                    setWifiApState(WIFI_AP_STATE_ENABLED);
3478                    transitionTo(mSoftApStartedState);
3479                    break;
3480                case CMD_START_AP_FAILURE:
3481                    setWifiApState(WIFI_AP_STATE_FAILED);
3482                    transitionTo(mInitialState);
3483                    break;
3484                default:
3485                    return NOT_HANDLED;
3486            }
3487            return HANDLED;
3488        }
3489    }
3490
3491    class SoftApStartedState extends State {
3492        @Override
3493        public boolean processMessage(Message message) {
3494            switch(message.what) {
3495                case CMD_STOP_AP:
3496                    if (DBG) log("Stopping Soft AP");
3497                    /* We have not tethered at this point, so we just shutdown soft Ap */
3498                    try {
3499                        mNwService.stopAccessPoint(mInterfaceName);
3500                    } catch(Exception e) {
3501                        loge("Exception in stopAccessPoint()");
3502                    }
3503                    setWifiApState(WIFI_AP_STATE_DISABLED);
3504                    transitionTo(mInitialState);
3505                    break;
3506                case CMD_START_AP:
3507                    // Ignore a start on a running access point
3508                    break;
3509                    /* Fail client mode operation when soft AP is enabled */
3510                case CMD_START_SUPPLICANT:
3511                    loge("Cannot start supplicant with a running soft AP");
3512                    setWifiState(WIFI_STATE_UNKNOWN);
3513                    break;
3514                case CMD_TETHER_STATE_CHANGE:
3515                    TetherStateChange stateChange = (TetherStateChange) message.obj;
3516                    if (startTethering(stateChange.available)) {
3517                        transitionTo(mTetheringState);
3518                    }
3519                    break;
3520                default:
3521                    return NOT_HANDLED;
3522            }
3523            return HANDLED;
3524        }
3525    }
3526
3527    class TetheringState extends State {
3528        @Override
3529        public void enter() {
3530            /* Send ourselves a delayed message to shut down if tethering fails to notify */
3531            sendMessageDelayed(obtainMessage(CMD_TETHER_NOTIFICATION_TIMED_OUT,
3532                    ++mTetherToken, 0), TETHER_NOTIFICATION_TIME_OUT_MSECS);
3533        }
3534        @Override
3535        public boolean processMessage(Message message) {
3536            switch(message.what) {
3537                case CMD_TETHER_STATE_CHANGE:
3538                    TetherStateChange stateChange = (TetherStateChange) message.obj;
3539                    if (isWifiTethered(stateChange.active)) {
3540                        transitionTo(mTetheredState);
3541                    }
3542                    return HANDLED;
3543                case CMD_TETHER_NOTIFICATION_TIMED_OUT:
3544                    if (message.arg1 == mTetherToken) {
3545                        loge("Failed to get tether update, shutdown soft access point");
3546                        transitionTo(mSoftApStartedState);
3547                        // Needs to be first thing handled
3548                        sendMessageAtFrontOfQueue(CMD_STOP_AP);
3549                    }
3550                    break;
3551                case CMD_START_SUPPLICANT:
3552                case CMD_STOP_SUPPLICANT:
3553                case CMD_START_AP:
3554                case CMD_STOP_AP:
3555                case CMD_START_DRIVER:
3556                case CMD_STOP_DRIVER:
3557                case CMD_SET_OPERATIONAL_MODE:
3558                case CMD_SET_COUNTRY_CODE:
3559                case CMD_SET_FREQUENCY_BAND:
3560                case CMD_START_PACKET_FILTERING:
3561                case CMD_STOP_PACKET_FILTERING:
3562                    deferMessage(message);
3563                    break;
3564                default:
3565                    return NOT_HANDLED;
3566            }
3567            return HANDLED;
3568        }
3569    }
3570
3571    class TetheredState extends State {
3572        @Override
3573        public boolean processMessage(Message message) {
3574            switch(message.what) {
3575                case CMD_TETHER_STATE_CHANGE:
3576                    TetherStateChange stateChange = (TetherStateChange) message.obj;
3577                    if (!isWifiTethered(stateChange.active)) {
3578                        loge("Tethering reports wifi as untethered!, shut down soft Ap");
3579                        setHostApRunning(null, false);
3580                    }
3581                    return HANDLED;
3582                case CMD_STOP_AP:
3583                    if (DBG) log("Untethering before stopping AP");
3584                    setWifiApState(WIFI_AP_STATE_DISABLING);
3585                    stopTethering();
3586                    transitionTo(mUntetheringState);
3587                    // More work to do after untethering
3588                    deferMessage(message);
3589                    break;
3590                default:
3591                    return NOT_HANDLED;
3592            }
3593            return HANDLED;
3594        }
3595    }
3596
3597    class UntetheringState extends State {
3598        @Override
3599        public void enter() {
3600            /* Send ourselves a delayed message to shut down if tethering fails to notify */
3601            sendMessageDelayed(obtainMessage(CMD_TETHER_NOTIFICATION_TIMED_OUT,
3602                    ++mTetherToken, 0), TETHER_NOTIFICATION_TIME_OUT_MSECS);
3603
3604        }
3605        @Override
3606        public boolean processMessage(Message message) {
3607            switch(message.what) {
3608                case CMD_TETHER_STATE_CHANGE:
3609                    TetherStateChange stateChange = (TetherStateChange) message.obj;
3610
3611                    /* Wait till wifi is untethered */
3612                    if (isWifiTethered(stateChange.active)) break;
3613
3614                    transitionTo(mSoftApStartedState);
3615                    break;
3616                case CMD_TETHER_NOTIFICATION_TIMED_OUT:
3617                    if (message.arg1 == mTetherToken) {
3618                        loge("Failed to get tether update, force stop access point");
3619                        transitionTo(mSoftApStartedState);
3620                    }
3621                    break;
3622                case CMD_START_SUPPLICANT:
3623                case CMD_STOP_SUPPLICANT:
3624                case CMD_START_AP:
3625                case CMD_STOP_AP:
3626                case CMD_START_DRIVER:
3627                case CMD_STOP_DRIVER:
3628                case CMD_SET_OPERATIONAL_MODE:
3629                case CMD_SET_COUNTRY_CODE:
3630                case CMD_SET_FREQUENCY_BAND:
3631                case CMD_START_PACKET_FILTERING:
3632                case CMD_STOP_PACKET_FILTERING:
3633                    deferMessage(message);
3634                    break;
3635                default:
3636                    return NOT_HANDLED;
3637            }
3638            return HANDLED;
3639        }
3640    }
3641
3642    //State machine initiated requests can have replyTo set to null indicating
3643    //there are no recepients, we ignore those reply actions
3644    private void replyToMessage(Message msg, int what) {
3645        if (msg.replyTo == null) return;
3646        Message dstMsg = obtainMessageWithArg2(msg);
3647        dstMsg.what = what;
3648        mReplyChannel.replyToMessage(msg, dstMsg);
3649    }
3650
3651    private void replyToMessage(Message msg, int what, int arg1) {
3652        if (msg.replyTo == null) return;
3653        Message dstMsg = obtainMessageWithArg2(msg);
3654        dstMsg.what = what;
3655        dstMsg.arg1 = arg1;
3656        mReplyChannel.replyToMessage(msg, dstMsg);
3657    }
3658
3659    private void replyToMessage(Message msg, int what, Object obj) {
3660        if (msg.replyTo == null) return;
3661        Message dstMsg = obtainMessageWithArg2(msg);
3662        dstMsg.what = what;
3663        dstMsg.obj = obj;
3664        mReplyChannel.replyToMessage(msg, dstMsg);
3665    }
3666
3667    /**
3668     * arg2 on the source message has a unique id that needs to be retained in replies
3669     * to match the request
3670     *
3671     * see WifiManager for details
3672     */
3673    private Message obtainMessageWithArg2(Message srcMsg) {
3674        Message msg = Message.obtain();
3675        msg.arg2 = srcMsg.arg2;
3676        return msg;
3677    }
3678}
3679