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