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