OSDN Git Service

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