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