WifiStateMachine.java revision 51991e784c605a7432a90c9e9a91b080fb106197
1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.wifi;
18
19import static android.net.wifi.WifiManager.WIFI_STATE_DISABLED;
20import static android.net.wifi.WifiManager.WIFI_STATE_DISABLING;
21import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED;
22import static android.net.wifi.WifiManager.WIFI_STATE_ENABLING;
23import static android.net.wifi.WifiManager.WIFI_STATE_UNKNOWN;
24
25/**
26 * TODO:
27 * Deprecate WIFI_STATE_UNKNOWN
28 */
29import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED;
30import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLING;
31import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
32import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLING;
33import static android.net.wifi.WifiManager.WIFI_AP_STATE_FAILED;
34
35import android.app.AlarmManager;
36import android.app.PendingIntent;
37import android.app.backup.IBackupManager;
38import android.bluetooth.BluetoothAdapter;
39import android.content.BroadcastReceiver;
40import android.content.Context;
41import android.content.Intent;
42import android.content.IntentFilter;
43import android.content.pm.PackageManager;
44import android.database.ContentObserver;
45import android.net.*;
46import android.net.NetworkInfo.DetailedState;
47import android.net.wifi.BatchedScanResult;
48import android.net.wifi.BatchedScanSettings;
49import android.net.wifi.RssiPacketCountInfo;
50import android.net.wifi.ScanResult;
51import android.net.wifi.ScanSettings;
52import android.net.wifi.WifiChannel;
53import android.net.wifi.SupplicantState;
54import android.net.wifi.WifiConfiguration;
55import android.net.wifi.WifiInfo;
56import android.net.wifi.WifiLinkLayerStats;
57import android.net.wifi.WifiManager;
58import android.net.wifi.WifiSsid;
59import android.net.wifi.WpsInfo;
60import android.net.wifi.WpsResult;
61import android.net.wifi.WpsResult.Status;
62import android.net.wifi.p2p.IWifiP2pManager;
63import android.net.wifi.passpoint.IWifiPasspointManager;
64import android.os.*;
65import android.os.Process;
66import android.provider.Settings;
67import android.util.LruCache;
68import android.text.TextUtils;
69import android.util.Log;
70
71import com.android.internal.R;
72import com.android.internal.app.IBatteryStats;
73import com.android.internal.util.AsyncChannel;
74import com.android.internal.util.Protocol;
75import com.android.internal.util.State;
76import com.android.internal.util.StateMachine;
77import com.android.server.net.BaseNetworkObserver;
78import com.android.server.net.NetlinkTracker;
79
80import com.android.server.wifi.p2p.WifiP2pServiceImpl;
81import com.android.server.wifi.passpoint.WifiPasspointServiceImpl;
82import com.android.server.wifi.passpoint.WifiPasspointStateMachine;
83
84import java.io.FileDescriptor;
85import java.io.PrintWriter;
86import java.net.InetAddress;
87import java.util.*;
88import java.util.concurrent.atomic.AtomicInteger;
89import java.util.concurrent.atomic.AtomicBoolean;
90import java.util.regex.Pattern;
91import java.io.FileReader;
92import java.io.BufferedReader;
93import java.io.FileNotFoundException;
94import java.io.IOException;
95import java.net.InetAddress;
96import java.net.Inet4Address;
97import java.net.Inet6Address;
98
99/**
100 * Track the state of Wifi connectivity. All event handling is done here,
101 * and all changes in connectivity state are initiated here.
102 *
103 * Wi-Fi now supports three modes of operation: Client, SoftAp and p2p
104 * In the current implementation, we support concurrent wifi p2p and wifi operation.
105 * The WifiStateMachine handles SoftAp and Client operations while WifiP2pService
106 * handles p2p operation.
107 *
108 * @hide
109 */
110public class WifiStateMachine extends StateMachine {
111
112    private static final String NETWORKTYPE = "WIFI";
113    private static boolean DBG = false;
114    private static boolean VDBG = false;
115    private static boolean mLogMessages = false;
116
117    /* temporary debug flag - best network selection development */
118    private static boolean PDBG = false;
119    /**
120     * Log with error attribute
121     *
122     * @param s is string log
123     */
124    protected void loge(String s) {
125        long now = SystemClock.elapsedRealtimeNanos();
126        String ts = String.format("[%,d us] ", now / 1000);
127        Log.e(getName(), ts + s);
128    }
129    protected void log(String s) {
130        long now = SystemClock.elapsedRealtimeNanos();
131        String ts = String.format("[%,d us] ", now / 1000);
132        Log.e(getName(), ts + s);
133    }
134
135    private WifiMonitor mWifiMonitor;
136    private WifiNative mWifiNative;
137    private WifiConfigStore mWifiConfigStore;
138    private WifiAutoJoinController mWifiAutoJoinController;
139    private INetworkManagementService mNwService;
140    private ConnectivityManager mCm;
141
142    private final boolean mP2pSupported;
143    private final AtomicBoolean mP2pConnected = new AtomicBoolean(false);
144    private boolean mTemporarilyDisconnectWifi = false;
145    private final String mPrimaryDeviceType;
146
147    /* Scan results handling */
148    private List<ScanResult> mScanResults = new ArrayList<ScanResult>();
149    private static final Pattern scanResultPattern = Pattern.compile("\t+");
150    private static final int SCAN_RESULT_CACHE_SIZE = 80;
151    private final LruCache<String, ScanResult> mScanResultCache;
152
153    /* Batch scan results */
154    private final List<BatchedScanResult> mBatchedScanResults =
155            new ArrayList<BatchedScanResult>();
156    private int mBatchedScanOwnerUid = UNKNOWN_SCAN_SOURCE;
157    private int mExpectedBatchedScans = 0;
158    private long mBatchedScanMinPollTime = 0;
159
160    private boolean mScreenOn = false;
161
162    /* Chipset supports background scan */
163    private final boolean mBackgroundScanSupported;
164
165    private String mInterfaceName;
166    /* Tethering interface could be separate from wlan interface */
167    private String mTetherInterfaceName;
168
169    private int mLastSignalLevel = -1;
170    private String mLastBssid;
171    private int mLastNetworkId;
172    private boolean mEnableRssiPolling = false;
173    private boolean mEnableBackgroundScan = false;
174    private int mRssiPollToken = 0;
175    private int mReconnectCount = 0;
176    /* 3 operational states for STA operation: CONNECT_MODE, SCAN_ONLY_MODE, SCAN_ONLY_WIFI_OFF_MODE
177    * In CONNECT_MODE, the STA can scan and connect to an access point
178    * In SCAN_ONLY_MODE, the STA can only scan for access points
179    * In SCAN_ONLY_WIFI_OFF_MODE, the STA can only scan for access points with wifi toggle being off
180    */
181    private int mOperationalMode = CONNECT_MODE;
182    private boolean mIsScanOngoing = false;
183    private boolean mIsFullScanOngoing = false;
184    private final Queue<Message> mBufferedScanMsg = new LinkedList<Message>();
185    private WorkSource mScanWorkSource = null;
186    private static final int UNKNOWN_SCAN_SOURCE = -1;
187    private static final int SCAN_ALARM_SOURCE = -2;
188
189    private static final int SCAN_REQUEST_BUFFER_MAX_SIZE = 10;
190    private static final String CUSTOMIZED_SCAN_SETTING = "customized_scan_settings";
191    private static final String CUSTOMIZED_SCAN_WORKSOURCE = "customized_scan_worksource";
192    private static final String BATCHED_SETTING = "batched_settings";
193    private static final String BATCHED_WORKSOURCE = "batched_worksource";
194
195    /* Tracks if state machine has received any screen state change broadcast yet.
196     * We can miss one of these at boot.
197     */
198    private AtomicBoolean mScreenBroadcastReceived = new AtomicBoolean(false);
199
200    private boolean mBluetoothConnectionActive = false;
201
202    private PowerManager.WakeLock mSuspendWakeLock;
203
204    /**
205     * Interval in milliseconds between polling for RSSI
206     * and linkspeed information
207     */
208    private static final int POLL_RSSI_INTERVAL_MSECS = 3000;
209
210    /**
211     * Delay between supplicant restarts upon failure to establish connection
212     */
213    private static final int SUPPLICANT_RESTART_INTERVAL_MSECS = 5000;
214
215    /**
216     * Number of times we attempt to restart supplicant
217     */
218    private static final int SUPPLICANT_RESTART_TRIES = 5;
219
220    private int mSupplicantRestartCount = 0;
221    /* Tracks sequence number on stop failure message */
222    private int mSupplicantStopFailureToken = 0;
223
224    /**
225     * Tether state change notification time out
226     */
227    private static final int TETHER_NOTIFICATION_TIME_OUT_MSECS = 5000;
228
229    /* Tracks sequence number on a tether notification time out */
230    private int mTetherToken = 0;
231
232    /**
233     * Driver start time out.
234     */
235    private static final int DRIVER_START_TIME_OUT_MSECS = 10000;
236
237    /* Tracks sequence number on a driver time out */
238    private int mDriverStartToken = 0;
239
240    /**
241     * The link properties of the wifi interface.
242     * Do not modify this directly; use updateLinkProperties instead.
243     */
244    private LinkProperties mLinkProperties;
245
246    /* Tracks sequence number on a periodic scan message */
247    private int mPeriodicScanToken = 0;
248
249    // Wakelock held during wifi start/stop and driver load/unload
250    private PowerManager.WakeLock mWakeLock;
251
252    private Context mContext;
253
254    private final Object mDhcpResultsLock = new Object();
255    private DhcpResults mDhcpResults;
256    private WifiInfo mWifiInfo;
257    private NetworkInfo mNetworkInfo;
258    private NetworkCapabilities mNetworkCapabilities;
259    private SupplicantStateTracker mSupplicantStateTracker;
260    private DhcpStateMachine mDhcpStateMachine;
261    private boolean mDhcpActive = false;
262
263    private int mWifiLinkLayerStatsSupported = 4;
264
265    private final AtomicInteger mCountryCodeSequence = new AtomicInteger();
266
267    //whether the state machine goes thru the Disconnecting->Disconnected->ObtainingIpAddress
268    private int mAutoRoaming = WifiAutoJoinController.AUTO_JOIN_IDLE;
269
270    private String mTargetRoamBSSID = "any";
271
272    boolean isRoaming() {
273        return mAutoRoaming == WifiAutoJoinController.AUTO_JOIN_ROAMING
274                || mAutoRoaming == WifiAutoJoinController.AUTO_JOIN_EXTENDED_ROAMING;
275    }
276
277    public void autoRoamSetBSSID(int netId, String bssid) {
278        autoRoamSetBSSID(mWifiConfigStore.getWifiConfiguration(netId), bssid);
279    }
280
281    public void autoRoamSetBSSID(WifiConfiguration config, String bssid) {
282        mTargetRoamBSSID = "any";
283        if (config == null)
284            return;
285        if (config.bssidOwnerUid == 0 || config.bssidOwnerUid == Process.WIFI_UID) {
286            if (VDBG) {
287                loge("autoRoamSetBSSID uid=" + Long.toString(config.bssidOwnerUid)
288                        + " bssid=" + bssid
289                        + " key=" + config.configKey());
290            }
291            config.bssidOwnerUid = Process.WIFI_UID;
292            config.BSSID = bssid;
293            mTargetRoamBSSID = bssid;
294        }
295    }
296
297    static private final int ONE_HOUR_MILLI = 1000 * 60 * 60;
298
299    /**
300     * Subset of link properties coming from netlink.
301     * Currently includes IPv4 and IPv6 addresses. In the future will also include IPv6 DNS servers
302     * and domains obtained from router advertisements (RFC 6106).
303     */
304    private NetlinkTracker mNetlinkTracker;
305
306    private AlarmManager mAlarmManager;
307    private PendingIntent mScanIntent;
308    private PendingIntent mDriverStopIntent;
309    private PendingIntent mBatchedScanIntervalIntent;
310
311    /* Tracks current frequency mode */
312    private AtomicInteger mFrequencyBand = new AtomicInteger(WifiManager.WIFI_FREQUENCY_BAND_AUTO);
313
314    /* Tracks if we are filtering Multicast v4 packets. Default is to filter. */
315    private AtomicBoolean mFilteringMulticastV4Packets = new AtomicBoolean(true);
316
317    // Channel for sending replies.
318    private AsyncChannel mReplyChannel = new AsyncChannel();
319
320    private WifiP2pServiceImpl mWifiP2pServiceImpl;
321    private WifiPasspointServiceImpl mPasspointServiceImpl;
322
323    // Used to initiate a connection with WifiP2pService
324    private AsyncChannel mWifiP2pChannel;
325    private AsyncChannel mWifiApConfigChannel;
326
327    private WifiNetworkFactory mNetworkFactory;
328    private WifiNetworkAgent mNetworkAgent;
329
330    // Used to filter out requests we couldn't possibly satisfy.
331    private final NetworkCapabilities mNetworkCapabilitiesFilter = new NetworkCapabilities();
332
333    /* The base for wifi message types */
334    static final int BASE = Protocol.BASE_WIFI;
335    /* Start the supplicant */
336    static final int CMD_START_SUPPLICANT                 = BASE + 11;
337    /* Stop the supplicant */
338    static final int CMD_STOP_SUPPLICANT                  = BASE + 12;
339    /* Start the driver */
340    static final int CMD_START_DRIVER                     = BASE + 13;
341    /* Stop the driver */
342    static final int CMD_STOP_DRIVER                      = BASE + 14;
343    /* Indicates Static IP succeeded */
344    static final int CMD_STATIC_IP_SUCCESS                = BASE + 15;
345    /* Indicates Static IP failed */
346    static final int CMD_STATIC_IP_FAILURE                = BASE + 16;
347    /* Indicates supplicant stop failed */
348    static final int CMD_STOP_SUPPLICANT_FAILED           = BASE + 17;
349    /* Delayed stop to avoid shutting down driver too quick*/
350    static final int CMD_DELAYED_STOP_DRIVER              = BASE + 18;
351    /* A delayed message sent to start driver when it fail to come up */
352    static final int CMD_DRIVER_START_TIMED_OUT           = BASE + 19;
353
354    /* Start the soft access point */
355    static final int CMD_START_AP                         = BASE + 21;
356    /* Indicates soft ap start succeeded */
357    static final int CMD_START_AP_SUCCESS                 = BASE + 22;
358    /* Indicates soft ap start failed */
359    static final int CMD_START_AP_FAILURE                 = BASE + 23;
360    /* Stop the soft access point */
361    static final int CMD_STOP_AP                          = BASE + 24;
362    /* Set the soft access point configuration */
363    static final int CMD_SET_AP_CONFIG                    = BASE + 25;
364    /* Soft access point configuration set completed */
365    static final int CMD_SET_AP_CONFIG_COMPLETED          = BASE + 26;
366    /* Request the soft access point configuration */
367    static final int CMD_REQUEST_AP_CONFIG                = BASE + 27;
368    /* Response to access point configuration request */
369    static final int CMD_RESPONSE_AP_CONFIG               = BASE + 28;
370    /* Invoked when getting a tether state change notification */
371    static final int CMD_TETHER_STATE_CHANGE              = BASE + 29;
372    /* A delayed message sent to indicate tether state change failed to arrive */
373    static final int CMD_TETHER_NOTIFICATION_TIMED_OUT    = BASE + 30;
374
375    static final int CMD_BLUETOOTH_ADAPTER_STATE_CHANGE   = BASE + 31;
376
377    /* Supplicant commands */
378    /* Is supplicant alive ? */
379    static final int CMD_PING_SUPPLICANT                  = BASE + 51;
380    /* Add/update a network configuration */
381    static final int CMD_ADD_OR_UPDATE_NETWORK            = BASE + 52;
382    /* Delete a network */
383    static final int CMD_REMOVE_NETWORK                   = BASE + 53;
384    /* Enable a network. The device will attempt a connection to the given network. */
385    static final int CMD_ENABLE_NETWORK                   = BASE + 54;
386    /* Enable all networks */
387    static final int CMD_ENABLE_ALL_NETWORKS              = BASE + 55;
388    /* Blacklist network. De-prioritizes the given BSSID for connection. */
389    static final int CMD_BLACKLIST_NETWORK                = BASE + 56;
390    /* Clear the blacklist network list */
391    static final int CMD_CLEAR_BLACKLIST                  = BASE + 57;
392    /* Save configuration */
393    static final int CMD_SAVE_CONFIG                      = BASE + 58;
394    /* Get configured networks */
395    static final int CMD_GET_CONFIGURED_NETWORKS          = BASE + 59;
396    /* Get available frequencies */
397    static final int CMD_GET_CAPABILITY_FREQ              = BASE + 60;
398
399    /* Supplicant commands after driver start*/
400    /* Initiate a scan */
401    static final int CMD_START_SCAN                       = BASE + 71;
402    /* Set operational mode. CONNECT, SCAN ONLY, SCAN_ONLY with Wi-Fi off mode */
403    static final int CMD_SET_OPERATIONAL_MODE             = BASE + 72;
404    /* Disconnect from a network */
405    static final int CMD_DISCONNECT                       = BASE + 73;
406    /* Reconnect to a network */
407    static final int CMD_RECONNECT                        = BASE + 74;
408    /* Reassociate to a network */
409    static final int CMD_REASSOCIATE                      = BASE + 75;
410    /* Controls suspend mode optimizations
411     *
412     * When high perf mode is enabled, suspend mode optimizations are disabled
413     *
414     * When high perf mode is disabled, suspend mode optimizations are enabled
415     *
416     * Suspend mode optimizations include:
417     * - packet filtering
418     * - turn off roaming
419     * - DTIM wake up settings
420     */
421    static final int CMD_SET_HIGH_PERF_MODE               = BASE + 77;
422    /* Set the country code */
423    static final int CMD_SET_COUNTRY_CODE                 = BASE + 80;
424    /* Enables RSSI poll */
425    static final int CMD_ENABLE_RSSI_POLL                 = BASE + 82;
426    /* RSSI poll */
427    static final int CMD_RSSI_POLL                        = BASE + 83;
428    /* Set up packet filtering */
429    static final int CMD_START_PACKET_FILTERING           = BASE + 84;
430    /* Clear packet filter */
431    static final int CMD_STOP_PACKET_FILTERING            = BASE + 85;
432    /* Enable suspend mode optimizations in the driver */
433    static final int CMD_SET_SUSPEND_OPT_ENABLED          = BASE + 86;
434    /* When there are no saved networks, we do a periodic scan to notify user of
435     * an open network */
436    static final int CMD_NO_NETWORKS_PERIODIC_SCAN        = BASE + 88;
437
438    /* arg1 values to CMD_STOP_PACKET_FILTERING and CMD_START_PACKET_FILTERING */
439    static final int MULTICAST_V6  = 1;
440    static final int MULTICAST_V4  = 0;
441
442   /* Set the frequency band */
443    static final int CMD_SET_FREQUENCY_BAND               = BASE + 90;
444    /* Enable background scan for configured networks */
445    static final int CMD_ENABLE_BACKGROUND_SCAN           = BASE + 91;
446    /* Enable TDLS on a specific MAC address */
447    static final int CMD_ENABLE_TDLS                      = BASE + 92;
448
449    /* Commands from/to the SupplicantStateTracker */
450    /* Reset the supplicant state tracker */
451    static final int CMD_RESET_SUPPLICANT_STATE           = BASE + 111;
452
453    /* P2p commands */
454    /* We are ok with no response here since we wont do much with it anyway */
455    public static final int CMD_ENABLE_P2P                = BASE + 131;
456    /* In order to shut down supplicant cleanly, we wait till p2p has
457     * been disabled */
458    public static final int CMD_DISABLE_P2P_REQ           = BASE + 132;
459    public static final int CMD_DISABLE_P2P_RSP           = BASE + 133;
460
461    public static final int CMD_BOOT_COMPLETED            = BASE + 134;
462
463    /* change the batch scan settings.
464     * arg1 = responsible UID
465     * arg2 = csph (channel scans per hour)
466     * obj = bundle with the new settings and the optional worksource
467     */
468    public static final int CMD_SET_BATCHED_SCAN          = BASE + 135;
469    public static final int CMD_START_NEXT_BATCHED_SCAN   = BASE + 136;
470    public static final int CMD_POLL_BATCHED_SCAN         = BASE + 137;
471
472    /* Link configuration (IP address, DNS, ...) changes notified via netlink */
473    public static final int CMD_NETLINK_UPDATE            = BASE + 140;
474
475    /* Reload all networks and reconnect */
476    static final int CMD_RELOAD_TLS_AND_RECONNECT         = BASE + 142;
477
478    static final int CMD_AUTO_CONNECT                     = BASE + 143;
479
480    static final int CMD_UNWANTED_NETWORK                 = BASE + 144;
481
482    static final int CMD_AUTO_ROAM                        = BASE + 145;
483
484
485    /* Wifi state machine modes of operation */
486    /* CONNECT_MODE - connect to any 'known' AP when it becomes available */
487    public static final int CONNECT_MODE                   = 1;
488    /* SCAN_ONLY_MODE - don't connect to any APs; scan, but only while apps hold lock */
489    public static final int SCAN_ONLY_MODE                 = 2;
490    /* SCAN_ONLY_WITH_WIFI_OFF - scan, but don't connect to any APs */
491    public static final int SCAN_ONLY_WITH_WIFI_OFF_MODE   = 3;
492
493    private static final int SUCCESS = 1;
494    private static final int FAILURE = -1;
495
496    /**
497     * The maximum number of times we will retry a connection to an access point
498     * for which we have failed in acquiring an IP address from DHCP. A value of
499     * N means that we will make N+1 connection attempts in all.
500     * <p>
501     * See {@link Settings.Secure#WIFI_MAX_DHCP_RETRY_COUNT}. This is the default
502     * value if a Settings value is not present.
503     */
504    private static final int DEFAULT_MAX_DHCP_RETRIES = 9;
505
506    /* Tracks if suspend optimizations need to be disabled by DHCP,
507     * screen or due to high perf mode.
508     * When any of them needs to disable it, we keep the suspend optimizations
509     * disabled
510     */
511    private int mSuspendOptNeedsDisabled = 0;
512
513    private static final int SUSPEND_DUE_TO_DHCP       = 1;
514    private static final int SUSPEND_DUE_TO_HIGH_PERF  = 1<<1;
515    private static final int SUSPEND_DUE_TO_SCREEN     = 1<<2;
516
517    /* Tracks if user has enabled suspend optimizations through settings */
518    private AtomicBoolean mUserWantsSuspendOpt = new AtomicBoolean(true);
519
520    /**
521     * Default framework scan interval in milliseconds. This is used in the scenario in which
522     * wifi chipset does not support background scanning to set up a
523     * periodic wake up scan so that the device can connect to a new access
524     * point on the move. {@link Settings.Global#WIFI_FRAMEWORK_SCAN_INTERVAL_MS} can
525     * override this.
526     */
527    private final int mDefaultFrameworkScanIntervalMs;
528
529    /**
530     * Connected state framework scan interval in milliseconds.
531     * This is used for extended roaming, when screen is lit.
532     */
533    private int mConnectedScanPeriodMs = 10000;
534    private int mDisconnectedScanPeriodMs = 10000;
535
536    /**
537     * Supplicant scan interval in milliseconds.
538     * Comes from {@link Settings.Global#WIFI_SUPPLICANT_SCAN_INTERVAL_MS} or
539     * from the default config if the setting is not set
540     */
541    private long mSupplicantScanIntervalMs;
542
543    /**
544     * timeStamp of last full band scan we perfoemed for autojoin while connected with screen lit
545     */
546    private long lastFullBandConnectedTimeMilli;
547
548    /**
549     * time interval to the next full band scan we will perform for autojoin while connected with screen lit
550     */
551    private long fullBandConnectedTimeIntervalMilli;
552
553    /**
554     * max time interval to the next full band scan we will perform for autojoin while connected with screen lit
555     * Max time is 5 minutes
556     */
557    private static final long  maxFullBandConnectedTimeIntervalMilli = 1000 * 60 * 5;
558
559    /**
560     * Minimum time interval between enabling all networks.
561     * A device can end up repeatedly connecting to a bad network on screen on/off toggle
562     * due to enabling every time. We add a threshold to avoid this.
563     */
564    private static final int MIN_INTERVAL_ENABLE_ALL_NETWORKS_MS = 10 * 60 * 1000; /* 10 minutes */
565    private long mLastEnableAllNetworksTime;
566
567    /**
568     * Starting and shutting down driver too quick causes problems leading to driver
569     * being in a bad state. Delay driver stop.
570     */
571    private final int mDriverStopDelayMs;
572    private int mDelayedStopCounter;
573    private boolean mInDelayedStop = false;
574
575    // sometimes telephony gives us this data before boot is complete and we can't store it
576    // until after, so the write is deferred
577    private volatile String mPersistedCountryCode;
578
579    // Supplicant doesn't like setting the same country code multiple times (it may drop
580    // currently connected network), so we save the country code here to avoid redundency
581    private String mLastSetCountryCode;
582
583    /* Default parent state */
584    private State mDefaultState = new DefaultState();
585    /* Temporary initial state */
586    private State mInitialState = new InitialState();
587    /* Driver loaded, waiting for supplicant to start */
588    private State mSupplicantStartingState = new SupplicantStartingState();
589    /* Driver loaded and supplicant ready */
590    private State mSupplicantStartedState = new SupplicantStartedState();
591    /* Waiting for supplicant to stop and monitor to exit */
592    private State mSupplicantStoppingState = new SupplicantStoppingState();
593    /* Driver start issued, waiting for completed event */
594    private State mDriverStartingState = new DriverStartingState();
595    /* Driver started */
596    private State mDriverStartedState = new DriverStartedState();
597    /* Wait until p2p is disabled
598     * This is a special state which is entered right after we exit out of DriverStartedState
599     * before transitioning to another state.
600     */
601    private State mWaitForP2pDisableState = new WaitForP2pDisableState();
602    /* Driver stopping */
603    private State mDriverStoppingState = new DriverStoppingState();
604    /* Driver stopped */
605    private State mDriverStoppedState = new DriverStoppedState();
606    /* Scan for networks, no connection will be established */
607    private State mScanModeState = new ScanModeState();
608    /* Connecting to an access point */
609    private State mConnectModeState = new ConnectModeState();
610    /* Connected at 802.11 (L2) level */
611    private State mL2ConnectedState = new L2ConnectedState();
612    /* fetching IP after connection to access point (assoc+auth complete) */
613    private State mObtainingIpState = new ObtainingIpState();
614    /* Waiting for link quality verification to be complete */
615    private State mVerifyingLinkState = new VerifyingLinkState();
616    /* Connected with IP addr */
617    private State mConnectedState = new ConnectedState();
618    /* Roaming */
619    private State mRoamingState = new RoamingState();
620    /* disconnect issued, waiting for network disconnect confirmation */
621    private State mDisconnectingState = new DisconnectingState();
622    /* Network is not connected, supplicant assoc+auth is not complete */
623    private State mDisconnectedState = new DisconnectedState();
624    /* Waiting for WPS to be completed*/
625    private State mWpsRunningState = new WpsRunningState();
626
627    /* Soft ap is starting up */
628    private State mSoftApStartingState = new SoftApStartingState();
629    /* Soft ap is running */
630    private State mSoftApStartedState = new SoftApStartedState();
631    /* Soft ap is running and we are waiting for tether notification */
632    private State mTetheringState = new TetheringState();
633    /* Soft ap is running and we are tethered through connectivity service */
634    private State mTetheredState = new TetheredState();
635    /* Waiting for untether confirmation before stopping soft Ap */
636    private State mUntetheringState = new UntetheringState();
637
638    private class TetherStateChange {
639        ArrayList<String> available;
640        ArrayList<String> active;
641        TetherStateChange(ArrayList<String> av, ArrayList<String> ac) {
642            available = av;
643            active = ac;
644        }
645    }
646
647
648    /**
649     * One of  {@link WifiManager#WIFI_STATE_DISABLED},
650     *         {@link WifiManager#WIFI_STATE_DISABLING},
651     *         {@link WifiManager#WIFI_STATE_ENABLED},
652     *         {@link WifiManager#WIFI_STATE_ENABLING},
653     *         {@link WifiManager#WIFI_STATE_UNKNOWN}
654     *
655     */
656    private final AtomicInteger mWifiState = new AtomicInteger(WIFI_STATE_DISABLED);
657
658    /**
659     * One of  {@link WifiManager#WIFI_AP_STATE_DISABLED},
660     *         {@link WifiManager#WIFI_AP_STATE_DISABLING},
661     *         {@link WifiManager#WIFI_AP_STATE_ENABLED},
662     *         {@link WifiManager#WIFI_AP_STATE_ENABLING},
663     *         {@link WifiManager#WIFI_AP_STATE_FAILED}
664     *
665     */
666    private final AtomicInteger mWifiApState = new AtomicInteger(WIFI_AP_STATE_DISABLED);
667
668    private static final int SCAN_REQUEST = 0;
669    private static final String ACTION_START_SCAN =
670        "com.android.server.WifiManager.action.START_SCAN";
671
672    private static final String DELAYED_STOP_COUNTER = "DelayedStopCounter";
673    private static final int DRIVER_STOP_REQUEST = 0;
674    private static final String ACTION_DELAYED_DRIVER_STOP =
675        "com.android.server.WifiManager.action.DELAYED_DRIVER_STOP";
676
677    private static final String ACTION_REFRESH_BATCHED_SCAN =
678            "com.android.server.WifiManager.action.REFRESH_BATCHED_SCAN";
679    /**
680     * Keep track of whether WIFI is running.
681     */
682    private boolean mIsRunning = false;
683
684    /**
685     * Keep track of whether we last told the battery stats we had started.
686     */
687    private boolean mReportedRunning = false;
688
689    /**
690     * Most recently set source of starting WIFI.
691     */
692    private final WorkSource mRunningWifiUids = new WorkSource();
693
694    /**
695     * The last reported UIDs that were responsible for starting WIFI.
696     */
697    private final WorkSource mLastRunningWifiUids = new WorkSource();
698
699    private final IBatteryStats mBatteryStats;
700
701    private BatchedScanSettings mBatchedScanSettings = null;
702
703    /**
704     * Track the worksource/cost of the current settings and track what's been noted
705     * to the battery stats, so we can mark the end of the previous when changing.
706     */
707    private WorkSource mBatchedScanWorkSource = null;
708    private int mBatchedScanCsph = 0;
709    private WorkSource mNotedBatchedScanWorkSource = null;
710    private int mNotedBatchedScanCsph = 0;
711
712    private AtomicBoolean mFrameworkAutoJoin = new AtomicBoolean(true); //enable by default
713
714    public WifiStateMachine(Context context, String wlanInterface, WifiTrafficPoller trafficPoller) {
715        super("WifiStateMachine");
716        mContext = context;
717        mInterfaceName = wlanInterface;
718        mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, NETWORKTYPE, "");
719        mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
720                BatteryStats.SERVICE_NAME));
721
722        IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
723        mNwService = INetworkManagementService.Stub.asInterface(b);
724
725        mP2pSupported = mContext.getPackageManager().hasSystemFeature(
726                PackageManager.FEATURE_WIFI_DIRECT);
727
728        mWifiNative = new WifiNative(mInterfaceName);
729        mWifiConfigStore = new WifiConfigStore(context, mWifiNative);
730        mWifiAutoJoinController = new WifiAutoJoinController(context, this,
731                mWifiConfigStore, trafficPoller, mWifiNative);
732        mWifiMonitor = new WifiMonitor(this, mWifiNative);
733        mWifiInfo = new WifiInfo();
734        mSupplicantStateTracker = new SupplicantStateTracker(context, this, mWifiConfigStore,
735                getHandler());
736        mLinkProperties = new LinkProperties();
737
738        IBinder s1 = ServiceManager.getService(Context.WIFI_P2P_SERVICE);
739        mWifiP2pServiceImpl = (WifiP2pServiceImpl)IWifiP2pManager.Stub.asInterface(s1);
740
741        IBinder s2 = ServiceManager.getService(Context.WIFI_PASSPOINT_SERVICE);
742        mPasspointServiceImpl = (WifiPasspointServiceImpl)IWifiPasspointManager.Stub.asInterface(s2);
743
744        mNetworkInfo.setIsAvailable(false);
745        mLastBssid = null;
746        mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
747        mLastSignalLevel = -1;
748
749        mNetlinkTracker = new NetlinkTracker(mInterfaceName, new NetlinkTracker.Callback() {
750            public void update() {
751                sendMessage(CMD_NETLINK_UPDATE);
752            }
753        });
754        try {
755            mNwService.registerObserver(mNetlinkTracker);
756        } catch (RemoteException e) {
757            loge("Couldn't register netlink tracker: " + e.toString());
758        }
759
760        mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
761        Intent scanIntent = new Intent(ACTION_START_SCAN, null);
762        mScanIntent = PendingIntent.getBroadcast(mContext, SCAN_REQUEST, scanIntent, 0);
763
764        Intent batchedIntent = new Intent(ACTION_REFRESH_BATCHED_SCAN, null);
765        mBatchedScanIntervalIntent = PendingIntent.getBroadcast(mContext, 0, batchedIntent, 0);
766
767        mDefaultFrameworkScanIntervalMs = mContext.getResources().getInteger(
768                R.integer.config_wifi_framework_scan_interval);
769
770        mDriverStopDelayMs = mContext.getResources().getInteger(
771                R.integer.config_wifi_driver_stop_delay);
772
773        mBackgroundScanSupported = mContext.getResources().getBoolean(
774                R.bool.config_wifi_background_scan_support);
775
776        mPrimaryDeviceType = mContext.getResources().getString(
777                R.string.config_wifi_p2p_device_type);
778
779        mUserWantsSuspendOpt.set(Settings.Global.getInt(mContext.getContentResolver(),
780                    Settings.Global.WIFI_SUSPEND_OPTIMIZATIONS_ENABLED, 1) == 1);
781
782        mFrameworkAutoJoin.set(Settings.Global.getInt(mContext.getContentResolver(),
783                Settings.Global.WIFI_ENHANCED_AUTO_JOIN, 1) == 1);
784
785        mNetworkCapabilitiesFilter.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
786        mNetworkCapabilitiesFilter.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
787        mNetworkCapabilitiesFilter.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
788        mNetworkCapabilitiesFilter.setLinkUpstreamBandwidthKbps(1024 * 1024);
789        mNetworkCapabilitiesFilter.setLinkDownstreamBandwidthKbps(1024 * 1024);
790        // TODO - needs to be a bit more dynamic
791        mNetworkCapabilities = new NetworkCapabilities(mNetworkCapabilitiesFilter);
792
793        mContext.registerReceiver(
794            new BroadcastReceiver() {
795                @Override
796                public void onReceive(Context context, Intent intent) {
797                    ArrayList<String> available = intent.getStringArrayListExtra(
798                            ConnectivityManager.EXTRA_AVAILABLE_TETHER);
799                    ArrayList<String> active = intent.getStringArrayListExtra(
800                            ConnectivityManager.EXTRA_ACTIVE_TETHER);
801                    sendMessage(CMD_TETHER_STATE_CHANGE, new TetherStateChange(available, active));
802                }
803            },new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED));
804
805        mContext.registerReceiver(
806                new BroadcastReceiver() {
807                    @Override
808                    public void onReceive(Context context, Intent intent) {
809                        startScan(SCAN_ALARM_SOURCE, null, null);
810                        if (VDBG)
811                            loge("WiFiStateMachine SCAN ALARM");
812                    }
813                },
814                new IntentFilter(ACTION_START_SCAN));
815
816        IntentFilter filter = new IntentFilter();
817        filter.addAction(Intent.ACTION_SCREEN_ON);
818        filter.addAction(Intent.ACTION_SCREEN_OFF);
819        filter.addAction(ACTION_REFRESH_BATCHED_SCAN);
820        mContext.registerReceiver(
821                new BroadcastReceiver() {
822                    @Override
823                    public void onReceive(Context context, Intent intent) {
824                        String action = intent.getAction();
825
826                        if (action.equals(Intent.ACTION_SCREEN_ON)) {
827                            handleScreenStateChanged(true);
828                        } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
829                            handleScreenStateChanged(false);
830                        } else if (action.equals(ACTION_REFRESH_BATCHED_SCAN)) {
831                            startNextBatchedScanAsync();
832                        }
833                    }
834                }, filter);
835
836        mContext.registerReceiver(
837                new BroadcastReceiver() {
838                    @Override
839                    public void onReceive(Context context, Intent intent) {
840                       int counter = intent.getIntExtra(DELAYED_STOP_COUNTER, 0);
841                       sendMessage(CMD_DELAYED_STOP_DRIVER, counter, 0);
842                    }
843                },
844                new IntentFilter(ACTION_DELAYED_DRIVER_STOP));
845
846        mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor(
847                Settings.Global.WIFI_SUSPEND_OPTIMIZATIONS_ENABLED), false,
848                new ContentObserver(getHandler()) {
849                    @Override
850                    public void onChange(boolean selfChange) {
851                        mUserWantsSuspendOpt.set(Settings.Global.getInt(mContext.getContentResolver(),
852                                Settings.Global.WIFI_SUSPEND_OPTIMIZATIONS_ENABLED, 1) == 1);
853                    }
854                });
855
856        mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor(
857                        Settings.Global.WIFI_ENHANCED_AUTO_JOIN), false,
858                new ContentObserver(getHandler()) {
859                    @Override
860                    public void onChange(boolean selfChange) {
861                        mFrameworkAutoJoin.set(Settings.Global.getInt(mContext.getContentResolver(),
862                                Settings.Global.WIFI_ENHANCED_AUTO_JOIN, 0) == 1);
863                    }
864                });
865
866        mContext.registerReceiver(
867                new BroadcastReceiver() {
868                    @Override
869                    public void onReceive(Context context, Intent intent) {
870                        sendMessage(CMD_BOOT_COMPLETED);
871                    }
872                },
873                new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
874
875        mScanResultCache = new LruCache<String, ScanResult>(SCAN_RESULT_CACHE_SIZE);
876
877        PowerManager powerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
878        mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, getName());
879
880        mSuspendWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "WifiSuspend");
881        mSuspendWakeLock.setReferenceCounted(false);
882
883        addState(mDefaultState);
884            addState(mInitialState, mDefaultState);
885            addState(mSupplicantStartingState, mDefaultState);
886            addState(mSupplicantStartedState, mDefaultState);
887                addState(mDriverStartingState, mSupplicantStartedState);
888                addState(mDriverStartedState, mSupplicantStartedState);
889                    addState(mScanModeState, mDriverStartedState);
890                    addState(mConnectModeState, mDriverStartedState);
891                        addState(mL2ConnectedState, mConnectModeState);
892                            addState(mObtainingIpState, mL2ConnectedState);
893                            addState(mVerifyingLinkState, mL2ConnectedState);
894                            addState(mConnectedState, mL2ConnectedState);
895                            addState(mRoamingState, mL2ConnectedState);
896                        addState(mDisconnectingState, mConnectModeState);
897                        addState(mDisconnectedState, mConnectModeState);
898                        addState(mWpsRunningState, mConnectModeState);
899                addState(mWaitForP2pDisableState, mSupplicantStartedState);
900                addState(mDriverStoppingState, mSupplicantStartedState);
901                addState(mDriverStoppedState, mSupplicantStartedState);
902            addState(mSupplicantStoppingState, mDefaultState);
903            addState(mSoftApStartingState, mDefaultState);
904            addState(mSoftApStartedState, mDefaultState);
905                addState(mTetheringState, mSoftApStartedState);
906                addState(mTetheredState, mSoftApStartedState);
907                addState(mUntetheringState, mSoftApStartedState);
908
909        setInitialState(mInitialState);
910
911        setLogRecSize(2000);
912        setLogOnlyTransitions(false);
913        if (VDBG) setDbg(true);
914
915        //start the state machine
916        start();
917
918        final Intent intent = new Intent(WifiManager.WIFI_SCAN_AVAILABLE);
919        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
920        intent.putExtra(WifiManager.EXTRA_SCAN_AVAILABLE, WIFI_STATE_DISABLED);
921        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
922    }
923
924    private int mVerboseLoggingLevel = 0;
925
926    int getVerboseLoggingLevel() {
927        return mVerboseLoggingLevel;
928    }
929
930    void enableVerboseLogging(int verbose) {
931        mVerboseLoggingLevel = verbose;
932        if (verbose > 0) {
933            DBG = true;
934            VDBG = true;
935            PDBG = true;
936            mLogMessages = true;
937            mWifiNative.setSupplicantLogLevel("DEBUG");
938        } else {
939            DBG = false;
940            VDBG = false;
941            PDBG = false;
942            mLogMessages = false;
943            mWifiNative.setSupplicantLogLevel("INFO");
944        }
945        mWifiAutoJoinController.enableVerboseLogging(verbose);
946        mWifiMonitor.enableVerboseLogging(verbose);
947        mWifiNative.enableVerboseLogging(verbose);
948        mWifiConfigStore.enableVerboseLogging(verbose);
949        mSupplicantStateTracker.enableVerboseLogging(verbose);
950    }
951
952    private int mAggressiveHandover = 0;
953
954    int getAggressiveHandover() {
955        return mAggressiveHandover;
956    }
957
958    void enableAggressiveHandover(int enabled) {
959        mAggressiveHandover = enabled;
960    }
961
962    private int mAllowScansWithTraffic = 1;
963
964    public void setAllowScansWithTraffic(int enabled) {
965        mAllowScansWithTraffic = enabled;
966    }
967
968    public int getAllowScansWithTraffic() {
969        return mAllowScansWithTraffic;
970    }
971
972    /*
973     *
974     * Framework scan control
975     */
976
977    private boolean mAlarmEnabled = false;
978    /* This is set from the overlay config file or from a secure setting.
979     * A value of 0 disables scanning in the framework.
980     */
981    private long mFrameworkScanIntervalMs = 10000;
982
983    private long mCurrentScanAlarmMs = 10000;
984    private void setScanAlarm(boolean enabled) {
985        if (PDBG) {
986            loge("setScanAlarm " + enabled + " period " + mCurrentScanAlarmMs);
987        }
988        if (mCurrentScanAlarmMs <= 0) enabled = false;
989        if (enabled == mAlarmEnabled) return;
990        if (enabled) {
991            mAlarmManager.setRepeating(AlarmManager.RTC_WAKEUP,
992                    System.currentTimeMillis() + mCurrentScanAlarmMs,
993                    mCurrentScanAlarmMs,
994                    mScanIntent);
995            mAlarmEnabled = true;
996        } else {
997            mAlarmManager.cancel(mScanIntent);
998            mAlarmEnabled = false;
999        }
1000    }
1001
1002    /*********************************************************
1003     * Methods exposed for public use
1004     ********************************************************/
1005
1006    public Messenger getMessenger() {
1007        return new Messenger(getHandler());
1008    }
1009
1010    public WifiMonitor getWifiMonitor() {
1011        return mWifiMonitor;
1012    }
1013
1014    /**
1015     * TODO: doc
1016     */
1017    public boolean syncPingSupplicant(AsyncChannel channel) {
1018        Message resultMsg = channel.sendMessageSynchronously(CMD_PING_SUPPLICANT);
1019        boolean result = (resultMsg.arg1 != FAILURE);
1020        resultMsg.recycle();
1021        return result;
1022    }
1023
1024    public List<WifiChannel> syncGetChannelList(AsyncChannel channel) {
1025        Message resultMsg = channel.sendMessageSynchronously(CMD_GET_CAPABILITY_FREQ);
1026        List<WifiChannel> list = null;
1027        if (resultMsg.obj != null) {
1028            list = new ArrayList<WifiChannel>();
1029            String freqs = (String) resultMsg.obj;
1030            String[] lines = freqs.split("\n");
1031            for (String line : lines)
1032                if (line.contains("MHz")) {
1033                    // line format: " 52 = 5260 MHz (NO_IBSS) (DFS)"
1034                    WifiChannel c = new WifiChannel();
1035                    String[] prop = line.split(" ");
1036                    if (prop.length < 5) continue;
1037                    try {
1038                        c.channelNum = Integer.parseInt(prop[1]);
1039                        c.freqMHz = Integer.parseInt(prop[3]);
1040                    } catch (NumberFormatException e) { }
1041                    c.isDFS = line.contains("(DFS)");
1042                    list.add(c);
1043                } else if (line.contains("Mode[B] Channels:")) {
1044                    // B channels are the same as G channels, skipped
1045                    break;
1046                }
1047        }
1048        resultMsg.recycle();
1049        return (list != null && list.size() > 0) ? list : null;
1050    }
1051
1052
1053    /**
1054     * Initiate a wifi scan. If workSource is not null, blame is given to it, otherwise blame is
1055     * given to callingUid.
1056     *
1057     * @param callingUid The uid initiating the wifi scan. Blame will be given here unless
1058     *                   workSource is specified.
1059     * @param workSource If not null, blame is given to workSource.
1060     * @param settings Scan settings, see {@link ScanSettings}.
1061     */
1062    public void startScan(int callingUid, ScanSettings settings, WorkSource workSource) {
1063        Bundle bundle = new Bundle();
1064        bundle.putParcelable(CUSTOMIZED_SCAN_SETTING, settings);
1065        bundle.putParcelable(CUSTOMIZED_SCAN_WORKSOURCE, workSource);
1066        sendMessage(CMD_START_SCAN, callingUid, 0, bundle);
1067    }
1068
1069    /**
1070     * start or stop batched scanning using the given settings
1071     */
1072    public void setBatchedScanSettings(BatchedScanSettings settings, int callingUid, int csph,
1073            WorkSource workSource) {
1074        Bundle bundle = new Bundle();
1075        bundle.putParcelable(BATCHED_SETTING, settings);
1076        bundle.putParcelable(BATCHED_WORKSOURCE, workSource);
1077        sendMessage(CMD_SET_BATCHED_SCAN, callingUid, csph, bundle);
1078    }
1079
1080    public List<BatchedScanResult> syncGetBatchedScanResultsList() {
1081        synchronized (mBatchedScanResults) {
1082            List<BatchedScanResult> batchedScanList =
1083                    new ArrayList<BatchedScanResult>(mBatchedScanResults.size());
1084            for(BatchedScanResult result: mBatchedScanResults) {
1085                batchedScanList.add(new BatchedScanResult(result));
1086            }
1087            return batchedScanList;
1088        }
1089    }
1090
1091    public void requestBatchedScanPoll() {
1092        sendMessage(CMD_POLL_BATCHED_SCAN);
1093    }
1094
1095    private void startBatchedScan() {
1096        if (mBatchedScanSettings == null) return;
1097
1098        if (mDhcpActive) {
1099            if (DBG) log("not starting Batched Scans due to DHCP");
1100            return;
1101        }
1102
1103        // first grab any existing data
1104        retrieveBatchedScanData();
1105
1106        if (PDBG) loge("try  starting Batched Scans due to DHCP");
1107
1108
1109        mAlarmManager.cancel(mBatchedScanIntervalIntent);
1110
1111        String scansExpected = mWifiNative.setBatchedScanSettings(mBatchedScanSettings);
1112        try {
1113            mExpectedBatchedScans = Integer.parseInt(scansExpected);
1114            setNextBatchedAlarm(mExpectedBatchedScans);
1115            if (mExpectedBatchedScans > 0) noteBatchedScanStart();
1116        } catch (NumberFormatException e) {
1117            stopBatchedScan();
1118            loge("Exception parsing WifiNative.setBatchedScanSettings response " + e);
1119        }
1120    }
1121
1122    // called from BroadcastListener
1123    private void startNextBatchedScanAsync() {
1124        sendMessage(CMD_START_NEXT_BATCHED_SCAN);
1125    }
1126
1127    private void startNextBatchedScan() {
1128        // first grab any existing data
1129        retrieveBatchedScanData();
1130
1131        setNextBatchedAlarm(mExpectedBatchedScans);
1132    }
1133
1134    private void handleBatchedScanPollRequest() {
1135        if (DBG) {
1136            log("handleBatchedScanPoll Request - mBatchedScanMinPollTime=" +
1137                    mBatchedScanMinPollTime + " , mBatchedScanSettings=" +
1138                    mBatchedScanSettings);
1139        }
1140        // if there is no appropriate PollTime that's because we either aren't
1141        // batching or we've already set a time for a poll request
1142        if (mBatchedScanMinPollTime == 0) return;
1143        if (mBatchedScanSettings == null) return;
1144
1145        long now = System.currentTimeMillis();
1146
1147        if (now > mBatchedScanMinPollTime) {
1148            // do the poll and reset our timers
1149            startNextBatchedScan();
1150        } else {
1151            mAlarmManager.setExact(AlarmManager.RTC_WAKEUP, mBatchedScanMinPollTime,
1152                    mBatchedScanIntervalIntent);
1153            mBatchedScanMinPollTime = 0;
1154        }
1155    }
1156
1157    // return true if new/different
1158    private boolean recordBatchedScanSettings(int responsibleUid, int csph, Bundle bundle) {
1159        BatchedScanSettings settings = bundle.getParcelable(BATCHED_SETTING);
1160        WorkSource responsibleWorkSource = bundle.getParcelable(BATCHED_WORKSOURCE);
1161
1162        if (DBG) {
1163            log("set batched scan to " + settings + " for uid=" + responsibleUid +
1164                    ", worksource=" + responsibleWorkSource);
1165        }
1166        if (settings != null) {
1167            if (settings.equals(mBatchedScanSettings)) return false;
1168        } else {
1169            if (mBatchedScanSettings == null) return false;
1170        }
1171        mBatchedScanSettings = settings;
1172        if (responsibleWorkSource == null) responsibleWorkSource = new WorkSource(responsibleUid);
1173        mBatchedScanWorkSource = responsibleWorkSource;
1174        mBatchedScanCsph = csph;
1175        return true;
1176    }
1177
1178    private void stopBatchedScan() {
1179        mAlarmManager.cancel(mBatchedScanIntervalIntent);
1180        retrieveBatchedScanData();
1181        mWifiNative.setBatchedScanSettings(null);
1182        noteBatchedScanStop();
1183    }
1184
1185    private void setNextBatchedAlarm(int scansExpected) {
1186
1187        if (mBatchedScanSettings == null || scansExpected < 1) return;
1188
1189        mBatchedScanMinPollTime = System.currentTimeMillis() +
1190                mBatchedScanSettings.scanIntervalSec * 1000;
1191
1192        if (mBatchedScanSettings.maxScansPerBatch < scansExpected) {
1193            scansExpected = mBatchedScanSettings.maxScansPerBatch;
1194        }
1195
1196        int secToFull = mBatchedScanSettings.scanIntervalSec;
1197        secToFull *= scansExpected;
1198
1199        int debugPeriod = SystemProperties.getInt("wifi.batchedScan.pollPeriod", 0);
1200        if (debugPeriod > 0) secToFull = debugPeriod;
1201
1202        // set the alarm to do the next poll.  We set it a little short as we'd rather
1203        // wake up wearly than miss a scan due to buffer overflow
1204        mAlarmManager.setExact(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()
1205                + ((secToFull - (mBatchedScanSettings.scanIntervalSec / 2)) * 1000),
1206                mBatchedScanIntervalIntent);
1207    }
1208
1209    /**
1210     * Start reading new scan data
1211     * Data comes in as:
1212     * "scancount=5\n"
1213     * "nextcount=5\n"
1214     *   "apcount=3\n"
1215     *   "trunc\n" (optional)
1216     *     "bssid=...\n"
1217     *     "ssid=...\n"
1218     *     "freq=...\n" (in Mhz)
1219     *     "level=...\n"
1220     *     "dist=...\n" (in cm)
1221     *     "distsd=...\n" (standard deviation, in cm)
1222     *     "===="
1223     *     "bssid=...\n"
1224     *     etc
1225     *     "===="
1226     *     "bssid=...\n"
1227     *     etc
1228     *     "%%%%"
1229     *   "apcount=2\n"
1230     *     "bssid=...\n"
1231     *     etc
1232     *     "%%%%
1233     *   etc
1234     *   "----"
1235     */
1236    private final static boolean DEBUG_PARSE = false;
1237    private void retrieveBatchedScanData() {
1238        String rawData = mWifiNative.getBatchedScanResults();
1239        if (DEBUG_PARSE) log("rawData = " + rawData);
1240        mBatchedScanMinPollTime = 0;
1241        if (rawData == null || rawData.equalsIgnoreCase("OK")) {
1242            loge("Unexpected BatchedScanResults :" + rawData);
1243            return;
1244        }
1245
1246        int scanCount = 0;
1247        final String END_OF_BATCHES = "----";
1248        final String SCANCOUNT = "scancount=";
1249        final String TRUNCATED = "trunc";
1250        final String AGE = "age=";
1251        final String DIST = "dist=";
1252        final String DISTSD = "distSd=";
1253
1254        String splitData[] = rawData.split("\n");
1255        int n = 0;
1256        if (splitData[n].startsWith(SCANCOUNT)) {
1257            try {
1258                scanCount = Integer.parseInt(splitData[n++].substring(SCANCOUNT.length()));
1259            } catch (NumberFormatException e) {
1260                loge("scancount parseInt Exception from " + splitData[n]);
1261            }
1262        } else log("scancount not found");
1263        if (scanCount == 0) {
1264            loge("scanCount==0 - aborting");
1265            return;
1266        }
1267
1268        final Intent intent = new Intent(WifiManager.BATCHED_SCAN_RESULTS_AVAILABLE_ACTION);
1269        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1270
1271        synchronized (mBatchedScanResults) {
1272            mBatchedScanResults.clear();
1273            BatchedScanResult batchedScanResult = new BatchedScanResult();
1274
1275            String bssid = null;
1276            WifiSsid wifiSsid = null;
1277            int level = 0;
1278            int freq = 0;
1279            int dist, distSd;
1280            long tsf = 0;
1281            dist = distSd = ScanResult.UNSPECIFIED;
1282            final long now = SystemClock.elapsedRealtime();
1283            final int bssidStrLen = BSSID_STR.length();
1284
1285            while (true) {
1286                while (n < splitData.length) {
1287                    if (DEBUG_PARSE) logd("parsing " + splitData[n]);
1288                    if (splitData[n].equals(END_OF_BATCHES)) {
1289                        if (n+1 != splitData.length) {
1290                            loge("didn't consume " + (splitData.length-n));
1291                        }
1292                        if (mBatchedScanResults.size() > 0) {
1293                            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
1294                        }
1295                        logd("retrieveBatchedScanResults X");
1296                        return;
1297                    }
1298                    if ((splitData[n].equals(END_STR)) || splitData[n].equals(DELIMITER_STR)) {
1299                        if (bssid != null) {
1300                            batchedScanResult.scanResults.add(new ScanResult(
1301                                    wifiSsid, bssid, "", level, freq, tsf, dist, distSd));
1302                            wifiSsid = null;
1303                            bssid = null;
1304                            level = 0;
1305                            freq = 0;
1306                            tsf = 0;
1307                            dist = distSd = ScanResult.UNSPECIFIED;
1308                        }
1309                        if (splitData[n].equals(END_STR)) {
1310                            if (batchedScanResult.scanResults.size() != 0) {
1311                                mBatchedScanResults.add(batchedScanResult);
1312                                batchedScanResult = new BatchedScanResult();
1313                            } else {
1314                                logd("Found empty batch");
1315                            }
1316                        }
1317                    } else if (splitData[n].equals(TRUNCATED)) {
1318                        batchedScanResult.truncated = true;
1319                    } else if (splitData[n].startsWith(BSSID_STR)) {
1320                        bssid = new String(splitData[n].getBytes(), bssidStrLen,
1321                                splitData[n].length() - bssidStrLen);
1322                    } else if (splitData[n].startsWith(FREQ_STR)) {
1323                        try {
1324                            freq = Integer.parseInt(splitData[n].substring(FREQ_STR.length()));
1325                        } catch (NumberFormatException e) {
1326                            loge("Invalid freqency: " + splitData[n]);
1327                            freq = 0;
1328                        }
1329                    } else if (splitData[n].startsWith(AGE)) {
1330                        try {
1331                            tsf = now - Long.parseLong(splitData[n].substring(AGE.length()));
1332                            tsf *= 1000; // convert mS -> uS
1333                        } catch (NumberFormatException e) {
1334                            loge("Invalid timestamp: " + splitData[n]);
1335                            tsf = 0;
1336                        }
1337                    } else if (splitData[n].startsWith(SSID_STR)) {
1338                        wifiSsid = WifiSsid.createFromAsciiEncoded(
1339                                splitData[n].substring(SSID_STR.length()));
1340                    } else if (splitData[n].startsWith(LEVEL_STR)) {
1341                        try {
1342                            level = Integer.parseInt(splitData[n].substring(LEVEL_STR.length()));
1343                            if (level > 0) level -= 256;
1344                        } catch (NumberFormatException e) {
1345                            loge("Invalid level: " + splitData[n]);
1346                            level = 0;
1347                        }
1348                    } else if (splitData[n].startsWith(DIST)) {
1349                        try {
1350                            dist = Integer.parseInt(splitData[n].substring(DIST.length()));
1351                        } catch (NumberFormatException e) {
1352                            loge("Invalid distance: " + splitData[n]);
1353                            dist = ScanResult.UNSPECIFIED;
1354                        }
1355                    } else if (splitData[n].startsWith(DISTSD)) {
1356                        try {
1357                            distSd = Integer.parseInt(splitData[n].substring(DISTSD.length()));
1358                        } catch (NumberFormatException e) {
1359                            loge("Invalid distanceSd: " + splitData[n]);
1360                            distSd = ScanResult.UNSPECIFIED;
1361                        }
1362                    } else {
1363                        loge("Unable to parse batched scan result line: " + splitData[n]);
1364                    }
1365                    n++;
1366                }
1367                rawData = mWifiNative.getBatchedScanResults();
1368                if (DEBUG_PARSE) log("reading more data:\n" + rawData);
1369                if (rawData == null) {
1370                    loge("Unexpected null BatchedScanResults");
1371                    return;
1372                }
1373                splitData = rawData.split("\n");
1374                if (splitData.length == 0 || splitData[0].equals("ok")) {
1375                    loge("batch scan results just ended!");
1376                    if (mBatchedScanResults.size() > 0) {
1377                        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
1378                    }
1379                    return;
1380                }
1381                n = 0;
1382            }
1383        }
1384    }
1385
1386    // If workSource is not null, blame is given to it, otherwise blame is given to callingUid.
1387    private void noteScanStart(int callingUid, WorkSource workSource) {
1388        if (DBG) {
1389            long now = SystemClock.elapsedRealtimeNanos();
1390            String ts = String.format("[%,d us]", now/1000);
1391            if (workSource != null) {
1392                loge(ts + " noteScanStart" + workSource.toString()
1393                        + " uid " + Integer.toString(callingUid));
1394            } else {
1395                loge(ts + " noteScanstart no scan source");
1396            }
1397        }
1398        if (mScanWorkSource == null && ((callingUid != UNKNOWN_SCAN_SOURCE && callingUid != SCAN_ALARM_SOURCE)
1399                || workSource != null)) {
1400            mScanWorkSource = workSource != null ? workSource : new WorkSource(callingUid);
1401            try {
1402                mBatteryStats.noteWifiScanStartedFromSource(mScanWorkSource);
1403            } catch (RemoteException e) {
1404                log(e.toString());
1405            }
1406        }
1407    }
1408
1409    private void noteScanEnd() {
1410        if (DBG) {
1411            long now = SystemClock.elapsedRealtimeNanos();
1412            String ts = String.format("[%,d us]", now/1000);
1413
1414            if (mScanWorkSource != null)
1415                loge(ts + " noteScanEnd " + mScanWorkSource.toString());
1416            else
1417                loge(ts + " noteScanEnd no scan source");
1418        }
1419        if (mScanWorkSource != null) {
1420            try {
1421                mBatteryStats.noteWifiScanStoppedFromSource(mScanWorkSource);
1422            } catch (RemoteException e) {
1423                log(e.toString());
1424            } finally {
1425                mScanWorkSource = null;
1426            }
1427        }
1428    }
1429
1430    private void noteBatchedScanStart() {
1431        if (PDBG) loge("noteBatchedScanstart()");
1432        // note the end of a previous scan set
1433        if (mNotedBatchedScanWorkSource != null &&
1434                (mNotedBatchedScanWorkSource.equals(mBatchedScanWorkSource) == false ||
1435                 mNotedBatchedScanCsph != mBatchedScanCsph)) {
1436            try {
1437                mBatteryStats.noteWifiBatchedScanStoppedFromSource(mNotedBatchedScanWorkSource);
1438            } catch (RemoteException e) {
1439                log(e.toString());
1440            } finally {
1441                mNotedBatchedScanWorkSource = null;
1442                mNotedBatchedScanCsph = 0;
1443            }
1444        }
1445        // note the start of the new
1446        try {
1447            mBatteryStats.noteWifiBatchedScanStartedFromSource(mBatchedScanWorkSource,
1448                    mBatchedScanCsph);
1449            mNotedBatchedScanWorkSource = mBatchedScanWorkSource;
1450            mNotedBatchedScanCsph = mBatchedScanCsph;
1451        } catch (RemoteException e) {
1452            log(e.toString());
1453        }
1454    }
1455
1456    private void noteBatchedScanStop() {
1457        if (PDBG) loge("noteBatchedScanstop()");
1458
1459        if (mNotedBatchedScanWorkSource != null) {
1460            try {
1461                mBatteryStats.noteWifiBatchedScanStoppedFromSource(mNotedBatchedScanWorkSource);
1462            } catch (RemoteException e) {
1463                log(e.toString());
1464            } finally {
1465                mNotedBatchedScanWorkSource = null;
1466                mNotedBatchedScanCsph = 0;
1467            }
1468        }
1469    }
1470
1471    private void handleScanRequest(int type, Message message) {
1472        // unbundle parameters
1473        Bundle bundle = (Bundle) message.obj;
1474        ScanSettings settings = bundle.getParcelable(CUSTOMIZED_SCAN_SETTING);
1475        WorkSource workSource = bundle.getParcelable(CUSTOMIZED_SCAN_WORKSOURCE);
1476
1477        // parse scan settings
1478        String freqs = null;
1479        if (settings != null && settings.channelSet != null) {
1480            StringBuilder sb = new StringBuilder();
1481            boolean first = true;
1482            for (WifiChannel channel : settings.channelSet) {
1483                if (!first) sb.append(','); else first = false;
1484                sb.append(channel.freqMHz);
1485            }
1486            freqs = sb.toString();
1487        }
1488
1489        // call wifi native to start the scan
1490        if (startScanNative(type, freqs)) {
1491            // only count battery consumption if scan request is accepted
1492            noteScanStart(message.arg1, workSource);
1493            // a full scan covers everything, clearing scan request buffer
1494            if (freqs == null)
1495                mBufferedScanMsg.clear();
1496            return;
1497        }
1498
1499        // if reach here, scan request is rejected
1500
1501        if (!mIsScanOngoing) {
1502            // if rejection is NOT due to ongoing scan (e.g. bad scan parameters),
1503            // discard this request and pop up the next one
1504            if (mBufferedScanMsg.size() > 0)
1505                sendMessage(mBufferedScanMsg.remove());
1506        } else if (!mIsFullScanOngoing) {
1507            // if rejection is due to an ongoing scan, and the ongoing one is NOT a full scan,
1508            // buffer the scan request to make sure specified channels will be scanned eventually
1509            if (freqs == null)
1510                mBufferedScanMsg.clear();
1511            if (mBufferedScanMsg.size() < SCAN_REQUEST_BUFFER_MAX_SIZE) {
1512                Message msg = obtainMessage(CMD_START_SCAN, message.arg1, 0, bundle);
1513                mBufferedScanMsg.add(msg);
1514            } else {
1515                // if too many requests in buffer, combine them into a single full scan
1516                bundle = new Bundle();
1517                bundle.putParcelable(CUSTOMIZED_SCAN_SETTING, null);
1518                bundle.putParcelable(CUSTOMIZED_SCAN_WORKSOURCE, workSource);
1519                Message msg = obtainMessage(CMD_START_SCAN, message.arg1, 0, bundle);
1520                mBufferedScanMsg.clear();
1521                mBufferedScanMsg.add(msg);
1522            }
1523        }
1524    }
1525
1526
1527    /** return true iff scan request is accepted */
1528    private boolean startScanNative(int type, String freqs) {
1529        if (mWifiNative.scan(type, freqs)) {
1530            mIsScanOngoing = true;
1531            mIsFullScanOngoing = (freqs == null);
1532            return true;
1533        }
1534        return false;
1535    }
1536
1537    /**
1538     * TODO: doc
1539     */
1540    public void setSupplicantRunning(boolean enable) {
1541        if (enable) {
1542            sendMessage(CMD_START_SUPPLICANT);
1543        } else {
1544            sendMessage(CMD_STOP_SUPPLICANT);
1545        }
1546    }
1547
1548    /**
1549     * TODO: doc
1550     */
1551    public void setHostApRunning(WifiConfiguration wifiConfig, boolean enable) {
1552        if (enable) {
1553            sendMessage(CMD_START_AP, wifiConfig);
1554        } else {
1555            sendMessage(CMD_STOP_AP);
1556        }
1557    }
1558
1559    public void setWifiApConfiguration(WifiConfiguration config) {
1560        mWifiApConfigChannel.sendMessage(CMD_SET_AP_CONFIG, config);
1561    }
1562
1563    public WifiConfiguration syncGetWifiApConfiguration() {
1564        Message resultMsg = mWifiApConfigChannel.sendMessageSynchronously(CMD_REQUEST_AP_CONFIG);
1565        WifiConfiguration ret = (WifiConfiguration) resultMsg.obj;
1566        resultMsg.recycle();
1567        return ret;
1568    }
1569
1570    /**
1571     * TODO: doc
1572     */
1573    public int syncGetWifiState() {
1574        return mWifiState.get();
1575    }
1576
1577    /**
1578     * TODO: doc
1579     */
1580    public String syncGetWifiStateByName() {
1581        switch (mWifiState.get()) {
1582            case WIFI_STATE_DISABLING:
1583                return "disabling";
1584            case WIFI_STATE_DISABLED:
1585                return "disabled";
1586            case WIFI_STATE_ENABLING:
1587                return "enabling";
1588            case WIFI_STATE_ENABLED:
1589                return "enabled";
1590            case WIFI_STATE_UNKNOWN:
1591                return "unknown state";
1592            default:
1593                return "[invalid state]";
1594        }
1595    }
1596
1597    /**
1598     * TODO: doc
1599     */
1600    public int syncGetWifiApState() {
1601        return mWifiApState.get();
1602    }
1603
1604    /**
1605     * TODO: doc
1606     */
1607    public String syncGetWifiApStateByName() {
1608        switch (mWifiApState.get()) {
1609            case WIFI_AP_STATE_DISABLING:
1610                return "disabling";
1611            case WIFI_AP_STATE_DISABLED:
1612                return "disabled";
1613            case WIFI_AP_STATE_ENABLING:
1614                return "enabling";
1615            case WIFI_AP_STATE_ENABLED:
1616                return "enabled";
1617            case WIFI_AP_STATE_FAILED:
1618                return "failed";
1619            default:
1620                return "[invalid state]";
1621        }
1622    }
1623
1624    /**
1625     * Get status information for the current connection, if any.
1626     * @return a {@link WifiInfo} object containing information about the current connection
1627     *
1628     */
1629    public WifiInfo syncRequestConnectionInfo() {
1630        return mWifiInfo;
1631    }
1632
1633    public DhcpResults syncGetDhcpResults() {
1634        synchronized (mDhcpResultsLock) {
1635            return new DhcpResults(mDhcpResults);
1636        }
1637    }
1638
1639    /**
1640     * TODO: doc
1641     */
1642    public void setDriverStart(boolean enable) {
1643        if (enable) {
1644            sendMessage(CMD_START_DRIVER);
1645        } else {
1646            sendMessage(CMD_STOP_DRIVER);
1647        }
1648    }
1649
1650    /**
1651     * TODO: doc
1652     */
1653    public void setOperationalMode(int mode) {
1654        if (DBG) log("setting operational mode to " + String.valueOf(mode));
1655        sendMessage(CMD_SET_OPERATIONAL_MODE, mode, 0);
1656    }
1657
1658    /**
1659     * TODO: doc
1660     */
1661    public List<ScanResult> syncGetScanResultsList() {
1662        synchronized (mScanResultCache) {
1663            List<ScanResult> scanList = new ArrayList<ScanResult>();
1664            for(ScanResult result: mScanResults) {
1665                scanList.add(new ScanResult(result));
1666            }
1667            return scanList;
1668        }
1669    }
1670
1671    /**
1672     * Disconnect from Access Point
1673     */
1674    public void disconnectCommand() {
1675        sendMessage(CMD_DISCONNECT);
1676    }
1677
1678    /**
1679     * Initiate a reconnection to AP
1680     */
1681    public void reconnectCommand() {
1682        sendMessage(CMD_RECONNECT);
1683    }
1684
1685    /**
1686     * Initiate a re-association to AP
1687     */
1688    public void reassociateCommand() {
1689        sendMessage(CMD_REASSOCIATE);
1690    }
1691
1692    /**
1693     * Reload networks and then reconnect; helps load correct data for TLS networks
1694     */
1695
1696    public void reloadTlsNetworksAndReconnect() {
1697        sendMessage(CMD_RELOAD_TLS_AND_RECONNECT);
1698    }
1699
1700    /**
1701     * Add a network synchronously
1702     *
1703     * @return network id of the new network
1704     */
1705    public int syncAddOrUpdateNetwork(AsyncChannel channel, WifiConfiguration config) {
1706        Message resultMsg = channel.sendMessageSynchronously(CMD_ADD_OR_UPDATE_NETWORK, config);
1707        int result = resultMsg.arg1;
1708        resultMsg.recycle();
1709        return result;
1710    }
1711
1712    public List<WifiConfiguration> syncGetConfiguredNetworks(AsyncChannel channel) {
1713        Message resultMsg = channel.sendMessageSynchronously(CMD_GET_CONFIGURED_NETWORKS);
1714        List<WifiConfiguration> result = (List<WifiConfiguration>) resultMsg.obj;
1715        resultMsg.recycle();
1716        return result;
1717    }
1718
1719    /**
1720     * Delete a network
1721     *
1722     * @param networkId id of the network to be removed
1723     */
1724    public boolean syncRemoveNetwork(AsyncChannel channel, int networkId) {
1725        Message resultMsg = channel.sendMessageSynchronously(CMD_REMOVE_NETWORK, networkId);
1726        boolean result = (resultMsg.arg1 != FAILURE);
1727        resultMsg.recycle();
1728        return result;
1729    }
1730
1731    /**
1732     * Enable a network
1733     *
1734     * @param netId network id of the network
1735     * @param disableOthers true, if all other networks have to be disabled
1736     * @return {@code true} if the operation succeeds, {@code false} otherwise
1737     */
1738    public boolean syncEnableNetwork(AsyncChannel channel, int netId, boolean disableOthers) {
1739        Message resultMsg = channel.sendMessageSynchronously(CMD_ENABLE_NETWORK, netId,
1740                disableOthers ? 1 : 0);
1741        boolean result = (resultMsg.arg1 != FAILURE);
1742        resultMsg.recycle();
1743        return result;
1744    }
1745
1746    /**
1747     * Disable a network
1748     *
1749     * @param netId network id of the network
1750     * @return {@code true} if the operation succeeds, {@code false} otherwise
1751     */
1752    public boolean syncDisableNetwork(AsyncChannel channel, int netId) {
1753        Message resultMsg = channel.sendMessageSynchronously(WifiManager.DISABLE_NETWORK, netId);
1754        boolean result = (resultMsg.arg1 != WifiManager.DISABLE_NETWORK_FAILED);
1755        resultMsg.recycle();
1756        return result;
1757    }
1758
1759    /**
1760     * Retrieves a WPS-NFC configuration token for the specified network
1761     * @return a hex string representation of the WPS-NFC configuration token
1762     */
1763    public String syncGetWpsNfcConfigurationToken(int netId) {
1764        return mWifiNative.getNfcWpsConfigurationToken(netId);
1765    }
1766
1767    /**
1768     * Blacklist a BSSID. This will avoid the AP if there are
1769     * alternate APs to connect
1770     *
1771     * @param bssid BSSID of the network
1772     */
1773    public void addToBlacklist(String bssid) {
1774        sendMessage(CMD_BLACKLIST_NETWORK, bssid);
1775    }
1776
1777    /**
1778     * Clear the blacklist list
1779     *
1780     */
1781    public void clearBlacklist() {
1782        sendMessage(CMD_CLEAR_BLACKLIST);
1783    }
1784
1785    public void enableRssiPolling(boolean enabled) {
1786       sendMessage(CMD_ENABLE_RSSI_POLL, enabled ? 1 : 0, 0);
1787    }
1788
1789    public void enableBackgroundScanCommand(boolean enabled) {
1790       sendMessage(CMD_ENABLE_BACKGROUND_SCAN, enabled ? 1 : 0, 0);
1791    }
1792
1793    public void enableAllNetworks() {
1794        sendMessage(CMD_ENABLE_ALL_NETWORKS);
1795    }
1796
1797    /**
1798     * Start filtering Multicast v4 packets
1799     */
1800    public void startFilteringMulticastV4Packets() {
1801        mFilteringMulticastV4Packets.set(true);
1802        sendMessage(CMD_START_PACKET_FILTERING, MULTICAST_V4, 0);
1803    }
1804
1805    /**
1806     * Stop filtering Multicast v4 packets
1807     */
1808    public void stopFilteringMulticastV4Packets() {
1809        mFilteringMulticastV4Packets.set(false);
1810        sendMessage(CMD_STOP_PACKET_FILTERING, MULTICAST_V4, 0);
1811    }
1812
1813    /**
1814     * Start filtering Multicast v4 packets
1815     */
1816    public void startFilteringMulticastV6Packets() {
1817        sendMessage(CMD_START_PACKET_FILTERING, MULTICAST_V6, 0);
1818    }
1819
1820    /**
1821     * Stop filtering Multicast v4 packets
1822     */
1823    public void stopFilteringMulticastV6Packets() {
1824        sendMessage(CMD_STOP_PACKET_FILTERING, MULTICAST_V6, 0);
1825    }
1826
1827    /**
1828     * Set high performance mode of operation.
1829     * Enabling would set active power mode and disable suspend optimizations;
1830     * disabling would set auto power mode and enable suspend optimizations
1831     * @param enable true if enable, false otherwise
1832     */
1833    public void setHighPerfModeEnabled(boolean enable) {
1834        sendMessage(CMD_SET_HIGH_PERF_MODE, enable ? 1 : 0, 0);
1835    }
1836
1837    /**
1838     * Set the country code
1839     * @param countryCode following ISO 3166 format
1840     * @param persist {@code true} if the setting should be remembered.
1841     */
1842    public void setCountryCode(String countryCode, boolean persist) {
1843        // If it's a good country code, apply after the current
1844        // wifi connection is terminated; ignore resetting of code
1845        // for now (it is unclear what the chipset should do when
1846        // country code is reset)
1847        int countryCodeSequence = mCountryCodeSequence.incrementAndGet();
1848        if (TextUtils.isEmpty(countryCode)) {
1849            log("Ignoring resetting of country code");
1850        } else {
1851            sendMessage(CMD_SET_COUNTRY_CODE, countryCodeSequence, persist ? 1 : 0, countryCode);
1852        }
1853    }
1854
1855    /**
1856     * Set the operational frequency band
1857     * @param band
1858     * @param persist {@code true} if the setting should be remembered.
1859     */
1860    public void setFrequencyBand(int band, boolean persist) {
1861        if (persist) {
1862            Settings.Global.putInt(mContext.getContentResolver(),
1863                    Settings.Global.WIFI_FREQUENCY_BAND,
1864                    band);
1865        }
1866        sendMessage(CMD_SET_FREQUENCY_BAND, band, 0);
1867    }
1868
1869    /**
1870     * Enable TDLS for a specific MAC address
1871     */
1872    public void enableTdls(String remoteMacAddress, boolean enable) {
1873        int enabler = enable ? 1 : 0;
1874        sendMessage(CMD_ENABLE_TDLS, enabler, 0, remoteMacAddress);
1875    }
1876
1877    /**
1878     * Returns the operational frequency band
1879     */
1880    public int getFrequencyBand() {
1881        return mFrequencyBand.get();
1882    }
1883
1884    /**
1885     * Returns the wifi configuration file
1886     */
1887    public String getConfigFile() {
1888        return mWifiConfigStore.getConfigFile();
1889    }
1890
1891    /**
1892     * Send a message indicating bluetooth adapter connection state changed
1893     */
1894    public void sendBluetoothAdapterStateChange(int state) {
1895        sendMessage(CMD_BLUETOOTH_ADAPTER_STATE_CHANGE, state, 0);
1896    }
1897
1898    /**
1899     * Save configuration on supplicant
1900     *
1901     * @return {@code true} if the operation succeeds, {@code false} otherwise
1902     *
1903     * TODO: deprecate this
1904     */
1905    public boolean syncSaveConfig(AsyncChannel channel) {
1906        Message resultMsg = channel.sendMessageSynchronously(CMD_SAVE_CONFIG);
1907        boolean result = (resultMsg.arg1 != FAILURE);
1908        resultMsg.recycle();
1909        return result;
1910    }
1911
1912    public void updateBatteryWorkSource(WorkSource newSource) {
1913        synchronized (mRunningWifiUids) {
1914            try {
1915                if (newSource != null) {
1916                    mRunningWifiUids.set(newSource);
1917                }
1918                if (mIsRunning) {
1919                    if (mReportedRunning) {
1920                        // If the work source has changed since last time, need
1921                        // to remove old work from battery stats.
1922                        if (mLastRunningWifiUids.diff(mRunningWifiUids)) {
1923                            mBatteryStats.noteWifiRunningChanged(mLastRunningWifiUids,
1924                                    mRunningWifiUids);
1925                            mLastRunningWifiUids.set(mRunningWifiUids);
1926                        }
1927                    } else {
1928                        // Now being started, report it.
1929                        mBatteryStats.noteWifiRunning(mRunningWifiUids);
1930                        mLastRunningWifiUids.set(mRunningWifiUids);
1931                        mReportedRunning = true;
1932                    }
1933                } else {
1934                    if (mReportedRunning) {
1935                        // Last reported we were running, time to stop.
1936                        mBatteryStats.noteWifiStopped(mLastRunningWifiUids);
1937                        mLastRunningWifiUids.clear();
1938                        mReportedRunning = false;
1939                    }
1940                }
1941                mWakeLock.setWorkSource(newSource);
1942            } catch (RemoteException ignore) {
1943            }
1944        }
1945    }
1946
1947    @Override
1948    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1949        super.dump(fd, pw, args);
1950        mSupplicantStateTracker.dump(fd, pw, args);
1951        pw.println("mLinkProperties " + mLinkProperties);
1952        pw.println("mWifiInfo " + mWifiInfo);
1953        pw.println("mDhcpResults " + mDhcpResults);
1954        pw.println("mNetworkInfo " + mNetworkInfo);
1955        pw.println("mLastSignalLevel " + mLastSignalLevel);
1956        pw.println("mLastBssid " + mLastBssid);
1957        pw.println("mLastNetworkId " + mLastNetworkId);
1958        pw.println("mReconnectCount " + mReconnectCount);
1959        pw.println("mOperationalMode " + mOperationalMode);
1960        pw.println("mUserWantsSuspendOpt " + mUserWantsSuspendOpt);
1961        pw.println("mSuspendOptNeedsDisabled " + mSuspendOptNeedsDisabled);
1962        pw.println("Supplicant status " + mWifiNative.status());
1963        pw.println("mEnableBackgroundScan " + mEnableBackgroundScan);
1964        pw.println("mLastSetCountryCode " + mLastSetCountryCode);
1965        pw.println("mPersistedCountryCode " + mPersistedCountryCode);
1966        pw.println();
1967        mWifiConfigStore.dump(fd, pw, args);
1968    }
1969
1970    /*********************************************************
1971     * Internal private functions
1972     ********************************************************/
1973
1974    private void logStateAndMessage(Message message, String state) {
1975        if (mLogMessages) {
1976            //long now = SystemClock.elapsedRealtimeNanos();
1977            //String ts = String.format("[%,d us]", now/1000);
1978
1979            loge(/*ts + " " + */this.getClass().getSimpleName()
1980                    + "state:" + state + " what:" + Integer.toString(message.what, 16)
1981                    + " " + smToString(message) + " ");
1982        }
1983    }
1984
1985    private void handleScreenStateChanged(boolean screenOn) {
1986        mScreenOn = screenOn;
1987        if (PDBG) {
1988            loge(" handleScreenStateChanged Enter: screenOn=" + screenOn
1989                    + "mCurrentScanAlarmMs = " + Long.toString(mCurrentScanAlarmMs)
1990                    + " mUserWantsSuspendOpt=" + mUserWantsSuspendOpt
1991                    + " autojoin " + mFrameworkAutoJoin
1992                    + " state " + getCurrentState().getName()
1993                    + " suppState:" + mSupplicantStateTracker.getSupplicantStateName());
1994        }
1995        enableRssiPolling(screenOn);
1996        if (mBackgroundScanSupported) {
1997            enableBackgroundScanCommand(screenOn == false);
1998        }
1999
2000        if (screenOn) enableAllNetworks();
2001        if (mUserWantsSuspendOpt.get()) {
2002            if (screenOn) {
2003                sendMessage(CMD_SET_SUSPEND_OPT_ENABLED, 0, 0);
2004            } else {
2005                //Allow 2s for suspend optimizations to be set
2006                mSuspendWakeLock.acquire(2000);
2007                sendMessage(CMD_SET_SUSPEND_OPT_ENABLED, 1, 0);
2008            }
2009        }
2010        mScreenBroadcastReceived.set(true);
2011
2012        if (screenOn) {
2013            fullBandConnectedTimeIntervalMilli = 20 * 1000; //start at 20 seconds interval
2014            if (mFrameworkAutoJoin.get()) {
2015                //start the scan alarm so as to enable autojoin
2016                if (getCurrentState() == mConnectedState) {
2017                    mCurrentScanAlarmMs = mConnectedScanPeriodMs;
2018                } else if (getCurrentState() == mDisconnectedState) {
2019                    mCurrentScanAlarmMs = mDisconnectedScanPeriodMs;
2020                    //kick a scan right now
2021                    startScanNative(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, null);
2022                } else if (getCurrentState() == mDisconnectingState) {
2023                    mCurrentScanAlarmMs = mDisconnectedScanPeriodMs;
2024                    //kick a scan right now
2025                    startScanNative(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, null);
2026                }
2027            }
2028            setScanAlarm(true);
2029
2030        } else {
2031            setScanAlarm(false);
2032        }
2033
2034        if (DBG) log("handleScreenStateChanged Exit: " + screenOn);
2035    }
2036
2037    private void checkAndSetConnectivityInstance() {
2038        if (mCm == null) {
2039            mCm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
2040        }
2041    }
2042
2043    private boolean startTethering(ArrayList<String> available) {
2044
2045        boolean wifiAvailable = false;
2046
2047        checkAndSetConnectivityInstance();
2048
2049        String[] wifiRegexs = mCm.getTetherableWifiRegexs();
2050
2051        for (String intf : available) {
2052            for (String regex : wifiRegexs) {
2053                if (intf.matches(regex)) {
2054
2055                    InterfaceConfiguration ifcg = null;
2056                    try {
2057                        ifcg = mNwService.getInterfaceConfig(intf);
2058                        if (ifcg != null) {
2059                            /* IP/netmask: 192.168.43.1/255.255.255.0 */
2060                            ifcg.setLinkAddress(new LinkAddress(
2061                                    NetworkUtils.numericToInetAddress("192.168.43.1"), 24));
2062                            ifcg.setInterfaceUp();
2063
2064                            mNwService.setInterfaceConfig(intf, ifcg);
2065                        }
2066                    } catch (Exception e) {
2067                        loge("Error configuring interface " + intf + ", :" + e);
2068                        return false;
2069                    }
2070
2071                    if(mCm.tether(intf) != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
2072                        loge("Error tethering on " + intf);
2073                        return false;
2074                    }
2075                    mTetherInterfaceName = intf;
2076                    return true;
2077                }
2078            }
2079        }
2080        // We found no interfaces to tether
2081        return false;
2082    }
2083
2084    private void stopTethering() {
2085
2086        checkAndSetConnectivityInstance();
2087
2088        /* Clear the interface config to allow dhcp correctly configure new
2089           ip settings */
2090        InterfaceConfiguration ifcg = null;
2091        try {
2092            ifcg = mNwService.getInterfaceConfig(mTetherInterfaceName);
2093            if (ifcg != null) {
2094                ifcg.setLinkAddress(
2095                        new LinkAddress(NetworkUtils.numericToInetAddress("0.0.0.0"), 0));
2096                mNwService.setInterfaceConfig(mTetherInterfaceName, ifcg);
2097            }
2098        } catch (Exception e) {
2099            loge("Error resetting interface " + mTetherInterfaceName + ", :" + e);
2100        }
2101
2102        if (mCm.untether(mTetherInterfaceName) != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
2103            loge("Untether initiate failed!");
2104        }
2105    }
2106
2107    private boolean isWifiTethered(ArrayList<String> active) {
2108
2109        checkAndSetConnectivityInstance();
2110
2111        String[] wifiRegexs = mCm.getTetherableWifiRegexs();
2112        for (String intf : active) {
2113            for (String regex : wifiRegexs) {
2114                if (intf.matches(regex)) {
2115                    return true;
2116                }
2117            }
2118        }
2119        // We found no interfaces that are tethered
2120        return false;
2121    }
2122
2123    /**
2124     * Set the country code from the system setting value, if any.
2125     */
2126    private void setCountryCode() {
2127        String countryCode = Settings.Global.getString(mContext.getContentResolver(),
2128                Settings.Global.WIFI_COUNTRY_CODE);
2129        if (countryCode != null && !countryCode.isEmpty()) {
2130            setCountryCode(countryCode, false);
2131        } else {
2132            //use driver default
2133        }
2134    }
2135
2136    /**
2137     * Set the frequency band from the system setting value, if any.
2138     */
2139    private void setFrequencyBand() {
2140        int band = Settings.Global.getInt(mContext.getContentResolver(),
2141                Settings.Global.WIFI_FREQUENCY_BAND, WifiManager.WIFI_FREQUENCY_BAND_AUTO);
2142        setFrequencyBand(band, false);
2143    }
2144
2145    private void setSuspendOptimizationsNative(int reason, boolean enabled) {
2146        if (DBG) {
2147            log("setSuspendOptimizationsNative: " + reason + " " + enabled
2148                    + " -want " + mUserWantsSuspendOpt.get()
2149                    + " stack:" + Thread.currentThread().getStackTrace()[2].getMethodName()
2150                    +" - "+ Thread.currentThread().getStackTrace()[3].getMethodName()
2151                    +" - "+ Thread.currentThread().getStackTrace()[4].getMethodName()
2152                    +" - "+ Thread.currentThread().getStackTrace()[5].getMethodName());
2153        }
2154        mWifiNative.setSuspendOptimizations(enabled);
2155
2156        if (enabled) {
2157            mSuspendOptNeedsDisabled &= ~reason;
2158            /* None of dhcp, screen or highperf need it disabled and user wants it enabled */
2159            if (mSuspendOptNeedsDisabled == 0 && mUserWantsSuspendOpt.get()) {
2160                if (DBG) {
2161                    log("setSuspendOptimizationsNative do it " + reason + " " + enabled
2162                            + " stack:" + Thread.currentThread().getStackTrace()[2].getMethodName()
2163                            +" - "+ Thread.currentThread().getStackTrace()[3].getMethodName()
2164                            +" - "+ Thread.currentThread().getStackTrace()[4].getMethodName()
2165                            +" - "+ Thread.currentThread().getStackTrace()[5].getMethodName());
2166                }
2167                mWifiNative.setSuspendOptimizations(true);
2168            }
2169        } else {
2170            mSuspendOptNeedsDisabled |= reason;
2171            mWifiNative.setSuspendOptimizations(false);
2172        }
2173    }
2174
2175    private void setSuspendOptimizations(int reason, boolean enabled) {
2176        if (DBG) log("setSuspendOptimizations: " + reason + " " + enabled);
2177        if (enabled) {
2178            mSuspendOptNeedsDisabled &= ~reason;
2179        } else {
2180            mSuspendOptNeedsDisabled |= reason;
2181        }
2182        if (DBG) log("mSuspendOptNeedsDisabled " + mSuspendOptNeedsDisabled);
2183    }
2184
2185    private void setWifiState(int wifiState) {
2186        final int previousWifiState = mWifiState.get();
2187
2188        try {
2189            if (wifiState == WIFI_STATE_ENABLED) {
2190                mBatteryStats.noteWifiOn();
2191            } else if (wifiState == WIFI_STATE_DISABLED) {
2192                mBatteryStats.noteWifiOff();
2193            }
2194        } catch (RemoteException e) {
2195            loge("Failed to note battery stats in wifi");
2196        }
2197
2198        mWifiState.set(wifiState);
2199
2200        if (DBG) log("setWifiState: " + syncGetWifiStateByName());
2201
2202        final Intent intent = new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION);
2203        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
2204        intent.putExtra(WifiManager.EXTRA_WIFI_STATE, wifiState);
2205        intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_STATE, previousWifiState);
2206        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
2207    }
2208
2209    private void setWifiApState(int wifiApState) {
2210        final int previousWifiApState = mWifiApState.get();
2211
2212        try {
2213            if (wifiApState == WIFI_AP_STATE_ENABLED) {
2214                mBatteryStats.noteWifiOn();
2215            } else if (wifiApState == WIFI_AP_STATE_DISABLED) {
2216                mBatteryStats.noteWifiOff();
2217            }
2218        } catch (RemoteException e) {
2219            loge("Failed to note battery stats in wifi");
2220        }
2221
2222        // Update state
2223        mWifiApState.set(wifiApState);
2224
2225        if (DBG) log("setWifiApState: " + syncGetWifiApStateByName());
2226
2227        final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
2228        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
2229        intent.putExtra(WifiManager.EXTRA_WIFI_AP_STATE, wifiApState);
2230        intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_AP_STATE, previousWifiApState);
2231        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
2232    }
2233
2234    private static final String ID_STR = "id=";
2235    private static final String BSSID_STR = "bssid=";
2236    private static final String FREQ_STR = "freq=";
2237    private static final String LEVEL_STR = "level=";
2238    private static final String TSF_STR = "tsf=";
2239    private static final String FLAGS_STR = "flags=";
2240    private static final String SSID_STR = "ssid=";
2241    private static final String DELIMITER_STR = "====";
2242    private static final String END_STR = "####";
2243
2244    /**
2245     * Format:
2246     *
2247     * id=1
2248     * bssid=68:7f:76:d7:1a:6e
2249     * freq=2412
2250     * level=-44
2251     * tsf=1344626243700342
2252     * flags=[WPA2-PSK-CCMP][WPS][ESS]
2253     * ssid=zfdy
2254     * ====
2255     * id=2
2256     * bssid=68:5f:74:d7:1a:6f
2257     * freq=5180
2258     * level=-73
2259     * tsf=1344626243700373
2260     * flags=[WPA2-PSK-CCMP][WPS][ESS]
2261     * ssid=zuby
2262     * ====
2263     */
2264    private void setScanResults() {
2265        String bssid = "";
2266        int level = 0;
2267        int freq = 0;
2268        long tsf = 0;
2269        String flags = "";
2270        WifiSsid wifiSsid = null;
2271        String scanResults;
2272        String tmpResults;
2273        StringBuffer scanResultsBuf = new StringBuffer();
2274        int sid = 0;
2275
2276        while (true) {
2277            tmpResults = mWifiNative.scanResults(sid);
2278            if (TextUtils.isEmpty(tmpResults)) break;
2279            scanResultsBuf.append(tmpResults);
2280            scanResultsBuf.append("\n");
2281            String[] lines = tmpResults.split("\n");
2282            sid = -1;
2283            for (int i=lines.length - 1; i >= 0; i--) {
2284                if (lines[i].startsWith(END_STR)) {
2285                    break;
2286                } else if (lines[i].startsWith(ID_STR)) {
2287                    try {
2288                        sid = Integer.parseInt(lines[i].substring(ID_STR.length())) + 1;
2289                    } catch (NumberFormatException e) {
2290                        // Nothing to do
2291                    }
2292                    break;
2293                }
2294            }
2295            if (sid == -1) break;
2296        }
2297
2298        scanResults = scanResultsBuf.toString();
2299        if (TextUtils.isEmpty(scanResults)) {
2300           return;
2301        }
2302
2303        // note that all these splits and substrings keep references to the original
2304        // huge string buffer while the amount we really want is generally pretty small
2305        // so make copies instead (one example b/11087956 wasted 400k of heap here).
2306        synchronized(mScanResultCache) {
2307            mScanResults = new ArrayList<ScanResult>();
2308            String[] lines = scanResults.split("\n");
2309            final int bssidStrLen = BSSID_STR.length();
2310            final int flagLen = FLAGS_STR.length();
2311
2312            for (String line : lines) {
2313                if (line.startsWith(BSSID_STR)) {
2314                    bssid = new String(line.getBytes(), bssidStrLen, line.length() - bssidStrLen);
2315                } else if (line.startsWith(FREQ_STR)) {
2316                    try {
2317                        freq = Integer.parseInt(line.substring(FREQ_STR.length()));
2318                    } catch (NumberFormatException e) {
2319                        freq = 0;
2320                    }
2321                } else if (line.startsWith(LEVEL_STR)) {
2322                    try {
2323                        level = Integer.parseInt(line.substring(LEVEL_STR.length()));
2324                        /* some implementations avoid negative values by adding 256
2325                         * so we need to adjust for that here.
2326                         */
2327                        if (level > 0) level -= 256;
2328                    } catch(NumberFormatException e) {
2329                        level = 0;
2330                    }
2331                } else if (line.startsWith(TSF_STR)) {
2332                    try {
2333                        tsf = Long.parseLong(line.substring(TSF_STR.length()));
2334                    } catch (NumberFormatException e) {
2335                        tsf = 0;
2336                    }
2337                } else if (line.startsWith(FLAGS_STR)) {
2338                    flags = new String(line.getBytes(), flagLen, line.length() - flagLen);
2339                } else if (line.startsWith(SSID_STR)) {
2340                    wifiSsid = WifiSsid.createFromAsciiEncoded(
2341                            line.substring(SSID_STR.length()));
2342                } else if (line.startsWith(DELIMITER_STR) || line.startsWith(END_STR)) {
2343                    if (bssid != null) {
2344                        String ssid = (wifiSsid != null) ? wifiSsid.toString() : WifiSsid.NONE;
2345                        String key = bssid + ssid;
2346                        ScanResult scanResult = mScanResultCache.get(key);
2347                        if (scanResult != null) {
2348                            scanResult.level = level;
2349                            scanResult.wifiSsid = wifiSsid;
2350                            // Keep existing API
2351                            scanResult.SSID = (wifiSsid != null) ? wifiSsid.toString() :
2352                                    WifiSsid.NONE;
2353                            scanResult.capabilities = flags;
2354                            scanResult.frequency = freq;
2355                            scanResult.timestamp = tsf;
2356                            scanResult.seen = System.currentTimeMillis();
2357                        } else {
2358                            scanResult =
2359                                new ScanResult(
2360                                        wifiSsid, bssid, flags, level, freq, tsf);
2361                            mScanResultCache.put(key, scanResult);
2362                        }
2363                        mScanResults.add(scanResult);
2364                    }
2365                    bssid = null;
2366                    level = 0;
2367                    freq = 0;
2368                    tsf = 0;
2369                    flags = "";
2370                    wifiSsid = null;
2371                }
2372            }
2373        }
2374        if (mFrameworkAutoJoin.get() == true)
2375            mWifiAutoJoinController.newSupplicantResults();
2376
2377    }
2378
2379    /*
2380     * Fetch RSSI, linkspeed, and frequency on current connection
2381     */
2382    private void fetchRssiLinkSpeedAndFrequencyNative() {
2383        int newRssi = -1;
2384        int newLinkSpeed = -1;
2385        int newFrequency = -1;
2386
2387        String signalPoll = mWifiNative.signalPoll();
2388
2389        if (signalPoll != null) {
2390            String[] lines = signalPoll.split("\n");
2391            for (String line : lines) {
2392                String[] prop = line.split("=");
2393                if (prop.length < 2) continue;
2394                try {
2395                    if (prop[0].equals("RSSI")) {
2396                        newRssi = Integer.parseInt(prop[1]);
2397                    } else if (prop[0].equals("LINKSPEED")) {
2398                        newLinkSpeed = Integer.parseInt(prop[1]);
2399                    } else if (prop[0].equals("FREQUENCY")) {
2400                        newFrequency = Integer.parseInt(prop[1]);
2401                    }
2402                } catch (NumberFormatException e) {
2403                    //Ignore, defaults on rssi and linkspeed are assigned
2404                }
2405            }
2406        }
2407
2408        if (PDBG) {
2409            loge("fetchRssiLinkSpeedAndFrequencyNative rssi="
2410                    + Integer.toString(newRssi) + " linkspeed="
2411                    + Integer.toString(newLinkSpeed));
2412        }
2413
2414        if (newRssi > WifiInfo.INVALID_RSSI && newRssi < WifiInfo.MAX_RSSI) {
2415        // screen out invalid values
2416            /* some implementations avoid negative values by adding 256
2417             * so we need to adjust for that here.
2418             */
2419            if (newRssi > 0) newRssi -= 256;
2420            mWifiInfo.setRssi(newRssi);
2421            /*
2422             * Rather then sending the raw RSSI out every time it
2423             * changes, we precalculate the signal level that would
2424             * be displayed in the status bar, and only send the
2425             * broadcast if that much more coarse-grained number
2426             * changes. This cuts down greatly on the number of
2427             * broadcasts, at the cost of not informing others
2428             * interested in RSSI of all the changes in signal
2429             * level.
2430             */
2431            int newSignalLevel = WifiManager.calculateSignalLevel(newRssi, WifiManager.RSSI_LEVELS);
2432            if (newSignalLevel != mLastSignalLevel) {
2433                sendRssiChangeBroadcast(newRssi);
2434            }
2435            mLastSignalLevel = newSignalLevel;
2436        } else {
2437            mWifiInfo.setRssi(WifiInfo.INVALID_RSSI);
2438        }
2439
2440        if (newLinkSpeed != -1) {
2441            mWifiInfo.setLinkSpeed(newLinkSpeed);
2442        }
2443        if (newFrequency > 0) {
2444            mWifiInfo.setFrequency(newFrequency);
2445        }
2446    }
2447
2448    /* determine if we need to switch network:
2449     * - the delta determine the urgency to switch and/or or the expected evilness of the disruption
2450     * - match the uregncy of the switch versus the packet usage at the interface
2451     */
2452    boolean shouldSwitchNetwork(int networkDelta) {
2453        int delta;
2454        if (networkDelta <= 0) {
2455            return false;
2456        }
2457        delta = networkDelta;
2458        if (mWifiInfo != null) {
2459            //TODO: look at per AC packet count, do not switch if VO/VI traffic is present at the interface
2460            //TODO: discriminate between ucast and mcast, since the rxSuccessRate include all the bonjour and Ipv6
2461            //TODO: broadcasts
2462            if ((mWifiInfo.txSuccessRate > 20) || (mWifiInfo.rxSuccessRate > 80)) {
2463                delta -= 999;
2464            } else if ((mWifiInfo.txSuccessRate > 5) || (mWifiInfo.rxSuccessRate > 30)) {
2465                delta -= 6;
2466            }
2467            loge("WifiStateMachine shouldSwitchNetwork "
2468                    + " txSuccessRate=" +  String.format( "%.2f", mWifiInfo.txSuccessRate)
2469                    + " rxSuccessRate=" +String.format( "%.2f", mWifiInfo.rxSuccessRate)
2470                    + " delta " + networkDelta + " -> " + delta);
2471        } else {
2472            loge("WifiStateMachine shouldSwitchNetwork "
2473                    + " delta " + networkDelta + " -> " + delta);
2474        }
2475        if (delta > 0) {
2476            return true;
2477        }
2478        return false;
2479    }
2480
2481    private void calculateWifiScore(WifiLinkLayerStats stats) {
2482
2483        if (stats == null || mWifiLinkLayerStatsSupported <= 0) {
2484            long mTxPkts = TrafficStats.getTxPackets(mInterfaceName);
2485            long mRxPkts = TrafficStats.getRxPackets(mInterfaceName);
2486            mWifiInfo.updatePacketRates(mTxPkts, mRxPkts);
2487
2488        } else {
2489            mWifiInfo.updatePacketRates(stats);
2490        }
2491        int score = 56; //starting score, temporarily hardcoded in between 50 and 60
2492        boolean isBadLinkspeed = (mWifiInfo.is24GHz()
2493                && mWifiInfo.getLinkSpeed() < 6)
2494                || (mWifiInfo.is5GHz() && mWifiInfo.getLinkSpeed() < 12);
2495        boolean isGoodLinkspeed = (mWifiInfo.is24GHz()
2496                && mWifiInfo.getLinkSpeed() >= 24)
2497                || (mWifiInfo.is5GHz() && mWifiInfo.getLinkSpeed() >= 48);
2498
2499
2500        //we want to make sure that we use the 24GHz RSSI thresholds is
2501        //there are 2.4GHz scan results
2502        //otherwise we end up lowering the score based on 5GHz values
2503        //which may cause a switch to LTE before roaming has a chance to try 2.4GHz
2504        //We also might unblacklist the configuation based on 2.4GHz
2505        //thresholds but joining 5GHz anyhow, and failing over to 2.4GHz because 5GHz is not good
2506        boolean use24Thresholds = false;
2507        boolean homeNetworkBoost = false;
2508        WifiConfiguration currentConfiguration = getCurrentWifiConfiguration();
2509        if (currentConfiguration != null
2510                && currentConfiguration.scanResultCache != null) {
2511            currentConfiguration.setVisibility(12000);
2512            if (currentConfiguration.visibility != null) {
2513                if (currentConfiguration.visibility.rssi24 != WifiConfiguration.INVALID_RSSI
2514                        && currentConfiguration.visibility.rssi24
2515                        >= (currentConfiguration.visibility.rssi5-2)) {
2516                    use24Thresholds = true;
2517                }
2518            }
2519            if (currentConfiguration.scanResultCache.size() <= 4
2520                && currentConfiguration.allowedKeyManagement.cardinality() == 1
2521                && currentConfiguration.allowedKeyManagement.
2522                    get(WifiConfiguration.KeyMgmt.WPA_PSK) == true) {
2523                //a PSK network with less than 4 known BSSIDs
2524                //This is most likely a home network and thus we want to stick to wifi more
2525                homeNetworkBoost = true;
2526            }
2527        }
2528
2529        int rssi = mWifiInfo.getRssi() - 6 * mAggressiveHandover
2530                + (homeNetworkBoost ? WifiConfiguration.HOME_NETWORK_RSSI_BOOST : 0);
2531        boolean is24GHz = use24Thresholds || mWifiInfo.is24GHz();
2532
2533        boolean isBadRSSI = (is24GHz && rssi < WifiConfiguration.BAD_RSSI_24 )
2534                || (!is24GHz && rssi < WifiConfiguration.BAD_RSSI_5);
2535        boolean isLowRSSI = (is24GHz && rssi < WifiConfiguration.LOW_RSSI_24)
2536                || (!is24GHz && mWifiInfo.getRssi() < WifiConfiguration.LOW_RSSI_5);
2537        boolean isHighRSSI = (is24GHz && rssi >= WifiConfiguration.GOOD_RSSI_24)
2538                || (!is24GHz && mWifiInfo.getRssi() >= WifiConfiguration.GOOD_RSSI_5);
2539
2540        if (PDBG) {
2541            String rssiStatus = "";
2542            if (isBadRSSI) rssiStatus += " badRSSI ";
2543            else if (isHighRSSI) rssiStatus += " highRSSI ";
2544            else if (isLowRSSI) rssiStatus += " lowRSSI ";
2545            if (isBadLinkspeed) rssiStatus += " lowSpeed ";
2546            loge("calculateWifiScore freq=" + Integer.toString(mWifiInfo.getFrequency())
2547                            + " speed=" + Integer.toString(mWifiInfo.getLinkSpeed())
2548                            + " score=" + Integer.toString(mWifiInfo.score)
2549                            + rssiStatus
2550                            + " -> txbadrate=" + String.format( "%.2f", mWifiInfo.txBadRate )
2551                            + " txgoodrate=" + String.format("%.2f", mWifiInfo.txSuccessRate)
2552                            + " txretriesrate=" + String.format("%.2f", mWifiInfo.txRetriesRate)
2553                            + " rxrate=" + String.format("%.2f", mWifiInfo.rxSuccessRate)
2554            );
2555        }
2556
2557        if ((mWifiInfo.txBadRate >= 1) && (mWifiInfo.txSuccessRate < 3)
2558                && (isBadRSSI || isLowRSSI)) {
2559            //link is stuck
2560            if (mWifiInfo.linkStuckCount < 5)
2561                mWifiInfo.linkStuckCount += 1;
2562            if (PDBG) loge(" bad link -> stuck count ="
2563                    + Integer.toString(mWifiInfo.linkStuckCount));
2564        } else if (mWifiInfo.txSuccessRate > 2 || mWifiInfo.txBadRate < 0.1) {
2565            if (mWifiInfo.linkStuckCount > 0)
2566                mWifiInfo.linkStuckCount -= 1;
2567            if (PDBG) loge(" good link -> stuck count ="
2568                    + Integer.toString(mWifiInfo.linkStuckCount));
2569
2570        }
2571
2572        if (mWifiInfo.linkStuckCount > 1) {
2573            //once link gets stuck for more than 3 seconds, start reducing the score
2574            score = score - 2 * (mWifiInfo.linkStuckCount - 1);
2575        }
2576
2577        if (isBadLinkspeed) {
2578            score -= 4;
2579            if (PDBG) loge(" isBadLinkspeed   ---> score=" + Integer.toString(score));
2580        } else if ((isGoodLinkspeed) && (mWifiInfo.txSuccessRate > 5)) {
2581            score += 4; //so as bad rssi alone dont kill us
2582        }
2583
2584        if (isBadRSSI) {
2585            if (mWifiInfo.badRssiCount < 7)
2586                mWifiInfo.badRssiCount += 1;
2587        } else if (isLowRSSI) {
2588            mWifiInfo.lowRssiCount = 1; //dont increment
2589            if (mWifiInfo.badRssiCount > 0) {
2590                mWifiInfo.badRssiCount -= 1;
2591            }
2592        } else {
2593            mWifiInfo.badRssiCount = 0;
2594            mWifiInfo.lowRssiCount = 0;
2595        }
2596
2597        score -= mWifiInfo.badRssiCount * 2 +  mWifiInfo.lowRssiCount ;
2598
2599        if (PDBG) loge(" badRSSI count" + Integer.toString(mWifiInfo.badRssiCount)
2600                     + " lowRSSI count" + Integer.toString(mWifiInfo.lowRssiCount)
2601                        + " --> score " + Integer.toString(score));
2602
2603
2604        if (isHighRSSI) {
2605            score += 5;
2606            if (PDBG) loge(" isHighRSSI       ---> score=" + Integer.toString(score));
2607        }
2608
2609        //sanitize boundaries
2610        if (score > NetworkAgent.WIFI_BASE_SCORE)
2611            score = NetworkAgent.WIFI_BASE_SCORE;
2612        if (score < 0)
2613            score = 0;
2614
2615        //report score
2616        if (score != mWifiInfo.score) {
2617            if (DBG) {
2618                loge("calculateWifiScore() report new score " + Integer.toString(score));
2619            }
2620            mWifiInfo.score = score;
2621            mNetworkAgent.sendNetworkScore(score);
2622        }
2623    }
2624
2625    public double getTxPacketRate() {
2626        if (mWifiInfo != null) {
2627            return mWifiInfo.txSuccessRate;
2628        }
2629        return -1;
2630    }
2631
2632    public double getRxPacketRate() {
2633        if (mWifiInfo != null) {
2634            return mWifiInfo.rxSuccessRate;
2635        }
2636        return -1;
2637    }
2638
2639    /*
2640     * Fetch TX packet counters on current connection
2641     */
2642    private void fetchPktcntNative(RssiPacketCountInfo info) {
2643        String pktcntPoll = mWifiNative.pktcntPoll();
2644
2645        if (pktcntPoll != null) {
2646            String[] lines = pktcntPoll.split("\n");
2647            for (String line : lines) {
2648                String[] prop = line.split("=");
2649                if (prop.length < 2) continue;
2650                try {
2651                    if (prop[0].equals("TXGOOD")) {
2652                        info.txgood = Integer.parseInt(prop[1]);
2653                    } else if (prop[0].equals("TXBAD")) {
2654                        info.txbad = Integer.parseInt(prop[1]);
2655                    }
2656                } catch (NumberFormatException e) {
2657                    //Ignore
2658                }
2659            }
2660        }
2661    }
2662
2663    /**
2664     * Updates mLinkProperties by merging information from various sources.
2665     *
2666     * This is needed because the information in mLinkProperties comes from multiple sources (DHCP,
2667     * netlink, static configuration, ...). When one of these sources of information has updated
2668     * link properties, we can't just assign them to mLinkProperties or we'd lose track of the
2669     * information that came from other sources. Instead, when one of those sources has new
2670     * information, we update the object that tracks the information from that source and then
2671     * call this method to apply the change to mLinkProperties.
2672     *
2673     * The information in mLinkProperties is currently obtained as follows:
2674     * - Interface name: set in the constructor.
2675     * - IPv4 and IPv6 addresses: netlink, passed in by mNetlinkTracker.
2676     * - IPv4 routes, DNS servers, and domains: DHCP.
2677     * - IPv6 routes: netlink, passed in by mNetlinkTracker.
2678     * - HTTP proxy: the wifi config store.
2679     */
2680    private void updateLinkProperties() {
2681        LinkProperties newLp = new LinkProperties();
2682
2683        // Interface name and proxy are locally configured.
2684        newLp.setInterfaceName(mInterfaceName);
2685        newLp.setHttpProxy(mWifiConfigStore.getProxyProperties(mLastNetworkId));
2686
2687        // IPv4/v6 addresses and IPv6 routes come from netlink.
2688        LinkProperties netlinkLinkProperties = mNetlinkTracker.getLinkProperties();
2689        newLp.setLinkAddresses(netlinkLinkProperties.getLinkAddresses());
2690        for (RouteInfo route : netlinkLinkProperties.getRoutes()) {
2691            newLp.addRoute(route);
2692        }
2693
2694        // For now, DNS only comes from DHCP or static configuration. In the future, we'll need to
2695        // merge IPv6 DNS servers and domains coming from netlink.
2696        synchronized (mDhcpResultsLock) {
2697            // Even when we're using static configuration, we don't need to look at the config
2698            // store, because static IP configuration also populates mDhcpResults.
2699            if ((mDhcpResults != null) && (mDhcpResults.linkProperties != null)) {
2700                LinkProperties lp = mDhcpResults.linkProperties;
2701                for (RouteInfo route : lp.getRoutes()) {
2702                    newLp.addRoute(route);
2703                }
2704                for (InetAddress dns : lp.getDnsServers()) {
2705                    newLp.addDnsServer(dns);
2706                }
2707                newLp.setDomains(lp.getDomains());
2708            }
2709        }
2710
2711        // If anything has changed, and we're already connected, send out a notification.
2712        // If we're still connecting, apps will be notified when we connect.
2713        if (!newLp.equals(mLinkProperties)) {
2714            if (DBG) {
2715                log("Link configuration changed for netId: " + mLastNetworkId
2716                        + " old: " + mLinkProperties + "new: " + newLp);
2717            }
2718            mLinkProperties = newLp;
2719            if (mNetworkAgent != null) mNetworkAgent.sendLinkProperties(mLinkProperties);
2720            if (getNetworkDetailedState() == DetailedState.CONNECTED) {
2721                sendLinkConfigurationChangedBroadcast();
2722            }
2723        }
2724    }
2725    /**
2726     * Clears all our link properties.
2727     */
2728     private void clearLinkProperties() {
2729         // If the network used DHCP, clear the LinkProperties we stored in the config store.
2730         if (!mWifiConfigStore.isUsingStaticIp(mLastNetworkId)) {
2731             mWifiConfigStore.clearLinkProperties(mLastNetworkId);
2732         }
2733
2734         // Clear the link properties obtained from DHCP and netlink.
2735         synchronized (mDhcpResultsLock) {
2736             if (mDhcpResults != null && mDhcpResults.linkProperties != null) {
2737                 mDhcpResults.linkProperties.clear();
2738             }
2739         }
2740         mNetlinkTracker.clearLinkProperties();
2741
2742         // Now clear the merged link properties.
2743         mLinkProperties.clear();
2744         if (mNetworkAgent != null) mNetworkAgent.sendLinkProperties(mLinkProperties);
2745     }
2746
2747     /**
2748      * try to update default route MAC address.
2749      */
2750      private String updateDefaultRouteMacAddress(int timeout) {
2751          String address = null;
2752          for (RouteInfo route : mLinkProperties.getRoutes()) {
2753              if (route.isDefaultRoute() && route.hasGateway()) {
2754                  InetAddress gateway = route.getGateway();
2755                  if (gateway instanceof Inet4Address) {
2756                      if (PDBG) {
2757                          loge("updateDefaultRouteMacAddress found Ipv4 default :"
2758                                  + gateway.getHostAddress());
2759                      }
2760                      address = macAddressFromRoute(gateway.getHostAddress());
2761                     /* the gateway's MAC address is known */
2762                      if ((address == null) && (timeout > 0)) {
2763                          boolean reachable = false;
2764                          try {
2765                              reachable = gateway.isReachable(timeout);
2766                          } catch (Exception e) {
2767                              loge("updateDefaultRouteMacAddress exception reaching :"
2768                                      + gateway.getHostAddress());
2769
2770                          } finally {
2771                              if (reachable == true) {
2772
2773                                  address = macAddressFromRoute(gateway.getHostAddress());
2774                                  if (PDBG) {
2775                                      loge("updateDefaultRouteMacAddress reachable (tried again) :"
2776                                              + gateway.getHostAddress() + " found " + address);
2777                                  }
2778                              }
2779                          }
2780                      }
2781                      if (address != null) {
2782                          mWifiConfigStore.setLinkProperties(mLastNetworkId,
2783                                  new LinkProperties(mLinkProperties));
2784                          mWifiConfigStore.setDefaultGwMacAddress(mLastNetworkId, address);
2785
2786                      }
2787                  }
2788              }
2789          }
2790          return address;
2791      }
2792
2793    private int getMaxDhcpRetries() {
2794        return Settings.Global.getInt(mContext.getContentResolver(),
2795                                      Settings.Global.WIFI_MAX_DHCP_RETRY_COUNT,
2796                                      DEFAULT_MAX_DHCP_RETRIES);
2797    }
2798
2799    private void sendScanResultsAvailableBroadcast() {
2800        noteScanEnd();
2801        Intent intent = new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
2802        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
2803        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
2804    }
2805
2806    private void sendRssiChangeBroadcast(final int newRssi) {
2807        try {
2808            mBatteryStats.noteWifiRssiChanged(newRssi);
2809        } catch (RemoteException e) {
2810            // Won't happen.
2811        }
2812        Intent intent = new Intent(WifiManager.RSSI_CHANGED_ACTION);
2813        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
2814        intent.putExtra(WifiManager.EXTRA_NEW_RSSI, newRssi);
2815        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
2816    }
2817
2818    private void sendNetworkStateChangeBroadcast(String bssid) {
2819        Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
2820        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
2821        intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, new NetworkInfo(mNetworkInfo));
2822        intent.putExtra(WifiManager.EXTRA_LINK_PROPERTIES, new LinkProperties (mLinkProperties));
2823        if (bssid != null)
2824            intent.putExtra(WifiManager.EXTRA_BSSID, bssid);
2825        if (mNetworkInfo.getDetailedState() == DetailedState.VERIFYING_POOR_LINK ||
2826                mNetworkInfo.getDetailedState() == DetailedState.CONNECTED) {
2827            intent.putExtra(WifiManager.EXTRA_WIFI_INFO, new WifiInfo(mWifiInfo));
2828        }
2829        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
2830    }
2831
2832    private void sendLinkConfigurationChangedBroadcast() {
2833        Intent intent = new Intent(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION);
2834        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
2835        intent.putExtra(WifiManager.EXTRA_LINK_PROPERTIES, new LinkProperties(mLinkProperties));
2836        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
2837    }
2838
2839    private void sendSupplicantConnectionChangedBroadcast(boolean connected) {
2840        Intent intent = new Intent(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
2841        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
2842        intent.putExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, connected);
2843        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
2844    }
2845
2846    /**
2847     * Record the detailed state of a network.
2848     * @param state the new {@code DetailedState}
2849     */
2850    private void setNetworkDetailedState(NetworkInfo.DetailedState state) {
2851        if (DBG) {
2852            log("setDetailed state, old ="
2853                    + mNetworkInfo.getDetailedState() + " and new state=" + state);
2854        }
2855
2856        if (state != mNetworkInfo.getDetailedState()) {
2857            mNetworkInfo.setDetailedState(state, null, mWifiInfo.getSSID());
2858            if (mNetworkAgent != null) {
2859                if (!isRoaming() ||
2860                        (state != DetailedState.DISCONNECTED && state != DetailedState.DISCONNECTING) ) {
2861                    //don't tell the Network agent if we are doing a disconnect-roam
2862                    mNetworkAgent.sendNetworkInfo(mNetworkInfo);
2863                }
2864            }
2865        }
2866    }
2867
2868    private DetailedState getNetworkDetailedState() {
2869        return mNetworkInfo.getDetailedState();
2870    }
2871
2872
2873    private SupplicantState handleSupplicantStateChange(Message message) {
2874        StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
2875        SupplicantState state = stateChangeResult.state;
2876        // Supplicant state change
2877        // [31-13] Reserved for future use
2878        // [8 - 0] Supplicant state (as defined in SupplicantState.java)
2879        // 50023 supplicant_state_changed (custom|1|5)
2880        mWifiInfo.setSupplicantState(state);
2881        // Network id is only valid when we start connecting
2882        if (SupplicantState.isConnecting(state)) {
2883            mWifiInfo.setNetworkId(stateChangeResult.networkId);
2884        } else {
2885            mWifiInfo.setNetworkId(WifiConfiguration.INVALID_NETWORK_ID);
2886        }
2887
2888        mWifiInfo.setBSSID(stateChangeResult.BSSID);
2889        mWifiInfo.setSSID(stateChangeResult.wifiSsid);
2890
2891        mSupplicantStateTracker.sendMessage(Message.obtain(message));
2892
2893        return state;
2894    }
2895
2896    /**
2897     * Resets the Wi-Fi Connections by clearing any state, resetting any sockets
2898     * using the interface, stopping DHCP & disabling interface
2899     */
2900    private void handleNetworkDisconnect() {
2901        if (DBG) log("handleNetworkDisconnect: Stopping DHCP and clearing IP"
2902                + " stack:" + Thread.currentThread().getStackTrace()[2].getMethodName()
2903                +" - "+ Thread.currentThread().getStackTrace()[3].getMethodName()
2904                +" - "+ Thread.currentThread().getStackTrace()[4].getMethodName()
2905                +" - "+ Thread.currentThread().getStackTrace()[5].getMethodName());
2906
2907        stopDhcp();
2908
2909        try {
2910            mNwService.clearInterfaceAddresses(mInterfaceName);
2911            mNwService.disableIpv6(mInterfaceName);
2912        } catch (Exception e) {
2913            loge("Failed to clear addresses or disable ipv6" + e);
2914        }
2915
2916        /* Reset data structures */
2917        mWifiInfo.reset();
2918
2919        setNetworkDetailedState(DetailedState.DISCONNECTED);
2920        if (mNetworkAgent != null) {
2921            mNetworkAgent.sendNetworkInfo(mNetworkInfo);
2922            mNetworkAgent = null;
2923        }
2924        mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.DISCONNECTED);
2925
2926        /* Clear network properties */
2927        clearLinkProperties();
2928
2929        /* send event to CM & network change broadcast */
2930        sendNetworkStateChangeBroadcast(mLastBssid);
2931
2932        //cancel auto roam requests
2933        autoRoamSetBSSID(mLastNetworkId, "any");
2934
2935        //reset roaming parameters
2936        mAutoRoaming = WifiAutoJoinController.AUTO_JOIN_IDLE;
2937        fullBandConnectedTimeIntervalMilli = 20 * 1000; //start at 20 seconds interval
2938
2939        mLastBssid= null;
2940        registerDisconnected();
2941        mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
2942    }
2943
2944    private void handleSupplicantConnectionLoss() {
2945        /* Socket connection can be lost when we do a graceful shutdown
2946        * or when the driver is hung. Ensure supplicant is stopped here.
2947        */
2948        mWifiMonitor.killSupplicant(mP2pSupported);
2949        mWifiNative.closeSupplicantConnection();
2950        sendSupplicantConnectionChangedBroadcast(false);
2951        setWifiState(WIFI_STATE_DISABLED);
2952    }
2953
2954    void handlePreDhcpSetup() {
2955        mDhcpActive = true;
2956        if (!mBluetoothConnectionActive) {
2957            /*
2958             * There are problems setting the Wi-Fi driver's power
2959             * mode to active when bluetooth coexistence mode is
2960             * enabled or sense.
2961             * <p>
2962             * We set Wi-Fi to active mode when
2963             * obtaining an IP address because we've found
2964             * compatibility issues with some routers with low power
2965             * mode.
2966             * <p>
2967             * In order for this active power mode to properly be set,
2968             * we disable coexistence mode until we're done with
2969             * obtaining an IP address.  One exception is if we
2970             * are currently connected to a headset, since disabling
2971             * coexistence would interrupt that connection.
2972             */
2973            // Disable the coexistence mode
2974            mWifiNative.setBluetoothCoexistenceMode(
2975                    mWifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED);
2976        }
2977
2978        /* Disable power save and suspend optimizations during DHCP */
2979        // Note: The order here is important for now. Brcm driver changes
2980        // power settings when we control suspend mode optimizations.
2981        // TODO: Remove this comment when the driver is fixed.
2982        setSuspendOptimizationsNative(SUSPEND_DUE_TO_DHCP, false);
2983        mWifiNative.setPowerSave(false);
2984
2985        stopBatchedScan();
2986        WifiNative.pauseScan();
2987
2988        /* P2p discovery breaks dhcp, shut it down in order to get through this */
2989        Message msg = new Message();
2990        msg.what = WifiP2pServiceImpl.BLOCK_DISCOVERY;
2991        msg.arg1 = WifiP2pServiceImpl.ENABLED;
2992        msg.arg2 = DhcpStateMachine.CMD_PRE_DHCP_ACTION_COMPLETE;
2993        msg.obj = mDhcpStateMachine;
2994        mWifiP2pChannel.sendMessage(msg);
2995    }
2996
2997
2998    void startDhcp() {
2999        if (mDhcpStateMachine == null) {
3000            mDhcpStateMachine = DhcpStateMachine.makeDhcpStateMachine(
3001                    mContext, WifiStateMachine.this, mInterfaceName);
3002
3003        }
3004        mDhcpStateMachine.registerForPreDhcpNotification();
3005        mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_START_DHCP);
3006    }
3007
3008    void renewDhcp() {
3009        if (mDhcpStateMachine == null) {
3010            mDhcpStateMachine = DhcpStateMachine.makeDhcpStateMachine(
3011                    mContext, WifiStateMachine.this, mInterfaceName);
3012
3013        }
3014        mDhcpStateMachine.registerForPreDhcpNotification();
3015        mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_RENEW_DHCP);
3016    }
3017
3018    void stopDhcp() {
3019        if (mDhcpStateMachine != null) {
3020            /* In case we were in middle of DHCP operation restore back powermode */
3021            handlePostDhcpSetup();
3022            mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_STOP_DHCP);
3023        }
3024    }
3025
3026    void handlePostDhcpSetup() {
3027        /* Restore power save and suspend optimizations */
3028        setSuspendOptimizationsNative(SUSPEND_DUE_TO_DHCP, true);
3029        mWifiNative.setPowerSave(true);
3030
3031        mWifiP2pChannel.sendMessage(WifiP2pServiceImpl.BLOCK_DISCOVERY, WifiP2pServiceImpl.DISABLED);
3032
3033        // Set the coexistence mode back to its default value
3034        mWifiNative.setBluetoothCoexistenceMode(
3035                mWifiNative.BLUETOOTH_COEXISTENCE_MODE_SENSE);
3036
3037        mDhcpActive = false;
3038
3039        startBatchedScan();
3040        WifiNative.restartScan();
3041    }
3042
3043    private void handleSuccessfulIpConfiguration(DhcpResults dhcpResults) {
3044
3045        if (PDBG) {
3046            loge("handleSuccessfulIpConfiguration <" + dhcpResults.toString()
3047                    + "> linkaddress num " + dhcpResults.linkProperties.getLinkAddresses().size());
3048            for (LinkAddress linkAddress : dhcpResults.linkProperties.getLinkAddresses()) {
3049                loge("link address " + linkAddress.toString());
3050            }
3051        }
3052
3053        mLastSignalLevel = -1; // force update of signal strength
3054        mReconnectCount = 0; //Reset IP failure tracking
3055        synchronized (mDhcpResultsLock) {
3056            mDhcpResults = dhcpResults;
3057        }
3058        LinkProperties linkProperties = dhcpResults.linkProperties;
3059        mWifiConfigStore.setLinkProperties(mLastNetworkId, new LinkProperties(linkProperties));
3060        InetAddress addr = null;
3061        Iterator<InetAddress> addrs = linkProperties.getAddresses().iterator();
3062        if (addrs.hasNext()) {
3063            addr = addrs.next();
3064        }
3065
3066        if (isRoaming()) {
3067            if (addr instanceof Inet4Address) {
3068                int previousAddress = mWifiInfo.getIpAddress();
3069                int newAddress = NetworkUtils.inetAddressToInt((Inet4Address)addr);
3070                if (previousAddress != newAddress) {
3071                    loge("handleSuccessfulIpConfiguration, roaming and address changed" +
3072                            mWifiInfo + " got: " + addr);
3073                } else {
3074
3075                }
3076            } else {
3077                loge("handleSuccessfulIpConfiguration, roaming and didnt get an IPv4 address" +
3078                        addr.toString());
3079
3080
3081            }
3082        }
3083        mWifiInfo.setInetAddress(addr);
3084        mWifiInfo.setMeteredHint(dhcpResults.hasMeteredHint());
3085        updateLinkProperties();
3086    }
3087
3088    private void handleFailedIpConfiguration() {
3089
3090        mWifiInfo.setInetAddress(null);
3091        mWifiInfo.setMeteredHint(false);
3092        /**
3093         * If we've exceeded the maximum number of retries for DHCP
3094         * to a given network, disable the network
3095         */
3096        int maxRetries = getMaxDhcpRetries();
3097        // maxRetries == 0 means keep trying forever
3098        if (maxRetries > 0 && ++mReconnectCount > maxRetries) {
3099            loge("Failed " +
3100                    mReconnectCount + " times, Disabling " + mLastNetworkId);
3101            mWifiConfigStore.disableNetwork(mLastNetworkId,
3102                    WifiConfiguration.DISABLED_DHCP_FAILURE);
3103            mReconnectCount = 0;
3104        }
3105
3106        /* DHCP times out after about 30 seconds, we do a
3107         * disconnect and an immediate reconnect to try again
3108         */
3109        mWifiNative.disconnect();
3110        mWifiNative.reconnect();
3111    }
3112
3113    /* Current design is to not set the config on a running hostapd but instead
3114     * stop and start tethering when user changes config on a running access point
3115     *
3116     * TODO: Add control channel setup through hostapd that allows changing config
3117     * on a running daemon
3118     */
3119    private void startSoftApWithConfig(final WifiConfiguration config) {
3120        // start hostapd on a seperate thread
3121        new Thread(new Runnable() {
3122            public void run() {
3123                try {
3124                    mNwService.startAccessPoint(config, mInterfaceName);
3125                } catch (Exception e) {
3126                    loge("Exception in softap start " + e);
3127                    try {
3128                        mNwService.stopAccessPoint(mInterfaceName);
3129                        mNwService.startAccessPoint(config, mInterfaceName);
3130                    } catch (Exception e1) {
3131                        loge("Exception in softap re-start " + e1);
3132                        sendMessage(CMD_START_AP_FAILURE);
3133                        return;
3134                    }
3135                }
3136                if (DBG) log("Soft AP start successful");
3137                sendMessage(CMD_START_AP_SUCCESS);
3138            }
3139        }).start();
3140    }
3141
3142
3143    /*
3144     * Read a MAC address in /proc/arp/table, used by WifistateMachine
3145     * so as to record MAC address of default gateway.
3146     **/
3147    private String macAddressFromRoute(String ipAddress) {
3148        String macAddress = null;
3149        BufferedReader reader = null;
3150        try {
3151            reader = new BufferedReader(new FileReader("/proc/net/arp"));
3152
3153            // Skip over the line bearing colum titles
3154            String line = reader.readLine();
3155
3156            while ((line = reader.readLine()) != null) {
3157                String[] tokens = line.split("[ ]+");
3158                if (tokens.length < 6) {
3159                    continue;
3160                }
3161
3162                // ARP column format is
3163                // Address HWType HWAddress Flags Mask IFace
3164                String ip = tokens[0];
3165                String mac = tokens[3];
3166
3167                if (ipAddress.equals(ip)) {
3168                    macAddress = mac;
3169                    break;
3170                }
3171            }
3172
3173            if (macAddress == null) {
3174                loge("Did not find remoteAddress {" + ipAddress + "} in " +
3175                        "/proc/net/arp");
3176            }
3177
3178        } catch (FileNotFoundException e) {
3179            loge("Could not open /proc/net/arp to lookup mac address");
3180        } catch (IOException e) {
3181            loge("Could not read /proc/net/arp to lookup mac address");
3182        } finally {
3183            try {
3184                if (reader != null) {
3185                    reader.close();
3186                }
3187            } catch (IOException e) {
3188                // Do nothing
3189            }
3190        }
3191        return macAddress;
3192
3193    }
3194
3195    private class WifiNetworkFactory extends NetworkFactory {
3196        public WifiNetworkFactory(Looper l, Context c, String TAG, NetworkCapabilities f) {
3197            super(l, c, TAG, f);
3198        }
3199        protected void startNetwork() {
3200            // TODO
3201            // enter association mode.
3202        }
3203        protected void stopNetwork() {
3204            // TODO
3205            // stop associating.
3206        }
3207    }
3208    /********************************************************
3209     * HSM states
3210     *******************************************************/
3211
3212    class DefaultState extends State {
3213        @Override
3214        public boolean processMessage(Message message) {
3215            logStateAndMessage(message, getClass().getSimpleName());
3216
3217            switch (message.what) {
3218                case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: {
3219                    AsyncChannel ac = (AsyncChannel) message.obj;
3220                    if (ac == mWifiP2pChannel) {
3221                        if (message.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
3222                            mWifiP2pChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
3223                        } else {
3224                            loge("WifiP2pService connection failure, error=" + message.arg1);
3225                        }
3226                    } else {
3227                        loge("got HALF_CONNECTED for unknown channel");
3228                    }
3229                    break;
3230                }
3231                case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
3232                    AsyncChannel ac = (AsyncChannel) message.obj;
3233                    if (ac == mWifiP2pChannel) {
3234                        loge("WifiP2pService channel lost, message.arg1 =" + message.arg1);
3235                        //TODO: Re-establish connection to state machine after a delay
3236                        //mWifiP2pChannel.connect(mContext, getHandler(),
3237                        // mWifiP2pManager.getMessenger());
3238                    }
3239                    break;
3240                }
3241                case CMD_BLUETOOTH_ADAPTER_STATE_CHANGE:
3242                    mBluetoothConnectionActive = (message.arg1 !=
3243                            BluetoothAdapter.STATE_DISCONNECTED);
3244                    break;
3245                    /* Synchronous call returns */
3246                case CMD_PING_SUPPLICANT:
3247                case CMD_ENABLE_NETWORK:
3248                case CMD_ADD_OR_UPDATE_NETWORK:
3249                case CMD_REMOVE_NETWORK:
3250                case CMD_SAVE_CONFIG:
3251                    replyToMessage(message, message.what, FAILURE);
3252                    break;
3253                case CMD_GET_CAPABILITY_FREQ:
3254                    replyToMessage(message, message.what, null);
3255                    break;
3256                case CMD_GET_CONFIGURED_NETWORKS:
3257                    replyToMessage(message, message.what, (List<WifiConfiguration>) null);
3258                    break;
3259                case CMD_ENABLE_RSSI_POLL:
3260                    mEnableRssiPolling = (message.arg1 == 1);
3261                    break;
3262                case CMD_ENABLE_BACKGROUND_SCAN:
3263                    mEnableBackgroundScan = (message.arg1 == 1);
3264                    break;
3265                case CMD_SET_HIGH_PERF_MODE:
3266                    if (message.arg1 == 1) {
3267                        setSuspendOptimizations(SUSPEND_DUE_TO_HIGH_PERF, false);
3268                    } else {
3269                        setSuspendOptimizations(SUSPEND_DUE_TO_HIGH_PERF, true);
3270                    }
3271                    break;
3272                case CMD_BOOT_COMPLETED:
3273                    String countryCode = mPersistedCountryCode;
3274                    if (TextUtils.isEmpty(countryCode) == false) {
3275                        Settings.Global.putString(mContext.getContentResolver(),
3276                                Settings.Global.WIFI_COUNTRY_CODE,
3277                                countryCode);
3278                        // it may be that the state transition that should send this info
3279                        // to the driver happened between mPersistedCountryCode getting set
3280                        // and now, so simply persisting it here would mean we have sent
3281                        // nothing to the driver.  Send the cmd so it might be set now.
3282                        int sequenceNum = mCountryCodeSequence.incrementAndGet();
3283                        sendMessageAtFrontOfQueue(CMD_SET_COUNTRY_CODE,
3284                                sequenceNum, 0, countryCode);
3285                    }
3286
3287                    checkAndSetConnectivityInstance();
3288                    mNetworkFactory = new WifiNetworkFactory(getHandler().getLooper(), mContext,
3289                            NETWORKTYPE, mNetworkCapabilitiesFilter);
3290                    mNetworkFactory.setScoreFilter(60);
3291                    mCm.registerNetworkFactory(new Messenger(mNetworkFactory), NETWORKTYPE);
3292                    break;
3293                case CMD_SET_BATCHED_SCAN:
3294                    recordBatchedScanSettings(message.arg1, message.arg2, (Bundle)message.obj);
3295                    break;
3296                case CMD_POLL_BATCHED_SCAN:
3297                    handleBatchedScanPollRequest();
3298                    break;
3299                case CMD_START_NEXT_BATCHED_SCAN:
3300                    startNextBatchedScan();
3301                    break;
3302                    /* Discard */
3303                case CMD_START_SCAN:
3304                case CMD_START_SUPPLICANT:
3305                case CMD_STOP_SUPPLICANT:
3306                case CMD_STOP_SUPPLICANT_FAILED:
3307                case CMD_START_DRIVER:
3308                case CMD_STOP_DRIVER:
3309                case CMD_DELAYED_STOP_DRIVER:
3310                case CMD_DRIVER_START_TIMED_OUT:
3311                case CMD_START_AP:
3312                case CMD_START_AP_SUCCESS:
3313                case CMD_START_AP_FAILURE:
3314                case CMD_STOP_AP:
3315                case CMD_TETHER_STATE_CHANGE:
3316                case CMD_TETHER_NOTIFICATION_TIMED_OUT:
3317                case CMD_DISCONNECT:
3318                case CMD_RECONNECT:
3319                case CMD_REASSOCIATE:
3320                case CMD_RELOAD_TLS_AND_RECONNECT:
3321                case WifiMonitor.SUP_CONNECTION_EVENT:
3322                case WifiMonitor.SUP_DISCONNECTION_EVENT:
3323                case WifiMonitor.NETWORK_CONNECTION_EVENT:
3324                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
3325                case WifiMonitor.SCAN_RESULTS_EVENT:
3326                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
3327                case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
3328                case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
3329                case WifiMonitor.WPS_OVERLAP_EVENT:
3330                case CMD_BLACKLIST_NETWORK:
3331                case CMD_CLEAR_BLACKLIST:
3332                case CMD_SET_OPERATIONAL_MODE:
3333                case CMD_SET_COUNTRY_CODE:
3334                case CMD_SET_FREQUENCY_BAND:
3335                case CMD_RSSI_POLL:
3336                case CMD_ENABLE_ALL_NETWORKS:
3337                case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
3338                case DhcpStateMachine.CMD_POST_DHCP_ACTION:
3339                /* Handled by WifiApConfigStore */
3340                case CMD_SET_AP_CONFIG:
3341                case CMD_SET_AP_CONFIG_COMPLETED:
3342                case CMD_REQUEST_AP_CONFIG:
3343                case CMD_RESPONSE_AP_CONFIG:
3344                case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
3345                case WifiWatchdogStateMachine.GOOD_LINK_DETECTED:
3346                case CMD_NO_NETWORKS_PERIODIC_SCAN:
3347                case CMD_DISABLE_P2P_RSP:
3348                    break;
3349                case DhcpStateMachine.CMD_ON_QUIT:
3350                    mDhcpStateMachine = null;
3351                    break;
3352                case CMD_SET_SUSPEND_OPT_ENABLED:
3353                    if (message.arg1 == 1) {
3354                        mSuspendWakeLock.release();
3355                        setSuspendOptimizations(SUSPEND_DUE_TO_SCREEN, true);
3356                    } else {
3357                        setSuspendOptimizations(SUSPEND_DUE_TO_SCREEN, false);
3358                    }
3359                    break;
3360                case WifiMonitor.DRIVER_HUNG_EVENT:
3361                    setSupplicantRunning(false);
3362                    setSupplicantRunning(true);
3363                    break;
3364                case WifiManager.CONNECT_NETWORK:
3365                    replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
3366                            WifiManager.BUSY);
3367                    break;
3368                case WifiManager.FORGET_NETWORK:
3369                    replyToMessage(message, WifiManager.FORGET_NETWORK_FAILED,
3370                            WifiManager.BUSY);
3371                    break;
3372                case WifiManager.SAVE_NETWORK:
3373                    replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED,
3374                            WifiManager.BUSY);
3375                    break;
3376                case WifiManager.START_WPS:
3377                    replyToMessage(message, WifiManager.WPS_FAILED,
3378                            WifiManager.BUSY);
3379                    break;
3380                case WifiManager.CANCEL_WPS:
3381                    replyToMessage(message, WifiManager.CANCEL_WPS_FAILED,
3382                            WifiManager.BUSY);
3383                    break;
3384                case WifiManager.DISABLE_NETWORK:
3385                    replyToMessage(message, WifiManager.DISABLE_NETWORK_FAILED,
3386                            WifiManager.BUSY);
3387                    break;
3388                case WifiManager.RSSI_PKTCNT_FETCH:
3389                    replyToMessage(message, WifiManager.RSSI_PKTCNT_FETCH_FAILED,
3390                            WifiManager.BUSY);
3391                    break;
3392                case WifiP2pServiceImpl.P2P_CONNECTION_CHANGED:
3393                    NetworkInfo info = (NetworkInfo) message.obj;
3394                    mP2pConnected.set(info.isConnected());
3395                    break;
3396                case WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST:
3397                    mTemporarilyDisconnectWifi = (message.arg1 == 1);
3398                    replyToMessage(message, WifiP2pServiceImpl.DISCONNECT_WIFI_RESPONSE);
3399                    break;
3400                /* Link configuration (IP address, DNS, ...) changes notified via netlink */
3401                case CMD_NETLINK_UPDATE:
3402                  updateLinkProperties();
3403                  break;
3404                default:
3405                    loge("Error! unhandled message" + message);
3406                    break;
3407            }
3408            return HANDLED;
3409        }
3410    }
3411
3412    class InitialState extends State {
3413        @Override
3414        public void enter() {
3415            mWifiNative.unloadDriver();
3416
3417            if (mWifiP2pChannel == null) {
3418                mWifiP2pChannel = new AsyncChannel();
3419                mWifiP2pChannel.connect(mContext, getHandler(),
3420                    mWifiP2pServiceImpl.getP2pStateMachineMessenger());
3421            }
3422
3423            if (mWifiApConfigChannel == null) {
3424                mWifiApConfigChannel = new AsyncChannel();
3425                WifiApConfigStore wifiApConfigStore = WifiApConfigStore.makeWifiApConfigStore(
3426                        mContext, getHandler());
3427                wifiApConfigStore.loadApConfiguration();
3428                mWifiApConfigChannel.connectSync(mContext, getHandler(),
3429                        wifiApConfigStore.getMessenger());
3430            }
3431        }
3432        @Override
3433        public boolean processMessage(Message message) {
3434            logStateAndMessage(message, getClass().getSimpleName());
3435            switch (message.what) {
3436                case CMD_START_SUPPLICANT:
3437                    if (mWifiNative.loadDriver()) {
3438                        try {
3439                            mNwService.wifiFirmwareReload(mInterfaceName, "STA");
3440                        } catch (Exception e) {
3441                            loge("Failed to reload STA firmware " + e);
3442                            // continue
3443                        }
3444
3445                        try {
3446                            // A runtime crash can leave the interface up and
3447                            // this affects connectivity when supplicant starts up.
3448                            // Ensure interface is down before a supplicant start.
3449                            mNwService.setInterfaceDown(mInterfaceName);
3450                            // Set privacy extensions
3451                            mNwService.setInterfaceIpv6PrivacyExtensions(mInterfaceName, true);
3452
3453                           // IPv6 is enabled only as long as access point is connected since:
3454                           // - IPv6 addresses and routes stick around after disconnection
3455                           // - kernel is unaware when connected and fails to start IPv6 negotiation
3456                           // - kernel can start autoconfiguration when 802.1x is not complete
3457                            mNwService.disableIpv6(mInterfaceName);
3458                        } catch (RemoteException re) {
3459                            loge("Unable to change interface settings: " + re);
3460                        } catch (IllegalStateException ie) {
3461                            loge("Unable to change interface settings: " + ie);
3462                        }
3463
3464                       /* Stop a running supplicant after a runtime restart
3465                        * Avoids issues with drivers that do not handle interface down
3466                        * on a running supplicant properly.
3467                        */
3468                        mWifiMonitor.killSupplicant(mP2pSupported);
3469                        if(mWifiNative.startSupplicant(mP2pSupported)) {
3470                            setWifiState(WIFI_STATE_ENABLING);
3471                            if (DBG) log("Supplicant start successful");
3472                            mWifiMonitor.startMonitoring();
3473                            transitionTo(mSupplicantStartingState);
3474                        } else {
3475                            loge("Failed to start supplicant!");
3476                        }
3477                    } else {
3478                        loge("Failed to load driver");
3479                    }
3480                    break;
3481                case CMD_START_AP:
3482                    if (mWifiNative.loadDriver()) {
3483                        setWifiApState(WIFI_AP_STATE_ENABLING);
3484                        transitionTo(mSoftApStartingState);
3485                    } else {
3486                        loge("Failed to load driver for softap");
3487                    }
3488                default:
3489                    return NOT_HANDLED;
3490            }
3491            return HANDLED;
3492        }
3493    }
3494
3495    class SupplicantStartingState extends State {
3496        private void initializeWpsDetails() {
3497            String detail;
3498            detail = SystemProperties.get("ro.product.name", "");
3499            if (!mWifiNative.setDeviceName(detail)) {
3500                loge("Failed to set device name " +  detail);
3501            }
3502            detail = SystemProperties.get("ro.product.manufacturer", "");
3503            if (!mWifiNative.setManufacturer(detail)) {
3504                loge("Failed to set manufacturer " + detail);
3505            }
3506            detail = SystemProperties.get("ro.product.model", "");
3507            if (!mWifiNative.setModelName(detail)) {
3508                loge("Failed to set model name " + detail);
3509            }
3510            detail = SystemProperties.get("ro.product.model", "");
3511            if (!mWifiNative.setModelNumber(detail)) {
3512                loge("Failed to set model number " + detail);
3513            }
3514            detail = SystemProperties.get("ro.serialno", "");
3515            if (!mWifiNative.setSerialNumber(detail)) {
3516                loge("Failed to set serial number " + detail);
3517            }
3518            if (!mWifiNative.setConfigMethods("physical_display virtual_push_button")) {
3519                loge("Failed to set WPS config methods");
3520            }
3521            if (!mWifiNative.setDeviceType(mPrimaryDeviceType)) {
3522                loge("Failed to set primary device type " + mPrimaryDeviceType);
3523            }
3524        }
3525
3526        @Override
3527        public boolean processMessage(Message message) {
3528            logStateAndMessage(message, getClass().getSimpleName());
3529
3530            switch(message.what) {
3531                case WifiMonitor.SUP_CONNECTION_EVENT:
3532                    if (DBG) log("Supplicant connection established");
3533                    setWifiState(WIFI_STATE_ENABLED);
3534                    mSupplicantRestartCount = 0;
3535                    /* Reset the supplicant state to indicate the supplicant
3536                     * state is not known at this time */
3537                    mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
3538                    /* Initialize data structures */
3539                    mLastBssid = null;
3540                    mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
3541                    mLastSignalLevel = -1;
3542
3543                    mWifiInfo.setMacAddress(mWifiNative.getMacAddress());
3544                    mWifiNative.enableSaveConfig();
3545                    mWifiConfigStore.loadAndEnableAllNetworks();
3546                    initializeWpsDetails();
3547
3548                    sendSupplicantConnectionChangedBroadcast(true);
3549                    transitionTo(mDriverStartedState);
3550                    break;
3551                case WifiMonitor.SUP_DISCONNECTION_EVENT:
3552                    if (++mSupplicantRestartCount <= SUPPLICANT_RESTART_TRIES) {
3553                        loge("Failed to setup control channel, restart supplicant");
3554                        mWifiMonitor.killSupplicant(mP2pSupported);
3555                        transitionTo(mInitialState);
3556                        sendMessageDelayed(CMD_START_SUPPLICANT, SUPPLICANT_RESTART_INTERVAL_MSECS);
3557                    } else {
3558                        loge("Failed " + mSupplicantRestartCount +
3559                                " times to start supplicant, unload driver");
3560                        mSupplicantRestartCount = 0;
3561                        setWifiState(WIFI_STATE_UNKNOWN);
3562                        transitionTo(mInitialState);
3563                    }
3564                    break;
3565                case CMD_START_SUPPLICANT:
3566                case CMD_STOP_SUPPLICANT:
3567                case CMD_START_AP:
3568                case CMD_STOP_AP:
3569                case CMD_START_DRIVER:
3570                case CMD_STOP_DRIVER:
3571                case CMD_SET_OPERATIONAL_MODE:
3572                case CMD_SET_COUNTRY_CODE:
3573                case CMD_SET_FREQUENCY_BAND:
3574                case CMD_START_PACKET_FILTERING:
3575                case CMD_STOP_PACKET_FILTERING:
3576                    deferMessage(message);
3577                    break;
3578                default:
3579                    return NOT_HANDLED;
3580            }
3581            return HANDLED;
3582        }
3583    }
3584
3585    class SupplicantStartedState extends State {
3586        @Override
3587        public void enter() {
3588            /* Wifi is available as long as we have a connection to supplicant */
3589            mNetworkInfo.setIsAvailable(true);
3590            if (mNetworkAgent != null) mNetworkAgent.sendNetworkInfo(mNetworkInfo);
3591
3592            int defaultInterval = mContext.getResources().getInteger(
3593                    R.integer.config_wifi_supplicant_scan_interval);
3594
3595            mSupplicantScanIntervalMs = Settings.Global.getLong(mContext.getContentResolver(),
3596                    Settings.Global.WIFI_SUPPLICANT_SCAN_INTERVAL_MS,
3597                    defaultInterval);
3598
3599            mWifiNative.setScanInterval((int)mSupplicantScanIntervalMs / 1000);
3600
3601            if (mFrameworkAutoJoin.get()) {
3602                mWifiNative.enableAutoConnect(false);
3603            }
3604
3605        }
3606        @Override
3607        public boolean processMessage(Message message) {
3608            logStateAndMessage(message, getClass().getSimpleName());
3609
3610            switch(message.what) {
3611                case CMD_STOP_SUPPLICANT:   /* Supplicant stopped by user */
3612                    if (mP2pSupported) {
3613                        transitionTo(mWaitForP2pDisableState);
3614                    } else {
3615                        transitionTo(mSupplicantStoppingState);
3616                    }
3617                    break;
3618                case WifiMonitor.SUP_DISCONNECTION_EVENT:  /* Supplicant connection lost */
3619                    loge("Connection lost, restart supplicant");
3620                    handleSupplicantConnectionLoss();
3621                    handleNetworkDisconnect();
3622                    mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
3623                    if (mP2pSupported) {
3624                        transitionTo(mWaitForP2pDisableState);
3625                    } else {
3626                        transitionTo(mInitialState);
3627                    }
3628                    sendMessageDelayed(CMD_START_SUPPLICANT, SUPPLICANT_RESTART_INTERVAL_MSECS);
3629                    break;
3630                case WifiMonitor.SCAN_RESULTS_EVENT:
3631                    setScanResults();
3632                    sendScanResultsAvailableBroadcast();
3633                    mIsScanOngoing = false;
3634                    mIsFullScanOngoing = false;
3635                    if (mBufferedScanMsg.size() > 0)
3636                        sendMessage(mBufferedScanMsg.remove());
3637                    break;
3638                case CMD_PING_SUPPLICANT:
3639                    boolean ok = mWifiNative.ping();
3640                    replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
3641                    break;
3642                case CMD_GET_CAPABILITY_FREQ:
3643                    String freqs = mWifiNative.getFreqCapability();
3644                    replyToMessage(message, message.what, freqs);
3645                    break;
3646                case CMD_START_AP:
3647                    /* Cannot start soft AP while in client mode */
3648                    loge("Failed to start soft AP with a running supplicant");
3649                    setWifiApState(WIFI_AP_STATE_FAILED);
3650                    break;
3651                case CMD_SET_OPERATIONAL_MODE:
3652                    mOperationalMode = message.arg1;
3653                    break;
3654                default:
3655                    return NOT_HANDLED;
3656            }
3657            return HANDLED;
3658        }
3659
3660        @Override
3661        public void exit() {
3662            mNetworkInfo.setIsAvailable(false);
3663            if (mNetworkAgent != null) mNetworkAgent.sendNetworkInfo(mNetworkInfo);
3664        }
3665    }
3666
3667    class SupplicantStoppingState extends State {
3668        @Override
3669        public void enter() {
3670            /* Send any reset commands to supplicant before shutting it down */
3671            handleNetworkDisconnect();
3672            if (mDhcpStateMachine != null) {
3673                mDhcpStateMachine.doQuit();
3674            }
3675
3676            if (DBG) log("stopping supplicant");
3677            mWifiMonitor.stopSupplicant();
3678
3679            /* Send ourselves a delayed message to indicate failure after a wait time */
3680            sendMessageDelayed(obtainMessage(CMD_STOP_SUPPLICANT_FAILED,
3681                    ++mSupplicantStopFailureToken, 0), SUPPLICANT_RESTART_INTERVAL_MSECS);
3682            setWifiState(WIFI_STATE_DISABLING);
3683            mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
3684        }
3685        @Override
3686        public boolean processMessage(Message message) {
3687            logStateAndMessage(message, getClass().getSimpleName());
3688
3689            switch(message.what) {
3690                case WifiMonitor.SUP_CONNECTION_EVENT:
3691                    loge("Supplicant connection received while stopping");
3692                    break;
3693                case WifiMonitor.SUP_DISCONNECTION_EVENT:
3694                    if (DBG) log("Supplicant connection lost");
3695                    handleSupplicantConnectionLoss();
3696                    transitionTo(mInitialState);
3697                    break;
3698                case CMD_STOP_SUPPLICANT_FAILED:
3699                    if (message.arg1 == mSupplicantStopFailureToken) {
3700                        loge("Timed out on a supplicant stop, kill and proceed");
3701                        handleSupplicantConnectionLoss();
3702                        transitionTo(mInitialState);
3703                    }
3704                    break;
3705                case CMD_START_SUPPLICANT:
3706                case CMD_STOP_SUPPLICANT:
3707                case CMD_START_AP:
3708                case CMD_STOP_AP:
3709                case CMD_START_DRIVER:
3710                case CMD_STOP_DRIVER:
3711                case CMD_SET_OPERATIONAL_MODE:
3712                case CMD_SET_COUNTRY_CODE:
3713                case CMD_SET_FREQUENCY_BAND:
3714                case CMD_START_PACKET_FILTERING:
3715                case CMD_STOP_PACKET_FILTERING:
3716                    deferMessage(message);
3717                    break;
3718                default:
3719                    return NOT_HANDLED;
3720            }
3721            return HANDLED;
3722        }
3723    }
3724
3725    class DriverStartingState extends State {
3726        private int mTries;
3727        @Override
3728        public void enter() {
3729            mTries = 1;
3730            /* Send ourselves a delayed message to start driver a second time */
3731            sendMessageDelayed(obtainMessage(CMD_DRIVER_START_TIMED_OUT,
3732                        ++mDriverStartToken, 0), DRIVER_START_TIME_OUT_MSECS);
3733        }
3734        @Override
3735        public boolean processMessage(Message message) {
3736            logStateAndMessage(message, getClass().getSimpleName());
3737
3738            switch(message.what) {
3739               case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
3740                    SupplicantState state = handleSupplicantStateChange(message);
3741                    /* If suplicant is exiting out of INTERFACE_DISABLED state into
3742                     * a state that indicates driver has started, it is ready to
3743                     * receive driver commands
3744                     */
3745                    if (SupplicantState.isDriverActive(state)) {
3746                        transitionTo(mDriverStartedState);
3747                    }
3748                    break;
3749                case CMD_DRIVER_START_TIMED_OUT:
3750                    if (message.arg1 == mDriverStartToken) {
3751                        if (mTries >= 2) {
3752                            loge("Failed to start driver after " + mTries);
3753                            transitionTo(mDriverStoppedState);
3754                        } else {
3755                            loge("Driver start failed, retrying");
3756                            mWakeLock.acquire();
3757                            mWifiNative.startDriver();
3758                            mWakeLock.release();
3759
3760                            ++mTries;
3761                            /* Send ourselves a delayed message to start driver again */
3762                            sendMessageDelayed(obtainMessage(CMD_DRIVER_START_TIMED_OUT,
3763                                        ++mDriverStartToken, 0), DRIVER_START_TIME_OUT_MSECS);
3764                        }
3765                    }
3766                    break;
3767                    /* Queue driver commands & connection events */
3768                case CMD_START_DRIVER:
3769                case CMD_STOP_DRIVER:
3770                case WifiMonitor.NETWORK_CONNECTION_EVENT:
3771                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
3772                case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
3773                case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
3774                case WifiMonitor.WPS_OVERLAP_EVENT:
3775                case CMD_SET_COUNTRY_CODE:
3776                case CMD_SET_FREQUENCY_BAND:
3777                case CMD_START_PACKET_FILTERING:
3778                case CMD_STOP_PACKET_FILTERING:
3779                case CMD_START_SCAN:
3780                case CMD_DISCONNECT:
3781                case CMD_REASSOCIATE:
3782                case CMD_RECONNECT:
3783                    deferMessage(message);
3784                    break;
3785                default:
3786                    return NOT_HANDLED;
3787            }
3788            return HANDLED;
3789        }
3790    }
3791
3792    class DriverStartedState extends State {
3793        @Override
3794        public void enter() {
3795
3796            if (PDBG) {
3797                loge("Driverstarted State enter");
3798            }
3799            mIsRunning = true;
3800            mInDelayedStop = false;
3801            mDelayedStopCounter++;
3802            updateBatteryWorkSource(null);
3803            /**
3804             * Enable bluetooth coexistence scan mode when bluetooth connection is active.
3805             * When this mode is on, some of the low-level scan parameters used by the
3806             * driver are changed to reduce interference with bluetooth
3807             */
3808            mWifiNative.setBluetoothCoexistenceScanMode(mBluetoothConnectionActive);
3809            /* set country code */
3810            setCountryCode();
3811            /* set frequency band of operation */
3812            setFrequencyBand();
3813            /* initialize network state */
3814            setNetworkDetailedState(DetailedState.DISCONNECTED);
3815
3816            /* Remove any filtering on Multicast v6 at start */
3817            mWifiNative.stopFilteringMulticastV6Packets();
3818
3819            /* Reset Multicast v4 filtering state */
3820            if (mFilteringMulticastV4Packets.get()) {
3821                mWifiNative.startFilteringMulticastV4Packets();
3822            } else {
3823                mWifiNative.stopFilteringMulticastV4Packets();
3824            }
3825
3826            mDhcpActive = false;
3827
3828            startBatchedScan();
3829
3830            if (mOperationalMode != CONNECT_MODE) {
3831                mWifiNative.disconnect();
3832                mWifiConfigStore.disableAllNetworks();
3833                if (mOperationalMode == SCAN_ONLY_WITH_WIFI_OFF_MODE) {
3834                    setWifiState(WIFI_STATE_DISABLED);
3835                }
3836                transitionTo(mScanModeState);
3837            } else {
3838                /* Driver stop may have disabled networks, enable right after start */
3839                mWifiConfigStore.enableAllNetworks();
3840
3841                if (DBG) loge("Attempting to reconnect to wifi network ..");
3842                mWifiNative.reconnect();
3843
3844                // Status pulls in the current supplicant state and network connection state
3845                // events over the monitor connection. This helps framework sync up with
3846                // current supplicant state
3847                mWifiNative.status();
3848                transitionTo(mDisconnectedState);
3849            }
3850
3851            // We may have missed screen update at boot
3852            if (mScreenBroadcastReceived.get() == false) {
3853                PowerManager powerManager = (PowerManager)mContext.getSystemService(
3854                        Context.POWER_SERVICE);
3855                handleScreenStateChanged(powerManager.isScreenOn());
3856            } else {
3857                // Set the right suspend mode settings
3858                mWifiNative.setSuspendOptimizations(mSuspendOptNeedsDisabled == 0
3859                        && mUserWantsSuspendOpt.get());
3860            }
3861            mWifiNative.setPowerSave(true);
3862
3863            if (mP2pSupported) {
3864                if (mOperationalMode == CONNECT_MODE) {
3865                    mWifiP2pChannel.sendMessage(WifiStateMachine.CMD_ENABLE_P2P);
3866                } else {
3867                    // P2P statemachine starts in disabled state, and is not enabled until
3868                    // CMD_ENABLE_P2P is sent from here; so, nothing needs to be done to
3869                    // keep it disabled.
3870                }
3871            }
3872
3873            final Intent intent = new Intent(WifiManager.WIFI_SCAN_AVAILABLE);
3874            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
3875            intent.putExtra(WifiManager.EXTRA_SCAN_AVAILABLE, WIFI_STATE_ENABLED);
3876            mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
3877
3878            if (PDBG) {
3879                loge("Driverstarted State enter done");
3880            }
3881        }
3882
3883        @Override
3884        public boolean processMessage(Message message) {
3885            logStateAndMessage(message, getClass().getSimpleName());
3886
3887            switch(message.what) {
3888                case CMD_START_SCAN:
3889                    if (mFrameworkAutoJoin.get()) {
3890                        handleScanRequest(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, message);
3891                    } else {
3892                        handleScanRequest(WifiNative.SCAN_WITH_CONNECTION_SETUP, message);
3893                    }
3894                    break;
3895                case CMD_SET_BATCHED_SCAN:
3896                    if (recordBatchedScanSettings(message.arg1, message.arg2,
3897                            (Bundle)message.obj)) {
3898                        if (mBatchedScanSettings != null) {
3899                            startBatchedScan();
3900                        } else {
3901                            stopBatchedScan();
3902                        }
3903                    }
3904                    break;
3905                case CMD_SET_COUNTRY_CODE:
3906                    String country = (String) message.obj;
3907                    final boolean persist = (message.arg2 == 1);
3908                    final int sequence = message.arg1;
3909                    if (sequence != mCountryCodeSequence.get()) {
3910                        if (DBG) log("set country code ignored due to sequnce num");
3911                        break;
3912                    }
3913                    if (DBG) log("set country code " + country);
3914                    if (persist) {
3915                        mPersistedCountryCode = country;
3916                        Settings.Global.putString(mContext.getContentResolver(),
3917                                Settings.Global.WIFI_COUNTRY_CODE,
3918                                country);
3919                    }
3920                    country = country.toUpperCase(Locale.ROOT);
3921                    if (mLastSetCountryCode == null
3922                            || country.equals(mLastSetCountryCode) == false) {
3923                        if (mWifiNative.setCountryCode(country)) {
3924                            mLastSetCountryCode = country;
3925                        } else {
3926                            loge("Failed to set country code " + country);
3927                        }
3928                    }
3929                    mWifiP2pChannel.sendMessage(WifiP2pServiceImpl.SET_COUNTRY_CODE, country);
3930                    break;
3931                case CMD_SET_FREQUENCY_BAND:
3932                    int band =  message.arg1;
3933                    if (DBG) log("set frequency band " + band);
3934                    if (mWifiNative.setBand(band)) {
3935
3936                        if (PDBG)  loge("did set frequency band " + band);
3937
3938                        mFrequencyBand.set(band);
3939                        // flush old data - like scan results
3940                        mWifiNative.bssFlush();
3941                        // fetch the latest scan results when frequency band is set
3942                        if (mFrameworkAutoJoin.get())
3943                            startScanNative(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, null);
3944                        else
3945                            startScanNative(WifiNative.SCAN_WITH_CONNECTION_SETUP, null);
3946                        if (PDBG)  loge("done set frequency band " + band);
3947
3948                    } else {
3949                        loge("Failed to set frequency band " + band);
3950                    }
3951                    break;
3952                case CMD_BLUETOOTH_ADAPTER_STATE_CHANGE:
3953                    mBluetoothConnectionActive = (message.arg1 !=
3954                            BluetoothAdapter.STATE_DISCONNECTED);
3955                    mWifiNative.setBluetoothCoexistenceScanMode(mBluetoothConnectionActive);
3956                    break;
3957                case CMD_STOP_DRIVER:
3958                    int mode = message.arg1;
3959
3960                    /* Already doing a delayed stop */
3961                    if (mInDelayedStop) {
3962                        if (DBG) log("Already in delayed stop");
3963                        break;
3964                    }
3965                    /* disconnect right now, but leave the driver running for a bit */
3966                    mWifiConfigStore.disableAllNetworks();
3967
3968                    mInDelayedStop = true;
3969                    mDelayedStopCounter++;
3970                    if (DBG) log("Delayed stop message " + mDelayedStopCounter);
3971
3972                    /* send regular delayed shut down */
3973                    Intent driverStopIntent = new Intent(ACTION_DELAYED_DRIVER_STOP, null);
3974                    driverStopIntent.putExtra(DELAYED_STOP_COUNTER, mDelayedStopCounter);
3975                    mDriverStopIntent = PendingIntent.getBroadcast(mContext,
3976                            DRIVER_STOP_REQUEST, driverStopIntent,
3977                            PendingIntent.FLAG_UPDATE_CURRENT);
3978
3979                    mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()
3980                            + mDriverStopDelayMs, mDriverStopIntent);
3981                    break;
3982                case CMD_START_DRIVER:
3983                    if (mInDelayedStop) {
3984                        mInDelayedStop = false;
3985                        mDelayedStopCounter++;
3986                        mAlarmManager.cancel(mDriverStopIntent);
3987                        if (DBG) log("Delayed stop ignored due to start");
3988                        if (mOperationalMode == CONNECT_MODE) {
3989                            mWifiConfigStore.enableAllNetworks();
3990                        }
3991                    }
3992                    break;
3993                case CMD_DELAYED_STOP_DRIVER:
3994                    if (DBG) log("delayed stop " + message.arg1 + " " + mDelayedStopCounter);
3995                    if (message.arg1 != mDelayedStopCounter) break;
3996                    if (getCurrentState() != mDisconnectedState) {
3997                        mWifiNative.disconnect();
3998                        handleNetworkDisconnect();
3999                    }
4000                    mWakeLock.acquire();
4001                    mWifiNative.stopDriver();
4002                    mWakeLock.release();
4003                    if (mP2pSupported) {
4004                        transitionTo(mWaitForP2pDisableState);
4005                    } else {
4006                        transitionTo(mDriverStoppingState);
4007                    }
4008                    break;
4009                case CMD_START_PACKET_FILTERING:
4010                    if (message.arg1 == MULTICAST_V6) {
4011                        mWifiNative.startFilteringMulticastV6Packets();
4012                    } else if (message.arg1 == MULTICAST_V4) {
4013                        mWifiNative.startFilteringMulticastV4Packets();
4014                    } else {
4015                        loge("Illegal arugments to CMD_START_PACKET_FILTERING");
4016                    }
4017                    break;
4018                case CMD_STOP_PACKET_FILTERING:
4019                    if (message.arg1 == MULTICAST_V6) {
4020                        mWifiNative.stopFilteringMulticastV6Packets();
4021                    } else if (message.arg1 == MULTICAST_V4) {
4022                        mWifiNative.stopFilteringMulticastV4Packets();
4023                    } else {
4024                        loge("Illegal arugments to CMD_STOP_PACKET_FILTERING");
4025                    }
4026                    break;
4027                case CMD_SET_SUSPEND_OPT_ENABLED:
4028                    if (message.arg1 == 1) {
4029                        setSuspendOptimizationsNative(SUSPEND_DUE_TO_SCREEN, true);
4030                        mSuspendWakeLock.release();
4031                    } else {
4032                        setSuspendOptimizationsNative(SUSPEND_DUE_TO_SCREEN, false);
4033                    }
4034                    break;
4035                case CMD_SET_HIGH_PERF_MODE:
4036                    if (message.arg1 == 1) {
4037                        setSuspendOptimizationsNative(SUSPEND_DUE_TO_HIGH_PERF, false);
4038                    } else {
4039                        setSuspendOptimizationsNative(SUSPEND_DUE_TO_HIGH_PERF, true);
4040                    }
4041                    break;
4042                case CMD_ENABLE_TDLS:
4043                    if (message.obj != null) {
4044                        String remoteAddress = (String) message.obj;
4045                        boolean enable = (message.arg1 == 1);
4046                        mWifiNative.startTdls(remoteAddress, enable);
4047                    }
4048                    break;
4049                default:
4050                    return NOT_HANDLED;
4051            }
4052            return HANDLED;
4053        }
4054        @Override
4055        public void exit() {
4056            mIsRunning = false;
4057            updateBatteryWorkSource(null);
4058            mScanResults = new ArrayList<ScanResult>();
4059
4060            stopBatchedScan();
4061
4062            final Intent intent = new Intent(WifiManager.WIFI_SCAN_AVAILABLE);
4063            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
4064            intent.putExtra(WifiManager.EXTRA_SCAN_AVAILABLE, WIFI_STATE_DISABLED);
4065            mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
4066            noteScanEnd(); // wrap up any pending request.
4067            mBufferedScanMsg.clear();
4068
4069            mLastSetCountryCode = null;
4070        }
4071    }
4072
4073    class WaitForP2pDisableState extends State {
4074        private State mTransitionToState;
4075        @Override
4076        public void enter() {
4077            switch (getCurrentMessage().what) {
4078                case WifiMonitor.SUP_DISCONNECTION_EVENT:
4079                    mTransitionToState = mInitialState;
4080                    break;
4081                case CMD_DELAYED_STOP_DRIVER:
4082                    mTransitionToState = mDriverStoppingState;
4083                    break;
4084                case CMD_STOP_SUPPLICANT:
4085                    mTransitionToState = mSupplicantStoppingState;
4086                    break;
4087                default:
4088                    mTransitionToState = mDriverStoppingState;
4089                    break;
4090            }
4091            mWifiP2pChannel.sendMessage(WifiStateMachine.CMD_DISABLE_P2P_REQ);
4092        }
4093        @Override
4094        public boolean processMessage(Message message) {
4095            logStateAndMessage(message, getClass().getSimpleName());
4096
4097            switch(message.what) {
4098                case WifiStateMachine.CMD_DISABLE_P2P_RSP:
4099                    transitionTo(mTransitionToState);
4100                    break;
4101                /* Defer wifi start/shut and driver commands */
4102                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
4103                case CMD_START_SUPPLICANT:
4104                case CMD_STOP_SUPPLICANT:
4105                case CMD_START_AP:
4106                case CMD_STOP_AP:
4107                case CMD_START_DRIVER:
4108                case CMD_STOP_DRIVER:
4109                case CMD_SET_OPERATIONAL_MODE:
4110                case CMD_SET_COUNTRY_CODE:
4111                case CMD_SET_FREQUENCY_BAND:
4112                case CMD_START_PACKET_FILTERING:
4113                case CMD_STOP_PACKET_FILTERING:
4114                case CMD_START_SCAN:
4115                case CMD_DISCONNECT:
4116                case CMD_REASSOCIATE:
4117                case CMD_RECONNECT:
4118                    deferMessage(message);
4119                    break;
4120                default:
4121                    return NOT_HANDLED;
4122            }
4123            return HANDLED;
4124        }
4125    }
4126
4127    class DriverStoppingState extends State {
4128        @Override
4129        public boolean processMessage(Message message) {
4130            logStateAndMessage(message, getClass().getSimpleName());
4131
4132            switch(message.what) {
4133                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
4134                    SupplicantState state = handleSupplicantStateChange(message);
4135                    if (state == SupplicantState.INTERFACE_DISABLED) {
4136                        transitionTo(mDriverStoppedState);
4137                    }
4138                    break;
4139                    /* Queue driver commands */
4140                case CMD_START_DRIVER:
4141                case CMD_STOP_DRIVER:
4142                case CMD_SET_COUNTRY_CODE:
4143                case CMD_SET_FREQUENCY_BAND:
4144                case CMD_START_PACKET_FILTERING:
4145                case CMD_STOP_PACKET_FILTERING:
4146                case CMD_START_SCAN:
4147                case CMD_DISCONNECT:
4148                case CMD_REASSOCIATE:
4149                case CMD_RECONNECT:
4150                    deferMessage(message);
4151                    break;
4152                default:
4153                    return NOT_HANDLED;
4154            }
4155            return HANDLED;
4156        }
4157    }
4158
4159    class DriverStoppedState extends State {
4160        @Override
4161        public boolean processMessage(Message message) {
4162            logStateAndMessage(message, getClass().getSimpleName());
4163            switch (message.what) {
4164                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
4165                    StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
4166                    SupplicantState state = stateChangeResult.state;
4167                    // A WEXT bug means that we can be back to driver started state
4168                    // unexpectedly
4169                    if (SupplicantState.isDriverActive(state)) {
4170                        transitionTo(mDriverStartedState);
4171                    }
4172                    break;
4173                case CMD_START_DRIVER:
4174                    mWakeLock.acquire();
4175                    mWifiNative.startDriver();
4176                    mWakeLock.release();
4177                    transitionTo(mDriverStartingState);
4178                    break;
4179                default:
4180                    return NOT_HANDLED;
4181            }
4182            return HANDLED;
4183        }
4184    }
4185
4186    class ScanModeState extends State {
4187        private int mLastOperationMode;
4188        @Override
4189        public void enter() {
4190            mLastOperationMode = mOperationalMode;
4191        }
4192        @Override
4193        public boolean processMessage(Message message) {
4194            logStateAndMessage(message, getClass().getSimpleName());
4195
4196            switch(message.what) {
4197                case CMD_SET_OPERATIONAL_MODE:
4198                    if (message.arg1 == CONNECT_MODE) {
4199
4200                        if (mLastOperationMode == SCAN_ONLY_WITH_WIFI_OFF_MODE) {
4201                            setWifiState(WIFI_STATE_ENABLED);
4202                            // Load and re-enable networks when going back to enabled state
4203                            // This is essential for networks to show up after restore
4204                            mWifiConfigStore.loadAndEnableAllNetworks();
4205                            mWifiP2pChannel.sendMessage(CMD_ENABLE_P2P);
4206                        } else {
4207                            mWifiConfigStore.enableAllNetworks();
4208                        }
4209
4210                        mWifiNative.reconnect();
4211
4212                        mOperationalMode = CONNECT_MODE;
4213                        transitionTo(mDisconnectedState);
4214                    } else {
4215                        // Nothing to do
4216                        return HANDLED;
4217                    }
4218                    break;
4219                // Handle scan. All the connection related commands are
4220                // handled only in ConnectModeState
4221                case CMD_START_SCAN:
4222                    handleScanRequest(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, message);
4223                    break;
4224                default:
4225                    return NOT_HANDLED;
4226            }
4227            return HANDLED;
4228        }
4229    }
4230
4231
4232    String smToString(Message message) {
4233        String s = "unknown";
4234        switch(message.what) {
4235
4236            case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
4237                s = "AsyncChannel.CMD_CHANNEL_HALF_CONNECTED";
4238                break;
4239            case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
4240                s = "AsyncChannel.CMD_CHANNEL_DISCONNECTED";
4241                break;
4242            case CMD_SET_FREQUENCY_BAND:
4243                s = "CMD_SET_FREQUENCY_BAND";
4244                break;
4245            case CMD_START_DRIVER:
4246                s = "CMD_START_DRIVER";
4247                break;
4248            case CMD_STOP_DRIVER:
4249                s = "CMD_STOP_DRIVER";
4250                break;
4251            case CMD_STOP_SUPPLICANT:
4252                s = "CMD_STOP_SUPPLICANT";
4253                break;
4254            case CMD_START_SUPPLICANT:
4255                s = "CMD_START_SUPPLICANT";
4256                break;
4257            case CMD_REQUEST_AP_CONFIG:
4258                s = "CMD_REQUEST_AP_CONFIG";
4259                break;
4260            case CMD_RESPONSE_AP_CONFIG:
4261                s = "CMD_RESPONSE_AP_CONFIG";
4262                break;
4263            case CMD_TETHER_STATE_CHANGE:
4264                s = "CMD_TETHER_STATE_CHANGE";
4265                break;
4266            case CMD_TETHER_NOTIFICATION_TIMED_OUT:
4267                s = "CMD_TETHER_NOTIFICATION_TIMED_OUT";
4268                break;
4269            case CMD_BLUETOOTH_ADAPTER_STATE_CHANGE:
4270                s = "CMD_BLUETOOTH_ADAPTER_STATE_CHANGE";
4271                break;
4272            case CMD_ADD_OR_UPDATE_NETWORK:
4273                s= "CMD_ADD_OR_UPDATE_NETWORK";
4274                break;
4275            case CMD_REMOVE_NETWORK:
4276                s= "CMD_REMOVE_NETWORK";
4277                break;
4278            case CMD_ENABLE_NETWORK:
4279                s= "CMD_ENABLE_NETWORK";
4280                break;
4281            case CMD_ENABLE_ALL_NETWORKS:
4282                s= "CMD_ENABLE_ALL_NETWORKS";
4283                break;
4284            case CMD_AUTO_CONNECT:
4285                s = "CMD_AUTO_CONNECT";
4286                break;
4287            case CMD_AUTO_ROAM:
4288                s = "CMD_AUTO_ROAM";
4289                break;
4290            case CMD_BOOT_COMPLETED:
4291                s = "CMD_BOOT_COMPLETED";
4292                break;
4293            case DhcpStateMachine.CMD_START_DHCP:
4294                s = "DhcpStateMachine.CMD_START_DHCP";
4295                break;
4296            case DhcpStateMachine.CMD_STOP_DHCP:
4297                s = "DhcpStateMachine.CMD_STOP_DHCP";
4298                break;
4299            case DhcpStateMachine.CMD_RENEW_DHCP:
4300                s = "DhcpStateMachine.CMD_RENEW_DHCP";
4301                break;
4302            case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
4303                s = "DhcpStateMachine.CMD_PRE_DHCP_ACTION";
4304                break;
4305            case DhcpStateMachine.CMD_POST_DHCP_ACTION:
4306                s = "DhcpStateMachine.CMD_POST_DHCP_ACTION";
4307                break;
4308            case DhcpStateMachine.CMD_PRE_DHCP_ACTION_COMPLETE:
4309                s = "DhcpStateMachine.CMD_PRE_DHCP_ACTION_COMPLETE";
4310                break;
4311            case DhcpStateMachine.CMD_ON_QUIT:
4312                s = "DhcpStateMachine.CMD_ON_QUIT";
4313                break;
4314            case WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST:
4315                s = "WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST";
4316                break;
4317            case WifiManager.DISABLE_NETWORK:
4318                s="WifiManager.DISABLE_NETWORK";
4319                break;
4320            case CMD_BLACKLIST_NETWORK:
4321                s="CMD_BLACKLIST_NETWORK";
4322                break;
4323            case CMD_CLEAR_BLACKLIST:
4324                s="CMD_CLEAR_BLACKLIST";
4325                break;
4326            case CMD_SAVE_CONFIG:
4327                s="CMD_SAVE_CONFIG";
4328                break;
4329            case CMD_GET_CONFIGURED_NETWORKS:
4330                s="CMD_GET_CONFIGURED_NETWORKS";
4331                break;
4332            case CMD_DISCONNECT:
4333                s="CMD_DISCONNECT";
4334                break;
4335            case CMD_RECONNECT:
4336                s= "CMD_RECONNECT";
4337                break;
4338            case CMD_REASSOCIATE:
4339                s= "CMD_REASSOCIATE";
4340                break;
4341            case CMD_SET_HIGH_PERF_MODE:
4342                s="CMD_SET_HIGH_PERF_MODE";
4343                break;
4344            case CMD_SET_COUNTRY_CODE:
4345                s="CMD_SET_COUNTRY_CODE";
4346                break;
4347            case CMD_ENABLE_RSSI_POLL:
4348                s="CMD_ENABLE_RSSI_POLL";
4349                break;
4350            case CMD_RSSI_POLL:
4351                s="CMD_RSSI_POLL";
4352                break;
4353            case CMD_START_PACKET_FILTERING:
4354                s="CMD_START_PACKET_FILTERING";
4355                break;
4356            case CMD_STOP_PACKET_FILTERING:
4357                s="CMD_STOP_PACKET_FILTERING";
4358                break;
4359            case CMD_SET_SUSPEND_OPT_ENABLED:
4360                s="CMD_SET_SUSPEND_OPT_ENABLED";
4361                break;
4362            case CMD_NO_NETWORKS_PERIODIC_SCAN:
4363                s="CMD_NO_NETWORKS_PERIODIC_SCAN";
4364                break;
4365            case CMD_SET_BATCHED_SCAN:
4366                s="CMD_SET_BATCHED_SCAN";
4367                break;
4368            case CMD_START_NEXT_BATCHED_SCAN:
4369                s="CMD_START_NEXT_BATCHED_SCAN";
4370                break;
4371            case CMD_POLL_BATCHED_SCAN:
4372                s="CMD_POLL_BATCHED_SCAN";
4373                break;
4374            case CMD_NETLINK_UPDATE:
4375                s="CMD_NETLINK_UPDATE";
4376                break;
4377            case CMD_RELOAD_TLS_AND_RECONNECT:
4378                s= "CMD_RELOAD_TLS_AND_RECONNECT";
4379                break;
4380            case WifiManager.CONNECT_NETWORK:
4381                s= "WifiManager.CONNECT_NETWORK";
4382                break;
4383            case WifiManager.SAVE_NETWORK:
4384                s= "WifiManager.SAVE_NETWORK";
4385                break;
4386            case WifiManager.FORGET_NETWORK:
4387                s = "WifiManager.FORGET_NETWORK";
4388                break;
4389            case WifiManager.START_WPS:
4390                s= "WifiManager.START_WPS";
4391                break;
4392            case WifiMonitor.SUP_CONNECTION_EVENT:
4393                s= "WifiMonitor.SUP_CONNECTION_EVENT";
4394                break;
4395            case WifiMonitor.SUP_DISCONNECTION_EVENT:
4396                s= "WifiMonitor.SUP_DISCONNECTION_EVENT";
4397                break;
4398            case WifiMonitor.SCAN_RESULTS_EVENT:
4399                s= "WifiMonitor.SCAN_RESULTS_EVENT";
4400                break;
4401            case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
4402                s= "WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT";
4403                break;
4404            case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
4405                s= "WifiMonitor.AUTHENTICATION_FAILURE_EVENT";
4406                break;
4407            case WifiMonitor.SSID_TEMP_DISABLED:
4408                s= "WifiMonitor.SSID_TEMP_DISABLED";
4409                break;
4410            case WifiMonitor.SSID_REENABLED:
4411                s= "WifiMonitor.SSID_REENABLED";
4412                break;
4413            case WifiMonitor.WPS_SUCCESS_EVENT:
4414                s= "WPS_SUCCESS_EVENT";
4415                break;
4416            case WifiMonitor.WPS_FAIL_EVENT:
4417                s= "WPS_FAIL_EVENT";
4418                break;
4419            case WifiMonitor.NETWORK_CONNECTION_EVENT:
4420                s= "networkConnectionEvent";
4421                break;
4422            case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
4423                s="networkDisconnectionEvent";
4424                break;
4425            case CMD_SET_OPERATIONAL_MODE:
4426                s="CMD_SET_OPERATIONAL_MODE";
4427                break;
4428            case CMD_START_SCAN:
4429                s="CMD_START_SCAN";
4430                break;
4431            case CMD_ENABLE_BACKGROUND_SCAN:
4432                s="CMD_ENABLE_BACKGROUND_SCAN";
4433                break;
4434        }
4435        return s;
4436    }
4437
4438    void registerConnected() {
4439       if (mLastNetworkId != WifiConfiguration.INVALID_NETWORK_ID) {
4440           long now_ms = System.currentTimeMillis();
4441           //we are switching away from this configuration,
4442           //hence record the time we were connected last
4443           WifiConfiguration config = mWifiConfigStore.getWifiConfiguration(mLastNetworkId);
4444           if (config != null) {
4445               config.lastConnected = System.currentTimeMillis();
4446           }
4447       }
4448    }
4449
4450    void registerDisconnected() {
4451        if (mLastNetworkId != WifiConfiguration.INVALID_NETWORK_ID) {
4452            long now_ms = System.currentTimeMillis();
4453            //we are switching away from this configuration,
4454            //hence record the time we were connected last
4455            WifiConfiguration config = mWifiConfigStore.getWifiConfiguration(mLastNetworkId);
4456            if (config != null) {
4457                config.lastDisconnected = System.currentTimeMillis();
4458            }
4459        }
4460    }
4461
4462    WifiConfiguration getCurrentWifiConfiguration() {
4463        if (mLastNetworkId == WifiConfiguration.INVALID_NETWORK_ID) {
4464            return null;
4465        }
4466        return mWifiConfigStore.getWifiConfiguration(mLastNetworkId);
4467    }
4468
4469    String getCurrentBSSID() {
4470        return mLastBssid;
4471    }
4472
4473    class ConnectModeState extends State {
4474        @Override
4475        public boolean processMessage(Message message) {
4476            WifiConfiguration config;
4477            int netId;
4478            boolean ok;
4479            NetworkUpdateResult result;
4480            logStateAndMessage(message, getClass().getSimpleName());
4481
4482            switch(message.what) {
4483                case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
4484                    mSupplicantStateTracker.sendMessage(WifiMonitor.ASSOCIATION_REJECTION_EVENT);
4485                    break;
4486                case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
4487                    mSupplicantStateTracker.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT);
4488                    break;
4489                case WifiMonitor.SSID_TEMP_DISABLED:
4490                case WifiMonitor.SSID_REENABLED:
4491                    String substr = (String)message.obj;
4492                    String en = message.what == WifiMonitor.SSID_TEMP_DISABLED ?
4493                             "temp-disabled" : "re-enabled";
4494                    loge("ConnectModeState SSID state=" + en + " nid="
4495                            + Integer.toString(message.arg1) + " [" + substr + "]");
4496                    mWifiConfigStore.handleSSIDStateChange(message.arg1, message.what ==
4497                            WifiMonitor.SSID_REENABLED, substr);
4498                    break;
4499                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
4500                    SupplicantState state = handleSupplicantStateChange(message);
4501                    // A driver/firmware hang can now put the interface in a down state.
4502                    // We detect the interface going down and recover from it
4503                    if (!SupplicantState.isDriverActive(state)) {
4504                        if (mNetworkInfo.getState() != NetworkInfo.State.DISCONNECTED) {
4505                            handleNetworkDisconnect();
4506                        }
4507                        log("Detected an interface down, restart driver");
4508                        transitionTo(mDriverStoppedState);
4509                        sendMessage(CMD_START_DRIVER);
4510                        break;
4511                    }
4512
4513                    // Supplicant can fail to report a NETWORK_DISCONNECTION_EVENT
4514                    // when authentication times out after a successful connection,
4515                    // we can figure this from the supplicant state. If supplicant
4516                    // state is DISCONNECTED, but the mNetworkInfo says we are not
4517                    // disconnected, we need to handle a disconnection
4518                    if (state == SupplicantState.DISCONNECTED &&
4519                            mNetworkInfo.getState() != NetworkInfo.State.DISCONNECTED) {
4520                        if (DBG) log("Missed CTRL-EVENT-DISCONNECTED, disconnect");
4521                        handleNetworkDisconnect();
4522                        transitionTo(mDisconnectedState);
4523                    }
4524                    break;
4525                case WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST:
4526                    if (message.arg1 == 1) {
4527                        mWifiNative.disconnect();
4528                        mTemporarilyDisconnectWifi = true;
4529                    } else {
4530                        mWifiNative.reconnect();
4531                        mTemporarilyDisconnectWifi = false;
4532                    }
4533                    break;
4534                case CMD_ADD_OR_UPDATE_NETWORK:
4535                    config = (WifiConfiguration) message.obj;
4536                    replyToMessage(message, CMD_ADD_OR_UPDATE_NETWORK,
4537                            mWifiConfigStore.addOrUpdateNetwork(config));
4538                    break;
4539                case CMD_REMOVE_NETWORK:
4540                    ok = mWifiConfigStore.removeNetwork(message.arg1);
4541                    replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
4542                    break;
4543                case CMD_ENABLE_NETWORK:
4544                    boolean others = message.arg2 == 1;
4545                    // Tell autojoin the user did try to select to that network
4546                    // However, do NOT persist the choice by bumping the priority of the network
4547                    if (others && mFrameworkAutoJoin.get()) {
4548                        mWifiAutoJoinController.
4549                                updateConfigurationHistory(message.arg1, true, false);
4550                    }
4551                    // Set the last selected configuration so as to allow the system to
4552                    // stick the last user choice without persisting the choice
4553                    mWifiConfigStore.setLastSelectedConfiguration(message.arg1);
4554
4555                    //cancel auto roam requests
4556                    autoRoamSetBSSID(message.arg1, "any");
4557
4558                    ok = mWifiConfigStore.enableNetwork(message.arg1, message.arg2 == 1);
4559                    replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
4560                    break;
4561                case CMD_ENABLE_ALL_NETWORKS:
4562                    long time =  android.os.SystemClock.elapsedRealtime();
4563                    if (time - mLastEnableAllNetworksTime > MIN_INTERVAL_ENABLE_ALL_NETWORKS_MS) {
4564                        mWifiConfigStore.enableAllNetworks();
4565                        mLastEnableAllNetworksTime = time;
4566                    }
4567                    break;
4568                case WifiManager.DISABLE_NETWORK:
4569                    if (mWifiConfigStore.disableNetwork(message.arg1,
4570                            WifiConfiguration.DISABLED_UNKNOWN_REASON) == true) {
4571                        replyToMessage(message, WifiManager.DISABLE_NETWORK_SUCCEEDED);
4572                    } else {
4573                        replyToMessage(message, WifiManager.DISABLE_NETWORK_FAILED,
4574                                WifiManager.ERROR);
4575                    }
4576                    break;
4577                case CMD_BLACKLIST_NETWORK:
4578                    mWifiNative.addToBlacklist((String)message.obj);
4579                    break;
4580                case CMD_CLEAR_BLACKLIST:
4581                    mWifiNative.clearBlacklist();
4582                    break;
4583                case CMD_SAVE_CONFIG:
4584                    ok = mWifiConfigStore.saveConfig();
4585
4586                    loge("wifistatemachine did save config " + ok);
4587                    replyToMessage(message, CMD_SAVE_CONFIG, ok ? SUCCESS : FAILURE);
4588
4589                    // Inform the backup manager about a data change
4590                    IBackupManager ibm = IBackupManager.Stub.asInterface(
4591                            ServiceManager.getService(Context.BACKUP_SERVICE));
4592                    if (ibm != null) {
4593                        try {
4594                            ibm.dataChanged("com.android.providers.settings");
4595                        } catch (Exception e) {
4596                            // Try again later
4597                        }
4598                    }
4599                    break;
4600                case CMD_GET_CONFIGURED_NETWORKS:
4601                    replyToMessage(message, message.what,
4602                            mWifiConfigStore.getConfiguredNetworks());
4603                    break;
4604                    /* Do a redundant disconnect without transition */
4605                case CMD_DISCONNECT:
4606                    mWifiConfigStore.setLastSelectedConfiguration
4607                            (WifiConfiguration.INVALID_NETWORK_ID);
4608                    mWifiNative.disconnect();
4609                    break;
4610                case CMD_RECONNECT:
4611                    mWifiNative.reconnect();
4612                    break;
4613                case CMD_REASSOCIATE:
4614                    mWifiNative.reassociate();
4615                    break;
4616                case CMD_RELOAD_TLS_AND_RECONNECT:
4617                    if (mWifiConfigStore.needsUnlockedKeyStore()) {
4618                        logd("Reconnecting to give a chance to un-connected TLS networks");
4619                        mWifiNative.disconnect();
4620                        mWifiNative.reconnect();
4621                    }
4622                    break;
4623                case CMD_AUTO_ROAM:
4624                    return HANDLED;
4625                case CMD_AUTO_CONNECT:
4626                    /* Work Around: wpa_supplicant can get in a bad state where it returns a non
4627                     * associated status thus the STATUS command but somehow-someplace still thinks
4628                     * it is associated and thus will ignore select/reconnect command with
4629                     * following message:
4630                     * "Already associated with the selected network - do nothing"
4631                     *
4632                     * Hence, sends a disconnect to supplicant first.
4633                     */
4634                    mWifiNative.disconnect();
4635
4636                    /* connect command coming from auto-join */
4637                    config = (WifiConfiguration) message.obj;
4638                    netId = message.arg1;
4639                    int roam = message.arg2;
4640                    loge("CMD_AUTO_CONNECT sup state "
4641                            + mSupplicantStateTracker.getSupplicantStateName()
4642                            + " my state " + getCurrentState().getName()
4643                            + " nid=" + Integer.toString(netId)
4644                            + " roam=" + Integer.toString(roam));
4645                    if (config == null) {
4646                        loge("AUTO_CONNECT and no config, bail out...");
4647                        break;
4648                    }
4649
4650                    /* make sure we cancel any previous roam request */
4651                    autoRoamSetBSSID(config, "any");
4652
4653                    /* Save the network config */
4654                    loge("CMD_AUTO_CONNECT will save config -> " + config.SSID
4655                                + " nid=" + Integer.toString(netId));
4656                    result = mWifiConfigStore.saveNetwork(config);
4657                    netId = result.getNetworkId();
4658                    loge("CMD_AUTO_CONNECT did save config -> "
4659                                + " nid=" + Integer.toString(netId));
4660
4661                    if (mWifiConfigStore.selectNetwork(netId) &&
4662                            mWifiNative.reconnect()) {
4663                        // we selected a better config,
4664                        // maybe because we could not see the last user
4665                        // selection, then forget it. We will remember the selection
4666                        // only if it was persisted.
4667                        mWifiConfigStore.
4668                                setLastSelectedConfiguration(WifiConfiguration.INVALID_NETWORK_ID);
4669
4670                        /* The state tracker handles enabling networks upon completion/failure */
4671                        mSupplicantStateTracker.sendMessage(WifiManager.CONNECT_NETWORK);
4672                        //replyToMessage(message, WifiManager.CONNECT_NETWORK_SUCCEEDED);
4673                        mAutoRoaming = roam;
4674                        if (isRoaming()) {
4675                            transitionTo(mRoamingState);
4676                        } else {
4677                            transitionTo(mDisconnectingState);
4678                        }
4679                    } else {
4680                        loge("Failed to connect config: " + config + " netId: " + netId);
4681                        replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
4682                                WifiManager.ERROR);
4683                        break;
4684                    }
4685                    break;
4686                case WifiManager.CONNECT_NETWORK:
4687                    /* The connect message can contain a network id passed as arg1 on message or
4688                     * or a config passed as obj on message.
4689                     * For a new network, a config is passed to create and connect.
4690                     * For an existing network, a network id is passed
4691                     */
4692                    netId = message.arg1;
4693                    config = (WifiConfiguration) message.obj;
4694
4695                    if (config == null) {
4696                        loge("CONNECT_NETWORK id=" + Integer.toString(netId) + " "
4697                                + mSupplicantStateTracker.getSupplicantStateName() + " my state "
4698                                + getCurrentState().getName());
4699                    } else {
4700                        loge("CONNECT_NETWORK id=" + Integer.toString(netId)
4701                                + " config=" + config.SSID
4702                                + " cnid=" + config.networkId
4703                                + " supstate=" + mSupplicantStateTracker.getSupplicantStateName()
4704                                + " my state " + getCurrentState().getName());
4705                    }
4706
4707                    autoRoamSetBSSID(netId, "any");
4708                    mAutoRoaming = WifiAutoJoinController.AUTO_JOIN_IDLE;
4709
4710                    /* Save the network config */
4711                    if (config != null) {
4712                        result = mWifiConfigStore.saveNetwork(config);
4713                        netId = result.getNetworkId();
4714                    }
4715                    if (mFrameworkAutoJoin.get()) {
4716                        /* Tell autojoin the user did try to connect to that network */
4717                        mWifiAutoJoinController.updateConfigurationHistory(netId, true, true);
4718                    }
4719                    mWifiConfigStore.setLastSelectedConfiguration(netId);
4720
4721                    if (mWifiConfigStore.selectNetwork(netId) &&
4722                            mWifiNative.reconnect()) {
4723                        /* The state tracker handles enabling networks upon completion/failure */
4724                        mSupplicantStateTracker.sendMessage(WifiManager.CONNECT_NETWORK);
4725                        replyToMessage(message, WifiManager.CONNECT_NETWORK_SUCCEEDED);
4726                        /* Expect a disconnection from the old connection */
4727                        transitionTo(mDisconnectingState);
4728                    } else {
4729                        loge("Failed to connect config: " + config + " netId: " + netId);
4730                        replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
4731                                WifiManager.ERROR);
4732                        break;
4733                    }
4734                    break;
4735                case WifiManager.SAVE_NETWORK:
4736                    config = (WifiConfiguration) message.obj;
4737                    int nid = config.networkId;
4738                    if (config == null) {
4739                        loge("SAVE_NETWORK id=" + Integer.toString(nid)
4740                                + " " + mSupplicantStateTracker.getSupplicantStateName()
4741                                + " my state " + getCurrentState().getName());
4742                    } else {
4743                        loge("SAVE_NETWORK id=" + Integer.toString(nid)
4744                                + " config=" + config.SSID
4745                                + " cnid=" + config.networkId
4746                                + " supstate=" + mSupplicantStateTracker.getSupplicantStateName()
4747                                + " my state " + getCurrentState().getName());
4748                    }
4749
4750                    result = mWifiConfigStore.saveNetwork(config);
4751                    if (result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID) {
4752                        replyToMessage(message, WifiManager.SAVE_NETWORK_SUCCEEDED);
4753                        if (VDBG) {
4754                            loge("Success save network nid="
4755                                    + Integer.toString(result.getNetworkId())
4756                                    + " autojoin " + mFrameworkAutoJoin.get());
4757                        }
4758                        if (mFrameworkAutoJoin.get()) {
4759                            /* Tell autojoin the user did try to modify and save that network */
4760                            mWifiAutoJoinController.updateConfigurationHistory(result.getNetworkId()
4761                                    ,true, false);
4762                            mWifiAutoJoinController.attemptAutoJoin();
4763                        }
4764                    } else {
4765                        loge("Failed to save network");
4766                        replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED,
4767                                WifiManager.ERROR);
4768                    }
4769                    break;
4770                case WifiManager.FORGET_NETWORK:
4771                    if (mWifiConfigStore.forgetNetwork(message.arg1)) {
4772                        replyToMessage(message, WifiManager.FORGET_NETWORK_SUCCEEDED);
4773                    } else {
4774                        loge("Failed to forget network");
4775                        replyToMessage(message, WifiManager.FORGET_NETWORK_FAILED,
4776                                WifiManager.ERROR);
4777                    }
4778                    break;
4779                case WifiManager.START_WPS:
4780                    WpsInfo wpsInfo = (WpsInfo) message.obj;
4781                    WpsResult wpsResult;
4782                    switch (wpsInfo.setup) {
4783                        case WpsInfo.PBC:
4784                            wpsResult = mWifiConfigStore.startWpsPbc(wpsInfo);
4785                            break;
4786                        case WpsInfo.KEYPAD:
4787                            wpsResult = mWifiConfigStore.startWpsWithPinFromAccessPoint(wpsInfo);
4788                            break;
4789                        case WpsInfo.DISPLAY:
4790                            wpsResult = mWifiConfigStore.startWpsWithPinFromDevice(wpsInfo);
4791                            break;
4792                        default:
4793                            wpsResult = new WpsResult(Status.FAILURE);
4794                            loge("Invalid setup for WPS");
4795                            break;
4796                    }
4797                    mWifiConfigStore.setLastSelectedConfiguration
4798                            (WifiConfiguration.INVALID_NETWORK_ID);
4799                    if (wpsResult.status == Status.SUCCESS) {
4800                        replyToMessage(message, WifiManager.START_WPS_SUCCEEDED, wpsResult);
4801                        transitionTo(mWpsRunningState);
4802                    } else {
4803                        loge("Failed to start WPS with config " + wpsInfo.toString());
4804                        replyToMessage(message, WifiManager.WPS_FAILED, WifiManager.ERROR);
4805                    }
4806                    break;
4807                case WifiMonitor.NETWORK_CONNECTION_EVENT:
4808                    if (DBG) log("Network connection established");
4809                    mLastNetworkId = message.arg1;
4810                    mLastBssid = (String) message.obj;
4811
4812                    mWifiInfo.setBSSID(mLastBssid);
4813                    mWifiInfo.setNetworkId(mLastNetworkId);
4814                    /* send event to CM & network change broadcast */
4815                    setNetworkDetailedState(DetailedState.OBTAINING_IPADDR);
4816                    sendNetworkStateChangeBroadcast(mLastBssid);
4817                    transitionTo(mObtainingIpState);
4818                    break;
4819                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
4820                    if (DBG) log("Network connection lost");
4821                    handleNetworkDisconnect();
4822                    transitionTo(mDisconnectedState);
4823                    break;
4824                default:
4825                    return NOT_HANDLED;
4826            }
4827            return HANDLED;
4828        }
4829    }
4830
4831    private class WifiNetworkAgent extends NetworkAgent {
4832        public WifiNetworkAgent(Looper l, Context c, String TAG, NetworkInfo ni,
4833                NetworkCapabilities nc, LinkProperties lp, int score) {
4834            super(l, c, TAG, ni, nc, lp, score);
4835        }
4836        protected void unwanted() {
4837            // ignore if we're not the current networkAgent.
4838            if (this != mNetworkAgent) return;
4839            // TODO - don't want this network.  What to do?
4840            if (DBG) log("WifiNetworkAgent -> Wifi unwanted score "
4841                    + Integer.toString(mWifiInfo.score));
4842            unwantedNetwork();
4843        }
4844    }
4845
4846    void unwantedNetwork() {
4847        sendMessage(CMD_UNWANTED_NETWORK);
4848    }
4849
4850    class L2ConnectedState extends State {
4851        @Override
4852        public void enter() {
4853            mRssiPollToken++;
4854            if (mEnableRssiPolling) {
4855                sendMessage(CMD_RSSI_POLL, mRssiPollToken, 0);
4856            }
4857            if (mNetworkAgent != null) {
4858                loge("Have NetworkAgent when entering L2Connected");
4859                setNetworkDetailedState(DetailedState.DISCONNECTED);
4860            }
4861            setNetworkDetailedState(DetailedState.CONNECTING);
4862            mNetworkAgent = new WifiNetworkAgent(getHandler().getLooper(), mContext,
4863                    "WifiNetworkAgent", mNetworkInfo, mNetworkCapabilitiesFilter,
4864                    mLinkProperties, 60);
4865        }
4866
4867        @Override
4868        public void exit() {
4869            handleNetworkDisconnect();
4870        }
4871
4872        @Override
4873        public boolean processMessage(Message message) {
4874            logStateAndMessage(message, getClass().getSimpleName());
4875
4876            switch (message.what) {
4877              case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
4878                  handlePreDhcpSetup();
4879                  break;
4880              case DhcpStateMachine.CMD_POST_DHCP_ACTION:
4881                  handlePostDhcpSetup();
4882                  if (message.arg1 == DhcpStateMachine.DHCP_SUCCESS) {
4883                      if (DBG) log("DHCP successful");
4884                      handleSuccessfulIpConfiguration((DhcpResults) message.obj);
4885                      transitionTo(mVerifyingLinkState);
4886                  } else if (message.arg1 == DhcpStateMachine.DHCP_FAILURE) {
4887                      if (DBG) log("DHCP failed");
4888                      handleFailedIpConfiguration();
4889                      transitionTo(mDisconnectingState);
4890                  }
4891                  break;
4892                case CMD_DISCONNECT:
4893                    mWifiNative.disconnect();
4894                    transitionTo(mDisconnectingState);
4895                    break;
4896                case WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST:
4897                    if (message.arg1 == 1) {
4898                        mWifiNative.disconnect();
4899                        mTemporarilyDisconnectWifi = true;
4900                        transitionTo(mDisconnectingState);
4901                    }
4902                    break;
4903                case CMD_SET_OPERATIONAL_MODE:
4904                    if (message.arg1 != CONNECT_MODE) {
4905                        sendMessage(CMD_DISCONNECT);
4906                        deferMessage(message);
4907                    }
4908                    break;
4909                case CMD_SET_COUNTRY_CODE:
4910                    deferMessage(message);
4911                    break;
4912                case CMD_START_SCAN:
4913                    if (DBG) {
4914                        loge("WifiStateMachine CMD_START_SCAN source " + message.arg1
4915                              + " txSuccessRate="+String.format( "%.2f", mWifiInfo.txSuccessRate)
4916                              + " rxSuccessRate="+String.format( "%.2f", mWifiInfo.rxSuccessRate)
4917                              + " BSSID=" + mTargetRoamBSSID
4918                              + " RSSI=" + mWifiInfo.getRssi());
4919                    }
4920                    if (message.arg1 == SCAN_ALARM_SOURCE) {
4921                        boolean tryFullBandScan = false;
4922                        boolean restrictChannelList = false;
4923                        long now_ms = System.currentTimeMillis();
4924                        if (mWifiInfo != null) {
4925                            if ((now_ms - lastFullBandConnectedTimeMilli)
4926                                    > fullBandConnectedTimeIntervalMilli) {
4927                                if (DBG) {
4928                                    loge("WifiStateMachine CMD_START_SCAN try full band scan age="
4929                                          + Long.toString(now_ms - lastFullBandConnectedTimeMilli)
4930                                          + " interval=" + fullBandConnectedTimeIntervalMilli);
4931                                }
4932                                tryFullBandScan = true;
4933                            }
4934
4935                            //TODO: really tune the logic
4936                            //TODO: right now we  discriminate only between full band and partial
4937                            //TODO: scans however partial scans can have many channels
4938                            //TODO: and be almost same as full band
4939                            //TODO: also look and exclude broadcast Rx packets
4940                            if (mWifiInfo.txSuccessRate > 5 || mWifiInfo.rxSuccessRate > 30) {
4941                                // too much traffic at the interface, hence no full band scan
4942                                if (DBG) {
4943                                    loge("WifiStateMachine CMD_START_SCAN " +
4944                                            "prevent full band scan due to pkt rate");
4945                                }
4946                                tryFullBandScan = false;
4947                            }
4948
4949                            if (mWifiInfo.txSuccessRate > 25 || mWifiInfo.rxSuccessRate > 80) {
4950                                // don't scan aggressively if lots of packets are being sent
4951                                restrictChannelList = true;
4952                                if (mAllowScansWithTraffic == 0
4953                                     || (getCurrentWifiConfiguration().scanResultCache == null
4954                                     || getCurrentWifiConfiguration().scanResultCache.size() > 4
4955                                     || mWifiInfo.getRssi()
4956                                     > (WifiConfiguration.A_BAND_PREFERENCE_RSSI_THRESHOLD - 5))) {
4957                                    //there is no need to scan because, either the configuration
4958                                    //has many
4959                                    //BSSIDs and thus roaming is left to the chip,
4960                                    //or,
4961                                    //RSSI is very good and then the potential roaming gain is not
4962                                    //high enough to outweigh the negative impact on traffic
4963                                    if (DBG) {
4964                                     loge("WifiStateMachine CMD_START_SCAN source " + message.arg1
4965                                        + " ...and ignore scans"
4966                                        + " tx=" + String.format("%.2f", mWifiInfo.txSuccessRate)
4967                                        + " rx=" + String.format("%.2f", mWifiInfo.rxSuccessRate));
4968                                    }
4969                                    return HANDLED;
4970                                }
4971                            }
4972                        }
4973
4974                        WifiConfiguration currentConfiguration = getCurrentWifiConfiguration();
4975                        if (currentConfiguration != null) {
4976                            if (tryFullBandScan) {
4977                                lastFullBandConnectedTimeMilli = now_ms;
4978                                if (fullBandConnectedTimeIntervalMilli < 20 * 1000) {
4979                                    //paranoia, make sure interval is not less than 20 seconds
4980                                    fullBandConnectedTimeIntervalMilli = 20 * 1000;
4981                                }
4982                                if (fullBandConnectedTimeIntervalMilli
4983                                        < maxFullBandConnectedTimeIntervalMilli) {
4984                                    //increase the interval
4985                                    fullBandConnectedTimeIntervalMilli
4986                                            = fullBandConnectedTimeIntervalMilli * 3 / 2;
4987                                }
4988                                handleScanRequest(
4989                                        WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, message);
4990                            } else {
4991                                HashSet<Integer> channels
4992                                        = mWifiConfigStore.makeChannelList(currentConfiguration,
4993                                        ONE_HOUR_MILLI, restrictChannelList);
4994                                if (channels != null && channels.size() != 0) {
4995                                    StringBuilder freqs = new StringBuilder();
4996                                    boolean first = true;
4997                                    for (Integer channel : channels) {
4998                                        if (!first)
4999                                            freqs.append(",");
5000                                        freqs.append(channel.toString());
5001                                        first = false;
5002                                    }
5003                                    if (DBG) {
5004                                        loge("WifiStateMachine starting scan with " + freqs);
5005                                    }
5006                                    // call wifi native to start the scan
5007                                    if (startScanNative(SCAN_ONLY_MODE, freqs.toString())) {
5008                                        // only count battery consumption if scan request is accepted
5009                                        noteScanStart(SCAN_ALARM_SOURCE, null);
5010                                    }
5011                                } else {
5012                                    if (DBG) {
5013                                        loge("WifiStateMachine starting scan, did not find channels");
5014                                    }
5015                                    handleScanRequest(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, message);
5016                                }
5017                            }
5018                        }
5019                    } else {
5020                        handleScanRequest(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, message);
5021                    }
5022                    break;
5023                    /* Ignore connection to same network */
5024                case WifiManager.CONNECT_NETWORK:
5025                    int netId = message.arg1;
5026                    if (mWifiInfo.getNetworkId() == netId) {
5027                        break;
5028                    }
5029                    return NOT_HANDLED;
5030                case WifiManager.SAVE_NETWORK:
5031                    WifiConfiguration config = (WifiConfiguration) message.obj;
5032                    int nid = config.networkId;
5033                    if (config == null) {
5034                        loge("SAVE_NETWORK-L2 id=" + Integer.toString(nid)
5035                                + " " + mSupplicantStateTracker.getSupplicantStateName()
5036                                + " my state " + getCurrentState().getName());
5037                    } else {
5038                        loge("SAVE_NETWORK-L2 id=" + Integer.toString(nid)
5039                                + " SSID=" + config.SSID
5040                                + " cnid=" + config.networkId
5041                                + " autojoin=" + Integer.toString(config.autoJoinStatus)
5042                                + " supstate=" + mSupplicantStateTracker.getSupplicantStateName()
5043                                + " my state " + getCurrentState().getName()
5044                                + " uid " + Integer.toString(config.creatorUid));
5045                    }
5046
5047                    NetworkUpdateResult result = mWifiConfigStore.saveNetwork(config);
5048                    if (mWifiInfo.getNetworkId() == result.getNetworkId()) {
5049                        if (result.hasIpChanged()) {
5050                            log("Reconfiguring IP on connection");
5051                            transitionTo(mObtainingIpState);
5052                        }
5053                        if (result.hasProxyChanged()) {
5054                            log("Reconfiguring proxy on connection");
5055                            updateLinkProperties();
5056                        }
5057                    }
5058
5059                    if (result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID) {
5060                        replyToMessage(message, WifiManager.SAVE_NETWORK_SUCCEEDED);
5061                        if (VDBG) {
5062                            loge("Success save network l2 nid="
5063                                    + Integer.toString(result.getNetworkId())
5064                                    + " autojoin " + mFrameworkAutoJoin.get());
5065                        }
5066                        if (mFrameworkAutoJoin.get()) {
5067                            /* Tell autojoin the user did try to modify and save that network */
5068                            mWifiAutoJoinController.updateConfigurationHistory(config.networkId,
5069                                    true, false);
5070                            mWifiAutoJoinController.attemptAutoJoin();
5071                        }
5072                    } else {
5073                        loge("Failed to save network");
5074                        replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED,
5075                                WifiManager.ERROR);
5076                    }
5077                    break;
5078                    /* Ignore */
5079                case WifiMonitor.NETWORK_CONNECTION_EVENT:
5080                    break;
5081                case CMD_RSSI_POLL:
5082                    if (message.arg1 == mRssiPollToken) {
5083                        WifiLinkLayerStats stats = null;
5084                        //try a reading L2 stats a couple of time, allow for a few failures
5085                        //in case the HAL/drivers are not completely initialized once we get there
5086                        if (mWifiLinkLayerStatsSupported > 0) {
5087                            stats = mWifiNative.getWifiLinkLayerStats();
5088                            if (stats == null && mWifiLinkLayerStatsSupported > 0) {
5089                                mWifiLinkLayerStatsSupported -= 1;
5090                            }
5091                        }
5092                        // Get Info and continue polling
5093                        fetchRssiLinkSpeedAndFrequencyNative();
5094                        calculateWifiScore(stats);
5095                        sendMessageDelayed(obtainMessage(CMD_RSSI_POLL,
5096                                mRssiPollToken, 0), POLL_RSSI_INTERVAL_MSECS);
5097                    } else {
5098                        // Polling has completed
5099                    }
5100                    break;
5101                case CMD_ENABLE_RSSI_POLL:
5102                    mEnableRssiPolling = (message.arg1 == 1);
5103                    mRssiPollToken++;
5104                    if (mEnableRssiPolling) {
5105                        // first poll
5106                        fetchRssiLinkSpeedAndFrequencyNative();
5107                        sendMessageDelayed(obtainMessage(CMD_RSSI_POLL,
5108                                mRssiPollToken, 0), POLL_RSSI_INTERVAL_MSECS);
5109                    }
5110                    break;
5111                case WifiManager.RSSI_PKTCNT_FETCH:
5112                    RssiPacketCountInfo info = new RssiPacketCountInfo();
5113                    fetchRssiLinkSpeedAndFrequencyNative();
5114                    info.rssi = mWifiInfo.getRssi();
5115                    fetchPktcntNative(info);
5116                    replyToMessage(message, WifiManager.RSSI_PKTCNT_FETCH_SUCCEEDED, info);
5117                    break;
5118                case CMD_UNWANTED_NETWORK:
5119                    //mWifiConfigStore.handleBadNetworkDisconnectReport(mLastNetworkId, mWifiInfo);
5120                    //disconnecting is probably too rough and reduce the chance we recover quickly.
5121                    //we should not have to disconnect, instead rely on network stack to send data
5122                    //traffic somewhere else but remember that this network is roamable with a
5123                    //low wifi score threshold
5124                    sendMessage(CMD_DISCONNECT);
5125                    break;
5126                default:
5127                    return NOT_HANDLED;
5128            }
5129
5130            return HANDLED;
5131        }
5132    }
5133
5134    class ObtainingIpState extends State {
5135        @Override
5136        public void enter() {
5137            if (DBG) {
5138                String key = "";
5139                if (getCurrentWifiConfiguration() != null) {
5140                    key = getCurrentWifiConfiguration().configKey();
5141                }
5142                log("enter ObtainingIpState netId=" + Integer.toString(mLastNetworkId)
5143                        + " " + key + " "
5144                        + " roam=" + mAutoRoaming
5145                        + " static=" + mWifiConfigStore.isUsingStaticIp(mLastNetworkId));
5146            }
5147
5148            if (!mWifiConfigStore.isUsingStaticIp(mLastNetworkId)) {
5149                // TODO: If we're switching between static IP configuration and DHCP, remove the
5150                // static configuration first.
5151                if (isRoaming()) {
5152                    renewDhcp();
5153                } else {
5154                    startDhcp();
5155                }
5156            } else {
5157                // stop any running dhcp before assigning static IP
5158                stopDhcp();
5159                DhcpResults dhcpResults = new DhcpResults(
5160                        mWifiConfigStore.getLinkProperties(mLastNetworkId));
5161                InterfaceConfiguration ifcg = new InterfaceConfiguration();
5162                Iterator<LinkAddress> addrs =
5163                        dhcpResults.linkProperties.getLinkAddresses().iterator();
5164                if (!addrs.hasNext()) {
5165                    loge("Static IP lacks address");
5166                    sendMessage(CMD_STATIC_IP_FAILURE);
5167                } else {
5168                    ifcg.setLinkAddress(addrs.next());
5169                    ifcg.setInterfaceUp();
5170                    try {
5171                        mNwService.setInterfaceConfig(mInterfaceName, ifcg);
5172                        if (DBG) log("Static IP configuration succeeded");
5173                        sendMessage(CMD_STATIC_IP_SUCCESS, dhcpResults);
5174                    } catch (RemoteException re) {
5175                        loge("Static IP configuration failed: " + re);
5176                        sendMessage(CMD_STATIC_IP_FAILURE);
5177                    } catch (IllegalStateException e) {
5178                        loge("Static IP configuration failed: " + e);
5179                        sendMessage(CMD_STATIC_IP_FAILURE);
5180                    }
5181                }
5182            }
5183        }
5184      @Override
5185      public boolean processMessage(Message message) {
5186          logStateAndMessage(message, getClass().getSimpleName());
5187
5188          switch(message.what) {
5189            case CMD_STATIC_IP_SUCCESS:
5190                  handleSuccessfulIpConfiguration((DhcpResults) message.obj);
5191                  transitionTo(mVerifyingLinkState);
5192                  break;
5193              case CMD_STATIC_IP_FAILURE:
5194                  handleFailedIpConfiguration();
5195                  transitionTo(mDisconnectingState);
5196                  break;
5197             case WifiManager.SAVE_NETWORK:
5198                  deferMessage(message);
5199                  break;
5200                  /* Defer any power mode changes since we must keep active power mode at DHCP */
5201              case CMD_SET_HIGH_PERF_MODE:
5202                  deferMessage(message);
5203                  break;
5204                  /* Defer scan request since we should not switch to other channels at DHCP */
5205              case CMD_START_SCAN:
5206                  deferMessage(message);
5207                  break;
5208              default:
5209                  return NOT_HANDLED;
5210          }
5211          return HANDLED;
5212      }
5213    }
5214
5215    class VerifyingLinkState extends State {
5216        @Override
5217        public void enter() {
5218            log(getName() + " enter");
5219            setNetworkDetailedState(DetailedState.VERIFYING_POOR_LINK);
5220            mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.VERIFYING_POOR_LINK);
5221            sendNetworkStateChangeBroadcast(mLastBssid);
5222            mAutoRoaming = WifiAutoJoinController.AUTO_JOIN_IDLE;
5223        }
5224        @Override
5225        public boolean processMessage(Message message) {
5226            logStateAndMessage(message, getClass().getSimpleName());
5227
5228            switch (message.what) {
5229                case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
5230                    //stay here
5231                    log(getName() + " POOR_LINK_DETECTED: no transition");
5232                    break;
5233                case WifiWatchdogStateMachine.GOOD_LINK_DETECTED:
5234                    log(getName() + " GOOD_LINK_DETECTED: transition to captive portal check");
5235                    // Send out a broadcast with the CAPTIVE_PORTAL_CHECK to preserve
5236                    // existing behaviour. The captive portal check really happens after we
5237                    // transition into DetailedState.CONNECTED.
5238                    setNetworkDetailedState(DetailedState.CAPTIVE_PORTAL_CHECK);
5239                    mWifiConfigStore.updateStatus(mLastNetworkId,
5240                            DetailedState.CAPTIVE_PORTAL_CHECK);
5241                    sendNetworkStateChangeBroadcast(mLastBssid);
5242
5243                    // NOTE: This might look like an odd place to enable IPV6 but this is in
5244                    // response to transitioning into GOOD_LINK_DETECTED. Similarly, we disable
5245                    // ipv6 when we transition into POOR_LINK_DETECTED in mConnectedState.
5246                    try {
5247                        mNwService.enableIpv6(mInterfaceName);
5248                    } catch (RemoteException re) {
5249                        loge("Failed to enable IPv6: " + re);
5250                    } catch (IllegalStateException e) {
5251                        loge("Failed to enable IPv6: " + e);
5252                    }
5253
5254                    log(getName() + " GOOD_LINK_DETECTED: transition to CONNECTED");
5255                    setNetworkDetailedState(DetailedState.CONNECTED);
5256                    mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.CONNECTED);
5257                    sendNetworkStateChangeBroadcast(mLastBssid);
5258                    transitionTo(mConnectedState);
5259
5260                    break;
5261                default:
5262                    if (DBG) log(getName() + " what=" + message.what + " NOT_HANDLED");
5263                    return NOT_HANDLED;
5264            }
5265            return HANDLED;
5266        }
5267    }
5268
5269    class RoamingState extends State {
5270        @Override
5271        public void enter() {
5272            if (DBG) {
5273                log("RoamingState Enter"
5274                        + " mScreenOn=" + mScreenOn );
5275            }
5276            setScanAlarm(false);
5277            //TODO: actually implement an alarm, but so as to disconnect if roaming fails
5278        }
5279        @Override
5280        public boolean processMessage(Message message) {
5281            logStateAndMessage(message, getClass().getSimpleName());
5282
5283            switch (message.what) {
5284               case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
5285                    if (DBG) log("Roaming and Watchdog reports poor link -> ignore");
5286                    return HANDLED;
5287               case CMD_UNWANTED_NETWORK:
5288                    if (DBG) log("Roaming and CS doesnt want the network -> ignore");
5289                    return HANDLED;
5290               case CMD_SET_OPERATIONAL_MODE:
5291                    if (message.arg1 != CONNECT_MODE) {
5292                        deferMessage(message);
5293                    }
5294                    break;
5295               case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
5296                    /* If we get a SUPPLICANT_STATE_CHANGE_EVENT before NETWORK_DISCONNECTION_EVENT
5297                     * we have missed the network disconnection, transition to mDisconnectedState
5298                     * and handle the rest of the events there
5299                     */
5300                    StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
5301                    setNetworkDetailedState(WifiInfo.getDetailedStateOf(stateChangeResult.state));
5302                    break;
5303               case WifiMonitor.NETWORK_CONNECTION_EVENT:
5304                   if (DBG) log("roaming and Network connection established");
5305                   mLastNetworkId = message.arg1;
5306                   mLastBssid = (String) message.obj;
5307
5308                   mWifiInfo.setBSSID(mLastBssid);
5309                   mWifiInfo.setNetworkId(mLastNetworkId);
5310                    /* send event to CM & network change broadcast */
5311                   setNetworkDetailedState(DetailedState.OBTAINING_IPADDR);
5312                   sendNetworkStateChangeBroadcast(mLastBssid);
5313                   transitionTo(mObtainingIpState);
5314                   break;
5315               case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
5316                   //throw away but only if it correspond to the network we're roaming to
5317                   String bssid = (String)message.obj;
5318                   if (bssid != null && bssid.equals(mTargetRoamBSSID)) {
5319                       handleNetworkDisconnect();
5320                   }
5321                   break;
5322                case WifiMonitor.SSID_TEMP_DISABLED:
5323                    //auith error while roaming
5324                    if (message.arg1 == mLastNetworkId) {
5325                        loge("DISABLED while roaming nid=" + Integer.toString(mLastNetworkId));
5326                        sendMessage(CMD_DISCONNECT);
5327                        transitionTo(mDisconnectingState);
5328                    }
5329                    return NOT_HANDLED;
5330                default:
5331                    return NOT_HANDLED;
5332            }
5333            return HANDLED;
5334        }
5335
5336        @Override
5337        public void exit() {
5338            loge("WifiStateMachine: Leaving Roaming state");
5339
5340            /* Request a CS wakelock during transition to mobile */
5341            //checkAndSetConnectivityInstance();
5342            //mCm.requestNetworkTransitionWakelock(getName());
5343        }
5344    }
5345
5346    class ConnectedState extends State {
5347        @Override
5348        public void enter() {
5349            String address;
5350            updateDefaultRouteMacAddress(1000);
5351            if (DBG) {
5352                log("ConnectedState Enter autojoin=" + mFrameworkAutoJoin.get()
5353                        + " mScreenOn=" + mScreenOn
5354                        + " scanperiod=" + Integer.toString(mConnectedScanPeriodMs) );
5355            }
5356            if (mFrameworkAutoJoin.get() && mScreenOn) {
5357                mCurrentScanAlarmMs = mConnectedScanPeriodMs;
5358                setScanAlarm(true);
5359            } else {
5360                mCurrentScanAlarmMs = 0;
5361            }
5362            registerConnected();
5363        }
5364        @Override
5365        public boolean processMessage(Message message) {
5366            logStateAndMessage(message, getClass().getSimpleName());
5367
5368            switch (message.what) {
5369                case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
5370                    if (DBG) log("Watchdog reports poor link");
5371                    try {
5372                        mNwService.disableIpv6(mInterfaceName);
5373                    } catch (RemoteException re) {
5374                        loge("Failed to disable IPv6: " + re);
5375                    } catch (IllegalStateException e) {
5376                        loge("Failed to disable IPv6: " + e);
5377                    }
5378                    /* Report a disconnect */
5379                    setNetworkDetailedState(DetailedState.DISCONNECTED);
5380                    mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.DISCONNECTED);
5381                    sendNetworkStateChangeBroadcast(mLastBssid);
5382
5383                    transitionTo(mVerifyingLinkState);
5384                    break;
5385                case CMD_UNWANTED_NETWORK:
5386                    mWifiConfigStore.handleBadNetworkDisconnectReport(mLastNetworkId, mWifiInfo);
5387                    //disconnecting is probably too rough and reduce the chance we recover quickly.
5388                    //we should not have to disconnect, instead rely on network stack to send data
5389                    //traffic somewhere else but remember that this network is roamable with a
5390                    //low wifi score threshold
5391                    sendMessage(CMD_DISCONNECT);
5392                    return HANDLED;
5393                case CMD_AUTO_ROAM:
5394                    /* this will happen similarly to an Auto_CONNECT, except we specify the BSSID */
5395                    /* Work Around: wpa_supplicant can get in a bad state where it returns a non
5396                     * associated status thus the STATUS command but somehow-someplace still thinks
5397                     * it is associated and thus will ignore select/reconnect command with
5398                     * following message:
5399                     * "Already associated with the selected network - do nothing"
5400                     *
5401                     * Hence, sends a disconnect to supplicant first.
5402                     */
5403                    //mWifiNative.disconnect();
5404
5405                    /* connect command coming from auto-join */
5406                    String bssid = (String) message.obj;
5407                    int netId = mLastNetworkId;
5408                    WifiConfiguration config = getCurrentWifiConfiguration();
5409
5410                    loge("CMD_AUTO_ROAM sup state "
5411                            + mSupplicantStateTracker.getSupplicantStateName()
5412                            + " my state " + getCurrentState().getName()
5413                            + " nid=" + Integer.toString(netId)
5414                            + " roam=" + Integer.toString(message.arg2)
5415                            + bssid);
5416                    if (config == null) {
5417                        loge("AUTO_ROAM and no config, bail out...");
5418                        break;
5419                    }
5420
5421                    /* save the BSSID so as to lock it @ firmware */
5422                    autoRoamSetBSSID(config, bssid);
5423
5424                    /* Save the network config */
5425                    loge("CMD_AUTO_ROAM will save config -> " + config.SSID
5426                                + " nid=" + Integer.toString(netId));
5427                    NetworkUpdateResult result = mWifiConfigStore.saveNetwork(config);
5428                    netId = result.getNetworkId();
5429                    loge("CMD_AUTO_ROAM did save config -> "
5430                                + " nid=" + Integer.toString(netId));
5431
5432                    if (mWifiConfigStore.selectNetwork(netId) &&
5433                            mWifiNative.reassociate()) {
5434
5435                        /* The state tracker handles enabling networks upon completion/failure */
5436                        mSupplicantStateTracker.sendMessage(WifiManager.CONNECT_NETWORK);
5437                        //replyToMessage(message, WifiManager.CONNECT_NETWORK_SUCCEEDED);
5438                        mAutoRoaming = message.arg2;
5439                        transitionTo(mRoamingState);
5440
5441                    } else {
5442                        loge("Failed to connect config: " + config + " netId: " + netId);
5443                        replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
5444                                WifiManager.ERROR);
5445                        break;
5446                    }
5447                    break;
5448                default:
5449                    return NOT_HANDLED;
5450            }
5451            return HANDLED;
5452        }
5453
5454        @Override
5455        public void exit() {
5456            loge("WifiStateMachine: Leaving Connected state");
5457            setScanAlarm(false);
5458
5459            /* Request a CS wakelock during transition to mobile */
5460            checkAndSetConnectivityInstance();
5461            mCm.requestNetworkTransitionWakelock(getName());
5462            loge("WifiStateMachine: Left Connected state");
5463
5464        }
5465    }
5466
5467    class DisconnectingState extends State {
5468
5469        @Override
5470        public void enter() {
5471            if (mFrameworkAutoJoin.get()) {
5472                mCurrentScanAlarmMs = mDisconnectedScanPeriodMs;
5473            } else {
5474
5475                mCurrentScanAlarmMs = mFrameworkScanIntervalMs;
5476            }
5477
5478            if (PDBG) {
5479                loge(" Enter DisconnectingState State scan interval " + mFrameworkScanIntervalMs
5480                        + " mEnableBackgroundScan= " + mEnableBackgroundScan
5481                        + " screenOn=" + mScreenOn);
5482            }
5483            if (mScreenOn)
5484                setScanAlarm(true);
5485        }
5486
5487        @Override
5488        public boolean processMessage(Message message) {
5489            logStateAndMessage(message, getClass().getSimpleName());
5490            switch (message.what) {
5491                case CMD_SET_OPERATIONAL_MODE:
5492                    if (message.arg1 != CONNECT_MODE) {
5493                        deferMessage(message);
5494                    }
5495                    break;
5496                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
5497                    /* If we get a SUPPLICANT_STATE_CHANGE_EVENT before NETWORK_DISCONNECTION_EVENT
5498                     * we have missed the network disconnection, transition to mDisconnectedState
5499                     * and handle the rest of the events there
5500                     */
5501                    deferMessage(message);
5502                    handleNetworkDisconnect();
5503                    transitionTo(mDisconnectedState);
5504                    break;
5505                default:
5506                    return NOT_HANDLED;
5507            }
5508            return HANDLED;
5509        }
5510
5511        @Override
5512        public void exit() {
5513            mCurrentScanAlarmMs = 0;
5514        }
5515    }
5516
5517    class DisconnectedState extends State {
5518        @Override
5519        public void enter() {
5520            // We dont scan frequently if this is a temporary disconnect
5521            // due to p2p
5522            if (mTemporarilyDisconnectWifi) {
5523                mWifiP2pChannel.sendMessage(WifiP2pServiceImpl.DISCONNECT_WIFI_RESPONSE);
5524                return;
5525            }
5526
5527            // loose the last selection choice
5528            // mWifiAutoJoinController.setLastSelectedConfiguration
5529            // (WifiConfiguration.INVALID_NETWORK_ID);
5530
5531            mFrameworkScanIntervalMs = Settings.Global.getLong(mContext.getContentResolver(),
5532                    Settings.Global.WIFI_FRAMEWORK_SCAN_INTERVAL_MS,
5533                    mDefaultFrameworkScanIntervalMs);
5534
5535            if (mFrameworkAutoJoin.get()) {
5536                if (mScreenOn)
5537                    mCurrentScanAlarmMs = mDisconnectedScanPeriodMs;
5538            } else {
5539                mCurrentScanAlarmMs = mFrameworkScanIntervalMs;
5540            }
5541
5542            if (PDBG) {
5543                loge(" Enter disconnected State scan interval " + mFrameworkScanIntervalMs
5544                        + " mEnableBackgroundScan= " + mEnableBackgroundScan
5545                        + " screenOn=" + mScreenOn);
5546            }
5547
5548            /** clear the roaming state, if we were roaming, we failed */
5549            mAutoRoaming = WifiAutoJoinController.AUTO_JOIN_IDLE;
5550
5551            /*
5552             * mFrameworkAutoJoin is False: We initiate background scanning if it is enabled,
5553             * otherwise we initiate an infrequent scan that wakes up the device to ensure
5554             * a user connects to an access point on the move
5555             *
5556             * mFrameworkAutoJoin is True:
5557             * - screen dark and PNO supported => scan alarm disabled
5558             * - everything else => scan alarm enabled with mDefaultFrameworkScanIntervalMs period
5559             */
5560            if ((mScreenOn == false) && mEnableBackgroundScan) { //mEnableBackgroundScan) {
5561                /* If a regular scan result is pending, do not initiate background
5562                 * scan until the scan results are returned. This is needed because
5563                 * initiating a background scan will cancel the regular scan and
5564                 * scan results will not be returned until background scanning is
5565                 * cleared
5566                 */
5567                if (!mIsScanOngoing) {
5568                    mWifiNative.enableBackgroundScan(true);
5569                }
5570            } else {
5571                setScanAlarm(true);
5572            }
5573
5574            if (mFrameworkAutoJoin.get() && mScreenOn) {
5575                startScanNative(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, null);
5576            }
5577
5578            /**
5579             * If we have no networks saved, the supplicant stops doing the periodic scan.
5580             * The scans are useful to notify the user of the presence of an open network.
5581             * Note that these are not wake up scans.
5582             */
5583            if (!mP2pConnected.get() && mWifiConfigStore.getConfiguredNetworks().size() == 0) {
5584                sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,
5585                        ++mPeriodicScanToken, 0), mSupplicantScanIntervalMs);
5586            }
5587        }
5588        @Override
5589        public boolean processMessage(Message message) {
5590            boolean ret = HANDLED;
5591
5592            logStateAndMessage(message, getClass().getSimpleName());
5593
5594            switch (message.what) {
5595                case CMD_NO_NETWORKS_PERIODIC_SCAN:
5596                    if (mP2pConnected.get()) break;
5597                    if (message.arg1 == mPeriodicScanToken &&
5598                            mWifiConfigStore.getConfiguredNetworks().size() == 0) {
5599                        startScan(UNKNOWN_SCAN_SOURCE, null, null);
5600                        sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,
5601                                    ++mPeriodicScanToken, 0), mSupplicantScanIntervalMs);
5602                    }
5603                    break;
5604                case WifiManager.FORGET_NETWORK:
5605                case CMD_REMOVE_NETWORK:
5606                    // Set up a delayed message here. After the forget/remove is handled
5607                    // the handled delayed message will determine if there is a need to
5608                    // scan and continue
5609                    sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,
5610                                ++mPeriodicScanToken, 0), mSupplicantScanIntervalMs);
5611                    ret = NOT_HANDLED;
5612                    break;
5613                case CMD_SET_OPERATIONAL_MODE:
5614                    if (message.arg1 != CONNECT_MODE) {
5615                        mOperationalMode = message.arg1;
5616
5617                        mWifiConfigStore.disableAllNetworks();
5618                        if (mOperationalMode == SCAN_ONLY_WITH_WIFI_OFF_MODE) {
5619                            mWifiP2pChannel.sendMessage(CMD_DISABLE_P2P_REQ);
5620                            setWifiState(WIFI_STATE_DISABLED);
5621                        }
5622
5623                        transitionTo(mScanModeState);
5624                    }
5625                    break;
5626                case CMD_ENABLE_BACKGROUND_SCAN:
5627                    mEnableBackgroundScan = (message.arg1 == 1);
5628                    loge("enableBackgroundScanCommand enabled=" + mEnableBackgroundScan
5629                            + " suppState:" + mSupplicantStateTracker.getSupplicantStateName());
5630
5631                    if (mEnableBackgroundScan) {
5632                        mWifiNative.enableBackgroundScan(true);
5633                        setScanAlarm(false);
5634                    } else {
5635                        if (mFrameworkAutoJoin.get()) {
5636                            // tell supplicant to disconnect so as it doesnt start scanning
5637                            // for connection upon disabling background scan
5638                            mWifiNative.disconnect();
5639                        }
5640                        mWifiNative.enableBackgroundScan(false);
5641                        setScanAlarm(true);
5642                    }
5643                    break;
5644                    /* Ignore network disconnect */
5645                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
5646                    break;
5647                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
5648                    StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
5649                    setNetworkDetailedState(WifiInfo.getDetailedStateOf(stateChangeResult.state));
5650                    /* ConnectModeState does the rest of the handling */
5651                    ret = NOT_HANDLED;
5652                    break;
5653                case CMD_START_SCAN:
5654                    /* Disable background scan temporarily during a regular scan */
5655                    if (mEnableBackgroundScan) {
5656                        mWifiNative.enableBackgroundScan(false);
5657                    }
5658                    /* Handled in parent state */
5659                    ret = NOT_HANDLED;
5660                    break;
5661                case WifiMonitor.SCAN_RESULTS_EVENT:
5662                    /* Re-enable background scan when a pending scan result is received */
5663                    if (mEnableBackgroundScan && mIsScanOngoing) {
5664                        mWifiNative.enableBackgroundScan(true);
5665                    }
5666                    /* Handled in parent state */
5667                    ret = NOT_HANDLED;
5668                    break;
5669                case WifiP2pServiceImpl.P2P_CONNECTION_CHANGED:
5670                    NetworkInfo info = (NetworkInfo) message.obj;
5671                    mP2pConnected.set(info.isConnected());
5672                    if (mP2pConnected.get()) {
5673                        int defaultInterval = mContext.getResources().getInteger(
5674                                R.integer.config_wifi_scan_interval_p2p_connected);
5675                        long scanIntervalMs = Settings.Global.getLong(mContext.getContentResolver(),
5676                                Settings.Global.WIFI_SCAN_INTERVAL_WHEN_P2P_CONNECTED_MS,
5677                                defaultInterval);
5678                        mWifiNative.setScanInterval((int) scanIntervalMs/1000);
5679                    } else if (mWifiConfigStore.getConfiguredNetworks().size() == 0) {
5680                        if (DBG) log("Turn on scanning after p2p disconnected");
5681                        sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,
5682                                    ++mPeriodicScanToken, 0), mSupplicantScanIntervalMs);
5683                    }
5684                case CMD_RECONNECT:
5685                case CMD_REASSOCIATE:
5686                    if (mTemporarilyDisconnectWifi) {
5687                        // Drop a third party reconnect/reassociate if STA is
5688                        // temporarily disconnected for p2p
5689                        break;
5690                    } else {
5691                        // ConnectModeState handles it
5692                        ret = NOT_HANDLED;
5693                    }
5694                    break;
5695                default:
5696                    ret = NOT_HANDLED;
5697            }
5698            return ret;
5699        }
5700
5701        @Override
5702        public void exit() {
5703            /* No need for a background scan upon exit from a disconnected state */
5704            if (mEnableBackgroundScan) {
5705                mWifiNative.enableBackgroundScan(false);
5706            }
5707            mCurrentScanAlarmMs = 0;
5708            setScanAlarm(false);
5709        }
5710    }
5711
5712    class WpsRunningState extends State {
5713        //Tracks the source to provide a reply
5714        private Message mSourceMessage;
5715        @Override
5716        public void enter() {
5717            mSourceMessage = Message.obtain(getCurrentMessage());
5718        }
5719        @Override
5720        public boolean processMessage(Message message) {
5721            logStateAndMessage(message, getClass().getSimpleName());
5722
5723            switch (message.what) {
5724                case WifiMonitor.WPS_SUCCESS_EVENT:
5725                    // Ignore intermediate success, wait for full connection
5726                    break;
5727                case WifiMonitor.NETWORK_CONNECTION_EVENT:
5728                    replyToMessage(mSourceMessage, WifiManager.WPS_COMPLETED);
5729                    mSourceMessage.recycle();
5730                    mSourceMessage = null;
5731                    deferMessage(message);
5732                    transitionTo(mDisconnectedState);
5733                    break;
5734                case WifiMonitor.WPS_OVERLAP_EVENT:
5735                    replyToMessage(mSourceMessage, WifiManager.WPS_FAILED,
5736                            WifiManager.WPS_OVERLAP_ERROR);
5737                    mSourceMessage.recycle();
5738                    mSourceMessage = null;
5739                    transitionTo(mDisconnectedState);
5740                    break;
5741                case WifiMonitor.WPS_FAIL_EVENT:
5742                    //arg1 has the reason for the failure
5743                    if ((message.arg1 != WifiManager.ERROR) || (message.arg2 != 0)) {
5744                        replyToMessage(mSourceMessage, WifiManager.WPS_FAILED, message.arg1);
5745                        mSourceMessage.recycle();
5746                        mSourceMessage = null;
5747                        transitionTo(mDisconnectedState);
5748                    } else {
5749                        if (DBG) log("Ignore unspecified fail event during WPS connection");
5750                    }
5751                    break;
5752                case WifiMonitor.WPS_TIMEOUT_EVENT:
5753                    replyToMessage(mSourceMessage, WifiManager.WPS_FAILED,
5754                            WifiManager.WPS_TIMED_OUT);
5755                    mSourceMessage.recycle();
5756                    mSourceMessage = null;
5757                    transitionTo(mDisconnectedState);
5758                    break;
5759                case WifiManager.START_WPS:
5760                    replyToMessage(message, WifiManager.WPS_FAILED, WifiManager.IN_PROGRESS);
5761                    break;
5762                case WifiManager.CANCEL_WPS:
5763                    if (mWifiNative.cancelWps()) {
5764                        replyToMessage(message, WifiManager.CANCEL_WPS_SUCCEDED);
5765                    } else {
5766                        replyToMessage(message, WifiManager.CANCEL_WPS_FAILED, WifiManager.ERROR);
5767                    }
5768                    transitionTo(mDisconnectedState);
5769                    break;
5770                /* Defer all commands that can cause connections to a different network
5771                 * or put the state machine out of connect mode
5772                 */
5773                case CMD_STOP_DRIVER:
5774                case CMD_SET_OPERATIONAL_MODE:
5775                case WifiManager.CONNECT_NETWORK:
5776                case CMD_ENABLE_NETWORK:
5777                case CMD_RECONNECT:
5778                case CMD_REASSOCIATE:
5779                    deferMessage(message);
5780                    break;
5781                case CMD_AUTO_CONNECT:
5782                    if (DBG) log("Ignore auto connect command while WpsRunningState");
5783                    break;
5784                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
5785                    if (DBG) log("Network connection lost");
5786                    handleNetworkDisconnect();
5787                    break;
5788                case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
5789                    if (DBG) log("Ignore Assoc reject event during WPS Connection");
5790                    break;
5791                case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
5792                    // Disregard auth failure events during WPS connection. The
5793                    // EAP sequence is retried several times, and there might be
5794                    // failures (especially for wps pin). We will get a WPS_XXX
5795                    // event at the end of the sequence anyway.
5796                    if (DBG) log("Ignore auth failure during WPS connection");
5797                    break;
5798                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
5799                    //Throw away supplicant state changes when WPS is running.
5800                    //We will start getting supplicant state changes once we get
5801                    //a WPS success or failure
5802                    break;
5803                default:
5804                    return NOT_HANDLED;
5805            }
5806            return HANDLED;
5807        }
5808
5809        @Override
5810        public void exit() {
5811            mWifiConfigStore.enableAllNetworks();
5812            mWifiConfigStore.loadConfiguredNetworks();
5813        }
5814    }
5815
5816    class SoftApStartingState extends State {
5817        @Override
5818        public void enter() {
5819            final Message message = getCurrentMessage();
5820            if (message.what == CMD_START_AP) {
5821                final WifiConfiguration config = (WifiConfiguration) message.obj;
5822
5823                if (config == null) {
5824                    mWifiApConfigChannel.sendMessage(CMD_REQUEST_AP_CONFIG);
5825                } else {
5826                    mWifiApConfigChannel.sendMessage(CMD_SET_AP_CONFIG, config);
5827                    startSoftApWithConfig(config);
5828                }
5829            } else {
5830                throw new RuntimeException("Illegal transition to SoftApStartingState: " + message);
5831            }
5832        }
5833        @Override
5834        public boolean processMessage(Message message) {
5835            logStateAndMessage(message, getClass().getSimpleName());
5836
5837            switch(message.what) {
5838                case CMD_START_SUPPLICANT:
5839                case CMD_STOP_SUPPLICANT:
5840                case CMD_START_AP:
5841                case CMD_STOP_AP:
5842                case CMD_START_DRIVER:
5843                case CMD_STOP_DRIVER:
5844                case CMD_SET_OPERATIONAL_MODE:
5845                case CMD_SET_COUNTRY_CODE:
5846                case CMD_SET_FREQUENCY_BAND:
5847                case CMD_START_PACKET_FILTERING:
5848                case CMD_STOP_PACKET_FILTERING:
5849                case CMD_TETHER_STATE_CHANGE:
5850                    deferMessage(message);
5851                    break;
5852                case WifiStateMachine.CMD_RESPONSE_AP_CONFIG:
5853                    WifiConfiguration config = (WifiConfiguration) message.obj;
5854                    if (config != null) {
5855                        startSoftApWithConfig(config);
5856                    } else {
5857                        loge("Softap config is null!");
5858                        sendMessage(CMD_START_AP_FAILURE);
5859                    }
5860                    break;
5861                case CMD_START_AP_SUCCESS:
5862                    setWifiApState(WIFI_AP_STATE_ENABLED);
5863                    transitionTo(mSoftApStartedState);
5864                    break;
5865                case CMD_START_AP_FAILURE:
5866                    setWifiApState(WIFI_AP_STATE_FAILED);
5867                    transitionTo(mInitialState);
5868                    break;
5869                default:
5870                    return NOT_HANDLED;
5871            }
5872            return HANDLED;
5873        }
5874    }
5875
5876    class SoftApStartedState extends State {
5877        @Override
5878        public boolean processMessage(Message message) {
5879            logStateAndMessage(message, getClass().getSimpleName());
5880
5881            switch(message.what) {
5882                case CMD_STOP_AP:
5883                    if (DBG) log("Stopping Soft AP");
5884                    /* We have not tethered at this point, so we just shutdown soft Ap */
5885                    try {
5886                        mNwService.stopAccessPoint(mInterfaceName);
5887                    } catch(Exception e) {
5888                        loge("Exception in stopAccessPoint()");
5889                    }
5890                    setWifiApState(WIFI_AP_STATE_DISABLED);
5891                    transitionTo(mInitialState);
5892                    break;
5893                case CMD_START_AP:
5894                    // Ignore a start on a running access point
5895                    break;
5896                    /* Fail client mode operation when soft AP is enabled */
5897                case CMD_START_SUPPLICANT:
5898                    loge("Cannot start supplicant with a running soft AP");
5899                    setWifiState(WIFI_STATE_UNKNOWN);
5900                    break;
5901                case CMD_TETHER_STATE_CHANGE:
5902                    TetherStateChange stateChange = (TetherStateChange) message.obj;
5903                    if (startTethering(stateChange.available)) {
5904                        transitionTo(mTetheringState);
5905                    }
5906                    break;
5907                default:
5908                    return NOT_HANDLED;
5909            }
5910            return HANDLED;
5911        }
5912    }
5913
5914    class TetheringState extends State {
5915        @Override
5916        public void enter() {
5917            /* Send ourselves a delayed message to shut down if tethering fails to notify */
5918            sendMessageDelayed(obtainMessage(CMD_TETHER_NOTIFICATION_TIMED_OUT,
5919                    ++mTetherToken, 0), TETHER_NOTIFICATION_TIME_OUT_MSECS);
5920        }
5921        @Override
5922        public boolean processMessage(Message message) {
5923            logStateAndMessage(message, getClass().getSimpleName());
5924
5925            switch(message.what) {
5926                case CMD_TETHER_STATE_CHANGE:
5927                    TetherStateChange stateChange = (TetherStateChange) message.obj;
5928                    if (isWifiTethered(stateChange.active)) {
5929                        transitionTo(mTetheredState);
5930                    }
5931                    return HANDLED;
5932                case CMD_TETHER_NOTIFICATION_TIMED_OUT:
5933                    if (message.arg1 == mTetherToken) {
5934                        loge("Failed to get tether update, shutdown soft access point");
5935                        transitionTo(mSoftApStartedState);
5936                        // Needs to be first thing handled
5937                        sendMessageAtFrontOfQueue(CMD_STOP_AP);
5938                    }
5939                    break;
5940                case CMD_START_SUPPLICANT:
5941                case CMD_STOP_SUPPLICANT:
5942                case CMD_START_AP:
5943                case CMD_STOP_AP:
5944                case CMD_START_DRIVER:
5945                case CMD_STOP_DRIVER:
5946                case CMD_SET_OPERATIONAL_MODE:
5947                case CMD_SET_COUNTRY_CODE:
5948                case CMD_SET_FREQUENCY_BAND:
5949                case CMD_START_PACKET_FILTERING:
5950                case CMD_STOP_PACKET_FILTERING:
5951                    deferMessage(message);
5952                    break;
5953                default:
5954                    return NOT_HANDLED;
5955            }
5956            return HANDLED;
5957        }
5958    }
5959
5960    class TetheredState extends State {
5961        @Override
5962        public boolean processMessage(Message message) {
5963            logStateAndMessage(message, getClass().getSimpleName());
5964
5965            switch(message.what) {
5966                case CMD_TETHER_STATE_CHANGE:
5967                    TetherStateChange stateChange = (TetherStateChange) message.obj;
5968                    if (!isWifiTethered(stateChange.active)) {
5969                        loge("Tethering reports wifi as untethered!, shut down soft Ap");
5970                        setHostApRunning(null, false);
5971                        setHostApRunning(null, true);
5972                    }
5973                    return HANDLED;
5974                case CMD_STOP_AP:
5975                    if (DBG) log("Untethering before stopping AP");
5976                    setWifiApState(WIFI_AP_STATE_DISABLING);
5977                    stopTethering();
5978                    transitionTo(mUntetheringState);
5979                    // More work to do after untethering
5980                    deferMessage(message);
5981                    break;
5982                default:
5983                    return NOT_HANDLED;
5984            }
5985            return HANDLED;
5986        }
5987    }
5988
5989    class UntetheringState extends State {
5990        @Override
5991        public void enter() {
5992            /* Send ourselves a delayed message to shut down if tethering fails to notify */
5993            sendMessageDelayed(obtainMessage(CMD_TETHER_NOTIFICATION_TIMED_OUT,
5994                    ++mTetherToken, 0), TETHER_NOTIFICATION_TIME_OUT_MSECS);
5995
5996        }
5997        @Override
5998        public boolean processMessage(Message message) {
5999            logStateAndMessage(message, getClass().getSimpleName());
6000
6001            switch(message.what) {
6002                case CMD_TETHER_STATE_CHANGE:
6003                    TetherStateChange stateChange = (TetherStateChange) message.obj;
6004
6005                    /* Wait till wifi is untethered */
6006                    if (isWifiTethered(stateChange.active)) break;
6007
6008                    transitionTo(mSoftApStartedState);
6009                    break;
6010                case CMD_TETHER_NOTIFICATION_TIMED_OUT:
6011                    if (message.arg1 == mTetherToken) {
6012                        loge("Failed to get tether update, force stop access point");
6013                        transitionTo(mSoftApStartedState);
6014                    }
6015                    break;
6016                case CMD_START_SUPPLICANT:
6017                case CMD_STOP_SUPPLICANT:
6018                case CMD_START_AP:
6019                case CMD_STOP_AP:
6020                case CMD_START_DRIVER:
6021                case CMD_STOP_DRIVER:
6022                case CMD_SET_OPERATIONAL_MODE:
6023                case CMD_SET_COUNTRY_CODE:
6024                case CMD_SET_FREQUENCY_BAND:
6025                case CMD_START_PACKET_FILTERING:
6026                case CMD_STOP_PACKET_FILTERING:
6027                    deferMessage(message);
6028                    break;
6029                default:
6030                    return NOT_HANDLED;
6031            }
6032            return HANDLED;
6033        }
6034    }
6035
6036    //State machine initiated requests can have replyTo set to null indicating
6037    //there are no recepients, we ignore those reply actions
6038    private void replyToMessage(Message msg, int what) {
6039        if (msg.replyTo == null) return;
6040        Message dstMsg = obtainMessageWithArg2(msg);
6041        dstMsg.what = what;
6042        mReplyChannel.replyToMessage(msg, dstMsg);
6043    }
6044
6045    private void replyToMessage(Message msg, int what, int arg1) {
6046        if (msg.replyTo == null) return;
6047        Message dstMsg = obtainMessageWithArg2(msg);
6048        dstMsg.what = what;
6049        dstMsg.arg1 = arg1;
6050        mReplyChannel.replyToMessage(msg, dstMsg);
6051    }
6052
6053    private void replyToMessage(Message msg, int what, Object obj) {
6054        if (msg.replyTo == null) return;
6055        Message dstMsg = obtainMessageWithArg2(msg);
6056        dstMsg.what = what;
6057        dstMsg.obj = obj;
6058        mReplyChannel.replyToMessage(msg, dstMsg);
6059    }
6060
6061    /**
6062     * arg2 on the source message has a unique id that needs to be retained in replies
6063     * to match the request
6064
6065     * see WifiManager for details
6066     */
6067    private Message obtainMessageWithArg2(Message srcMsg) {
6068        Message msg = Message.obtain();
6069        msg.arg2 = srcMsg.arg2;
6070        return msg;
6071    }
6072}
6073