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