WifiStateMachine.java revision 85f2d77b1b858c063471951f3a5f93c96bd5e72b
1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.wifi;
18
19import static android.net.wifi.WifiManager.WIFI_STATE_DISABLED;
20import static android.net.wifi.WifiManager.WIFI_STATE_DISABLING;
21import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED;
22import static android.net.wifi.WifiManager.WIFI_STATE_ENABLING;
23import static android.net.wifi.WifiManager.WIFI_STATE_UNKNOWN;
24
25/**
26 * TODO:
27 * Deprecate WIFI_STATE_UNKNOWN
28 */
29import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED;
30import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLING;
31import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
32import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLING;
33import static android.net.wifi.WifiManager.WIFI_AP_STATE_FAILED;
34
35import android.app.AlarmManager;
36import android.app.PendingIntent;
37import android.app.backup.IBackupManager;
38import android.bluetooth.BluetoothAdapter;
39import android.content.BroadcastReceiver;
40import android.content.Context;
41import android.content.Intent;
42import android.content.IntentFilter;
43import android.content.pm.PackageManager;
44import android.database.ContentObserver;
45import android.net.ConnectivityManager;
46import android.net.DhcpResults;
47import android.net.DhcpStateMachine;
48import android.net.InterfaceConfiguration;
49import android.net.LinkAddress;
50import android.net.LinkProperties;
51import android.net.NetworkAgent;
52import android.net.NetworkCapabilities;
53import android.net.NetworkFactory;
54import android.net.NetworkInfo;
55import android.net.NetworkInfo.DetailedState;
56import android.net.NetworkUtils;
57import android.net.RouteInfo;
58import android.net.StaticIpConfiguration;
59import android.net.TrafficStats;
60import android.net.wifi.*;
61import android.net.wifi.SupplicantState;
62import android.net.wifi.WpsResult.Status;
63import android.net.wifi.p2p.IWifiP2pManager;
64import android.os.BatteryStats;
65import android.os.Bundle;
66import android.os.IBinder;
67import android.os.INetworkManagementService;
68import android.os.Looper;
69import android.os.Message;
70import android.os.Messenger;
71import android.os.PowerManager;
72import android.os.Process;
73import android.os.RemoteException;
74import android.os.ServiceManager;
75import android.os.SystemClock;
76import android.os.SystemProperties;
77import android.os.UserHandle;
78import android.os.WorkSource;
79import android.provider.Settings;
80import android.telephony.TelephonyManager;
81import android.util.LruCache;
82import android.text.TextUtils;
83import android.util.Log;
84
85import com.android.internal.R;
86import com.android.internal.app.IBatteryStats;
87import com.android.internal.util.AsyncChannel;
88import com.android.internal.util.Protocol;
89import com.android.internal.util.State;
90import com.android.internal.util.StateMachine;
91import com.android.server.net.BaseNetworkObserver;
92import com.android.server.net.NetlinkTracker;
93
94import com.android.server.wifi.p2p.WifiP2pServiceImpl;
95
96import java.io.FileDescriptor;
97import java.io.PrintWriter;
98import java.net.InetAddress;
99import java.util.*;
100import java.util.concurrent.atomic.AtomicInteger;
101import java.util.concurrent.atomic.AtomicBoolean;
102import java.util.regex.Pattern;
103import java.io.FileReader;
104import java.io.BufferedReader;
105import java.io.FileNotFoundException;
106import java.io.IOException;
107import java.net.Inet4Address;
108
109/**
110 * Track the state of Wifi connectivity. All event handling is done here,
111 * and all changes in connectivity state are initiated here.
112 *
113 * Wi-Fi now supports three modes of operation: Client, SoftAp and p2p
114 * In the current implementation, we support concurrent wifi p2p and wifi operation.
115 * The WifiStateMachine handles SoftAp and Client operations while WifiP2pService
116 * handles p2p operation.
117 *
118 * @hide
119 */
120public class WifiStateMachine extends StateMachine {
121
122    private static final String NETWORKTYPE = "WIFI";
123    private static boolean DBG = false;
124    private static boolean VDBG = false;
125    private static boolean VVDBG = false;
126    private static boolean mLogMessages = false;
127
128    private static final int ONE_HOUR_MILLI = 1000 * 60 * 60;
129
130    private static final String GOOGLE_OUI = "DA-A1-19";
131
132    /* temporary debug flag - best network selection development */
133    private static boolean PDBG = false;
134
135    /* debug flag, indicating if handling of ASSOCIATION_REJECT ended up blacklisting
136     * the corresponding BSSID.
137     */
138    private boolean didBlackListBSSID = false;
139
140    /**
141     * Log with error attribute
142     *
143     * @param s is string log
144     */
145    protected void loge(String s) {
146        Log.e(getName(), s);
147    }
148    protected void log(String s) {;
149        Log.e(getName(), s);
150    }
151
152    private WifiMonitor mWifiMonitor;
153    private WifiNative mWifiNative;
154    private WifiConfigStore mWifiConfigStore;
155    private WifiAutoJoinController mWifiAutoJoinController;
156    private INetworkManagementService mNwService;
157    private ConnectivityManager mCm;
158
159    private final boolean mP2pSupported;
160    private final AtomicBoolean mP2pConnected = new AtomicBoolean(false);
161    private boolean mTemporarilyDisconnectWifi = false;
162    private final String mPrimaryDeviceType;
163
164    /* Scan results handling */
165    private List<ScanResult> mScanResults = new ArrayList<ScanResult>();
166    private static final Pattern scanResultPattern = Pattern.compile("\t+");
167    private static final int SCAN_RESULT_CACHE_SIZE = 160;
168    private final LruCache<String, ScanResult> mScanResultCache;
169    // For debug, number of known scan results that were found as part of last scan result event,
170    // as well the number of scans results returned by the supplicant with that message
171    private int mNumScanResultsKnown;
172    private int mNumScanResultsReturned;
173
174    /* Batch scan results */
175    private final List<BatchedScanResult> mBatchedScanResults =
176            new ArrayList<BatchedScanResult>();
177    private int mBatchedScanOwnerUid = UNKNOWN_SCAN_SOURCE;
178    private int mExpectedBatchedScans = 0;
179    private long mBatchedScanMinPollTime = 0;
180
181    private boolean mScreenOn = false;
182
183    /* Chipset supports background scan */
184    private final boolean mBackgroundScanSupported;
185
186    private String mInterfaceName;
187    /* Tethering interface could be separate from wlan interface */
188    private String mTetherInterfaceName;
189
190    private int mLastSignalLevel = -1;
191    private String mLastBssid;
192    private int mLastNetworkId; // The network Id we successfully joined
193    private boolean linkDebouncing = false;
194
195    // Testing various network disconnect cases by sending lots of spurious
196    // disconnect to supplicant
197    private boolean testNetworkDisconnect = false;
198
199    private boolean mEnableRssiPolling = false;
200    private boolean mEnableBackgroundScan = false;
201    private int mRssiPollToken = 0;
202    /* 3 operational states for STA operation: CONNECT_MODE, SCAN_ONLY_MODE, SCAN_ONLY_WIFI_OFF_MODE
203    * In CONNECT_MODE, the STA can scan and connect to an access point
204    * In SCAN_ONLY_MODE, the STA can only scan for access points
205    * In SCAN_ONLY_WIFI_OFF_MODE, the STA can only scan for access points with wifi toggle being off
206    */
207    private int mOperationalMode = CONNECT_MODE;
208    private boolean mIsScanOngoing = false;
209    private boolean mIsFullScanOngoing = false;
210    private final Queue<Message> mBufferedScanMsg = new LinkedList<Message>();
211    private WorkSource mScanWorkSource = null;
212    private static final int UNKNOWN_SCAN_SOURCE = -1;
213    private static final int SCAN_ALARM_SOURCE = -2;
214    private static final int ADD_OR_UPDATE_SOURCE = -3;
215
216    private static final int SCAN_REQUEST_BUFFER_MAX_SIZE = 10;
217    private static final String CUSTOMIZED_SCAN_SETTING = "customized_scan_settings";
218    private static final String CUSTOMIZED_SCAN_WORKSOURCE = "customized_scan_worksource";
219    private static final String SCAN_REQUEST_TIME = "scan_request_time";
220
221    private static final String BATCHED_SETTING = "batched_settings";
222    private static final String BATCHED_WORKSOURCE = "batched_worksource";
223
224    /* Tracks if state machine has received any screen state change broadcast yet.
225     * We can miss one of these at boot.
226     */
227    private AtomicBoolean mScreenBroadcastReceived = new AtomicBoolean(false);
228
229    private boolean mBluetoothConnectionActive = false;
230
231    private PowerManager.WakeLock mSuspendWakeLock;
232
233    /**
234     * Interval in milliseconds between polling for RSSI
235     * and linkspeed information
236     */
237    private static final int POLL_RSSI_INTERVAL_MSECS = 3000;
238
239    /**
240     * Interval in milliseconds between receiving a disconnect event
241     * while connected to a good AP, and handling the disconnect proper
242     */
243    private static final int LINK_FLAPPING_DEBOUNCE_MSEC = 7000;
244
245    /**
246     * Delay between supplicant restarts upon failure to establish connection
247     */
248    private static final int SUPPLICANT_RESTART_INTERVAL_MSECS = 5000;
249
250    /**
251     * Number of times we attempt to restart supplicant
252     */
253    private static final int SUPPLICANT_RESTART_TRIES = 5;
254
255    private int mSupplicantRestartCount = 0;
256    /* Tracks sequence number on stop failure message */
257    private int mSupplicantStopFailureToken = 0;
258
259    /**
260     * Tether state change notification time out
261     */
262    private static final int TETHER_NOTIFICATION_TIME_OUT_MSECS = 5000;
263
264    /* Tracks sequence number on a tether notification time out */
265    private int mTetherToken = 0;
266
267    /**
268     * Driver start time out.
269     */
270    private static final int DRIVER_START_TIME_OUT_MSECS = 10000;
271
272    /* Tracks sequence number on a driver time out */
273    private int mDriverStartToken = 0;
274
275    /**
276     * The link properties of the wifi interface.
277     * Do not modify this directly; use updateLinkProperties instead.
278     */
279    private LinkProperties mLinkProperties;
280
281    /* Tracks sequence number on a periodic scan message */
282    private int mPeriodicScanToken = 0;
283
284    // Wakelock held during wifi start/stop and driver load/unload
285    private PowerManager.WakeLock mWakeLock;
286
287    private Context mContext;
288
289    private final Object mDhcpResultsLock = new Object();
290    private DhcpResults mDhcpResults;
291    private WifiInfo mWifiInfo;
292    private NetworkInfo mNetworkInfo;
293    private NetworkCapabilities mNetworkCapabilities;
294    private SupplicantStateTracker mSupplicantStateTracker;
295    private DhcpStateMachine mDhcpStateMachine;
296    private boolean mDhcpActive = false;
297
298    private int mWifiLinkLayerStatsSupported = 4; // Temporary disable
299
300    private final AtomicInteger mCountryCodeSequence = new AtomicInteger();
301
302    // Whether the state machine goes thru the Disconnecting->Disconnected->ObtainingIpAddress
303    private int mAutoRoaming = WifiAutoJoinController.AUTO_JOIN_IDLE;
304
305    // Roaming failure count
306    private int mRoamFailCount = 0;
307
308    // This is the BSSID we are trying to associate to, it can be set to "any"
309    // if we havent selected a BSSID for joining.
310    // The BSSID we are associated to is found in mWifiInfo
311    private String mTargetRoamBSSID = "any";
312
313    private long mLastDriverRoamAttempt = 0;
314
315    private WifiConfiguration targetWificonfiguration = null;
316
317    // Used as debug to indicate which configuration last was saved
318    private WifiConfiguration lastSavedConfigurationAttempt = null;
319
320    // Used as debug to indicate which configuration last was removed
321    private WifiConfiguration lastForgetConfigurationAttempt = null;
322
323    boolean isRoaming() {
324        return mAutoRoaming == WifiAutoJoinController.AUTO_JOIN_ROAMING
325                || mAutoRoaming == WifiAutoJoinController.AUTO_JOIN_EXTENDED_ROAMING;
326    }
327
328    public void autoRoamSetBSSID(int netId, String bssid) {
329        autoRoamSetBSSID(mWifiConfigStore.getWifiConfiguration(netId), bssid);
330    }
331
332    public boolean autoRoamSetBSSID(WifiConfiguration config, String bssid) {
333        boolean ret = true;
334        if (mTargetRoamBSSID == null) mTargetRoamBSSID = "any";
335        if (bssid == null) bssid = "any";
336        if (config == null) return false; // Nothing to do
337
338        if (mTargetRoamBSSID != null && bssid == mTargetRoamBSSID && bssid == config.BSSID) {
339            return false; // We didnt change anything
340        }
341        if (!mTargetRoamBSSID.equals("any") && bssid.equals("any")) {
342            // Changing to ANY
343            if (!mWifiConfigStore.roamOnAny) {
344                ret =  false; // Nothing to do
345            }
346        }
347        if (VDBG) {
348           loge("autoRoamSetBSSID " + bssid
349                   + " key=" + config.configKey());
350        }
351        config.autoJoinBSSID = bssid;
352        mTargetRoamBSSID = bssid;
353        mWifiConfigStore.saveWifiConfigBSSID(config);
354        return ret;
355    }
356
357    /**
358     * Subset of link properties coming from netlink.
359     * Currently includes IPv4 and IPv6 addresses. In the future will also include IPv6 DNS servers
360     * and domains obtained from router advertisements (RFC 6106).
361     */
362    private NetlinkTracker mNetlinkTracker;
363
364    private AlarmManager mAlarmManager;
365    private PendingIntent mScanIntent;
366    private PendingIntent mDriverStopIntent;
367    private PendingIntent mBatchedScanIntervalIntent;
368
369    /* Tracks current frequency mode */
370    private AtomicInteger mFrequencyBand = new AtomicInteger(WifiManager.WIFI_FREQUENCY_BAND_AUTO);
371
372    /* Tracks if we are filtering Multicast v4 packets. Default is to filter. */
373    private AtomicBoolean mFilteringMulticastV4Packets = new AtomicBoolean(true);
374
375    // Channel for sending replies.
376    private AsyncChannel mReplyChannel = new AsyncChannel();
377
378    private WifiP2pServiceImpl mWifiP2pServiceImpl;
379
380    // Used to initiate a connection with WifiP2pService
381    private AsyncChannel mWifiP2pChannel;
382    private AsyncChannel mWifiApConfigChannel;
383
384    private WifiNetworkFactory mNetworkFactory;
385    private WifiNetworkAgent mNetworkAgent;
386
387    // Keep track of various statistics, for retrieval by System Apps, i.e. under @SystemApi
388    // We should really persist that into the networkHistory.txt file, and read it back when
389    // WifiStateMachine starts up
390    private WifiConnectionStatistics mWifiConnectionStatistics = new WifiConnectionStatistics();
391
392    // Used to filter out requests we couldn't possibly satisfy.
393    private final NetworkCapabilities mNetworkCapabilitiesFilter = new NetworkCapabilities();
394
395    /* The base for wifi message types */
396    static final int BASE = Protocol.BASE_WIFI;
397    /* Start the supplicant */
398    static final int CMD_START_SUPPLICANT                 = BASE + 11;
399    /* Stop the supplicant */
400    static final int CMD_STOP_SUPPLICANT                  = BASE + 12;
401    /* Start the driver */
402    static final int CMD_START_DRIVER                     = BASE + 13;
403    /* Stop the driver */
404    static final int CMD_STOP_DRIVER                      = BASE + 14;
405    /* Indicates Static IP succeeded */
406    static final int CMD_STATIC_IP_SUCCESS                = BASE + 15;
407    /* Indicates Static IP failed */
408    static final int CMD_STATIC_IP_FAILURE                = BASE + 16;
409    /* Indicates supplicant stop failed */
410    static final int CMD_STOP_SUPPLICANT_FAILED           = BASE + 17;
411    /* Delayed stop to avoid shutting down driver too quick*/
412    static final int CMD_DELAYED_STOP_DRIVER              = BASE + 18;
413    /* A delayed message sent to start driver when it fail to come up */
414    static final int CMD_DRIVER_START_TIMED_OUT           = BASE + 19;
415
416    /* Start the soft access point */
417    static final int CMD_START_AP                         = BASE + 21;
418    /* Indicates soft ap start succeeded */
419    static final int CMD_START_AP_SUCCESS                 = BASE + 22;
420    /* Indicates soft ap start failed */
421    static final int CMD_START_AP_FAILURE                 = BASE + 23;
422    /* Stop the soft access point */
423    static final int CMD_STOP_AP                          = BASE + 24;
424    /* Set the soft access point configuration */
425    static final int CMD_SET_AP_CONFIG                    = BASE + 25;
426    /* Soft access point configuration set completed */
427    static final int CMD_SET_AP_CONFIG_COMPLETED          = BASE + 26;
428    /* Request the soft access point configuration */
429    static final int CMD_REQUEST_AP_CONFIG                = BASE + 27;
430    /* Response to access point configuration request */
431    static final int CMD_RESPONSE_AP_CONFIG               = BASE + 28;
432    /* Invoked when getting a tether state change notification */
433    static final int CMD_TETHER_STATE_CHANGE              = BASE + 29;
434    /* A delayed message sent to indicate tether state change failed to arrive */
435    static final int CMD_TETHER_NOTIFICATION_TIMED_OUT    = BASE + 30;
436
437    static final int CMD_BLUETOOTH_ADAPTER_STATE_CHANGE   = BASE + 31;
438
439    /* Supplicant commands */
440    /* Is supplicant alive ? */
441    static final int CMD_PING_SUPPLICANT                  = BASE + 51;
442    /* Add/update a network configuration */
443    static final int CMD_ADD_OR_UPDATE_NETWORK            = BASE + 52;
444    /* Delete a network */
445    static final int CMD_REMOVE_NETWORK                   = BASE + 53;
446    /* Enable a network. The device will attempt a connection to the given network. */
447    static final int CMD_ENABLE_NETWORK                   = BASE + 54;
448    /* Enable all networks */
449    static final int CMD_ENABLE_ALL_NETWORKS              = BASE + 55;
450    /* Blacklist network. De-prioritizes the given BSSID for connection. */
451    static final int CMD_BLACKLIST_NETWORK                = BASE + 56;
452    /* Clear the blacklist network list */
453    static final int CMD_CLEAR_BLACKLIST                  = BASE + 57;
454    /* Save configuration */
455    static final int CMD_SAVE_CONFIG                      = BASE + 58;
456    /* Get configured networks */
457    static final int CMD_GET_CONFIGURED_NETWORKS          = BASE + 59;
458    /* Get available frequencies */
459    static final int CMD_GET_CAPABILITY_FREQ              = BASE + 60;
460    /* Get adaptors */
461    static final int CMD_GET_SUPPORTED_FEATURES           = BASE + 61;
462    /* Get configured networks with real preSharedKey */
463    static final int CMD_GET_PRIVILEGED_CONFIGURED_NETWORKS = BASE + 62;
464    /* Get Link Layer Stats thru HAL */
465    static final int CMD_GET_LINK_LAYER_STATS             = BASE + 63;
466    /* Supplicant commands after driver start*/
467    /* Initiate a scan */
468    static final int CMD_START_SCAN                       = BASE + 71;
469    /* Set operational mode. CONNECT, SCAN ONLY, SCAN_ONLY with Wi-Fi off mode */
470    static final int CMD_SET_OPERATIONAL_MODE             = BASE + 72;
471    /* Disconnect from a network */
472    static final int CMD_DISCONNECT                       = BASE + 73;
473    /* Reconnect to a network */
474    static final int CMD_RECONNECT                        = BASE + 74;
475    /* Reassociate to a network */
476    static final int CMD_REASSOCIATE                      = BASE + 75;
477    /* Get Connection Statistis */
478    static final int CMD_GET_CONNECTION_STATISTICS        = BASE + 76;
479
480    /* Controls suspend mode optimizations
481     *
482     * When high perf mode is enabled, suspend mode optimizations are disabled
483     *
484     * When high perf mode is disabled, suspend mode optimizations are enabled
485     *
486     * Suspend mode optimizations include:
487     * - packet filtering
488     * - turn off roaming
489     * - DTIM wake up settings
490     */
491    static final int CMD_SET_HIGH_PERF_MODE               = BASE + 77;
492    /* Set the country code */
493    static final int CMD_SET_COUNTRY_CODE                 = BASE + 80;
494    /* Enables RSSI poll */
495    static final int CMD_ENABLE_RSSI_POLL                 = BASE + 82;
496    /* RSSI poll */
497    static final int CMD_RSSI_POLL                        = BASE + 83;
498    /* Set up packet filtering */
499    static final int CMD_START_PACKET_FILTERING           = BASE + 84;
500    /* Clear packet filter */
501    static final int CMD_STOP_PACKET_FILTERING            = BASE + 85;
502    /* Enable suspend mode optimizations in the driver */
503    static final int CMD_SET_SUSPEND_OPT_ENABLED          = BASE + 86;
504    /* Delayed NETWORK_DISCONNECT */
505    static final int CMD_DELAYED_NETWORK_DISCONNECT       = BASE + 87;
506    /* When there are no saved networks, we do a periodic scan to notify user of
507     * an open network */
508    static final int CMD_NO_NETWORKS_PERIODIC_SCAN        = BASE + 88;
509    /* Test network Disconnection NETWORK_DISCONNECT */
510    static final int CMD_TEST_NETWORK_DISCONNECT          = BASE + 89;
511    private int testNetworkDisconnectCounter = 0;
512
513    /* arg1 values to CMD_STOP_PACKET_FILTERING and CMD_START_PACKET_FILTERING */
514    static final int MULTICAST_V6  = 1;
515    static final int MULTICAST_V4  = 0;
516
517   /* Set the frequency band */
518    static final int CMD_SET_FREQUENCY_BAND               = BASE + 90;
519    /* Enable TDLS on a specific MAC address */
520    static final int CMD_ENABLE_TDLS                      = BASE + 92;
521    /* DHCP/IP configuration watchdog */
522    static final int CMD_OBTAINING_IP_ADDRESS_WATCHDOG_TIMER    = BASE + 93;
523
524    /**
525     * Make this timer 40 seconds, which is about the normal DHCP timeout.
526     * In no valid case, the WiFiStateMachine should remain stuck in ObtainingIpAddress
527     * for more than 30 seconds.
528     */
529    static final int OBTAINING_IP_ADDRESS_GUARD_TIMER_MSEC = 40000;
530
531    int obtainingIpWatchdogCount = 0;
532
533    /* Commands from/to the SupplicantStateTracker */
534    /* Reset the supplicant state tracker */
535    static final int CMD_RESET_SUPPLICANT_STATE           = BASE + 111;
536
537
538    /**
539     * Watchdog for protecting against b/16823537
540     * Leave time for 4-ways handshake to succeed
541     */
542    static final int ROAM_GUARD_TIMER_MSEC = 15000;
543
544    int roamWatchdogCount = 0;
545    /* Roam state watchdog */
546    static final int CMD_ROAM_WATCHDOG_TIMER    = BASE + 94;
547    /* Screen change intent handling */
548    static final int CMD_SCREEN_STATE_CHANGED              = BASE + 95;
549
550    int disconnectingWatchdogCount = 0;
551    static final int DISCONNECTING_GUARD_TIMER_MSEC = 5000;
552
553    /* Disconnecting state watchdog */
554    static final int CMD_DISCONNECTING_WATCHDOG_TIMER     = BASE + 96;
555
556    /* P2p commands */
557    /* We are ok with no response here since we wont do much with it anyway */
558    public static final int CMD_ENABLE_P2P                = BASE + 131;
559    /* In order to shut down supplicant cleanly, we wait till p2p has
560     * been disabled */
561    public static final int CMD_DISABLE_P2P_REQ           = BASE + 132;
562    public static final int CMD_DISABLE_P2P_RSP           = BASE + 133;
563
564    public static final int CMD_BOOT_COMPLETED            = BASE + 134;
565
566    /* change the batch scan settings.
567     * arg1 = responsible UID
568     * arg2 = csph (channel scans per hour)
569     * obj = bundle with the new settings and the optional worksource
570     */
571    public static final int CMD_SET_BATCHED_SCAN          = BASE + 135;
572    public static final int CMD_START_NEXT_BATCHED_SCAN   = BASE + 136;
573    public static final int CMD_POLL_BATCHED_SCAN         = BASE + 137;
574
575    /* We now have a valid IP configuration. */
576    static final int CMD_IP_CONFIGURATION_SUCCESSFUL      = BASE + 138;
577    /* We no longer have a valid IP configuration. */
578    static final int CMD_IP_CONFIGURATION_LOST            = BASE + 139;
579    /* Link configuration (IP address, DNS, ...) changes notified via netlink */
580    static final int CMD_UPDATE_LINKPROPERTIES            = BASE + 140;
581
582    /* Supplicant is trying to associate to a given BSSID */
583    static final int CMD_TARGET_BSSID                     = BASE + 141;
584
585    /* Reload all networks and reconnect */
586    static final int CMD_RELOAD_TLS_AND_RECONNECT         = BASE + 142;
587
588    static final int CMD_AUTO_CONNECT                     = BASE + 143;
589
590    static final int network_status_unwanted_disconnect = 0;
591    static final int network_status_unwanted_disable_autojoin = 1;
592
593    static final int CMD_UNWANTED_NETWORK                 = BASE + 144;
594
595    static final int CMD_AUTO_ROAM                        = BASE + 145;
596
597    static final int CMD_AUTO_SAVE_NETWORK                = BASE + 146;
598
599    static final int CMD_ASSOCIATED_BSSID                = BASE + 147;
600
601    /* Wifi state machine modes of operation */
602    /* CONNECT_MODE - connect to any 'known' AP when it becomes available */
603    public static final int CONNECT_MODE                   = 1;
604    /* SCAN_ONLY_MODE - don't connect to any APs; scan, but only while apps hold lock */
605    public static final int SCAN_ONLY_MODE                 = 2;
606    /* SCAN_ONLY_WITH_WIFI_OFF - scan, but don't connect to any APs */
607    public static final int SCAN_ONLY_WITH_WIFI_OFF_MODE   = 3;
608
609    private static final int SUCCESS = 1;
610    private static final int FAILURE = -1;
611
612    /* Tracks if suspend optimizations need to be disabled by DHCP,
613     * screen or due to high perf mode.
614     * When any of them needs to disable it, we keep the suspend optimizations
615     * disabled
616     */
617    private int mSuspendOptNeedsDisabled = 0;
618
619    private static final int SUSPEND_DUE_TO_DHCP       = 1;
620    private static final int SUSPEND_DUE_TO_HIGH_PERF  = 1<<1;
621    private static final int SUSPEND_DUE_TO_SCREEN     = 1<<2;
622
623    /* Tracks if user has enabled suspend optimizations through settings */
624    private AtomicBoolean mUserWantsSuspendOpt = new AtomicBoolean(true);
625
626    /**
627     * Default framework scan interval in milliseconds. This is used in the scenario in which
628     * wifi chipset does not support background scanning to set up a
629     * periodic wake up scan so that the device can connect to a new access
630     * point on the move. {@link Settings.Global#WIFI_FRAMEWORK_SCAN_INTERVAL_MS} can
631     * override this.
632     */
633    private final int mDefaultFrameworkScanIntervalMs;
634
635    private int mDisconnectedScanPeriodMs = 10000;
636
637    /**
638     * Supplicant scan interval in milliseconds.
639     * Comes from {@link Settings.Global#WIFI_SUPPLICANT_SCAN_INTERVAL_MS} or
640     * from the default config if the setting is not set
641     */
642    private long mSupplicantScanIntervalMs;
643
644    /**
645     * timeStamp of last full band scan we perfoemed for autojoin while connected with screen lit
646     */
647    private long lastFullBandConnectedTimeMilli;
648
649    /**
650     * time interval to the next full band scan we will perform for
651     * autojoin while connected with screen lit
652     */
653    private long fullBandConnectedTimeIntervalMilli;
654
655    /**
656     * max time interval to the next full band scan we will perform for
657     * autojoin while connected with screen lit
658     * Max time is 5 minutes
659     */
660    private static final long  maxFullBandConnectedTimeIntervalMilli = 1000 * 60 * 5;
661
662    /**
663     * Minimum time interval between enabling all networks.
664     * A device can end up repeatedly connecting to a bad network on screen on/off toggle
665     * due to enabling every time. We add a threshold to avoid this.
666     */
667    private static final int MIN_INTERVAL_ENABLE_ALL_NETWORKS_MS = 10 * 60 * 1000; /* 10 minutes */
668    private long mLastEnableAllNetworksTime;
669
670    /**
671     * Starting and shutting down driver too quick causes problems leading to driver
672     * being in a bad state. Delay driver stop.
673     */
674    private final int mDriverStopDelayMs;
675    private int mDelayedStopCounter;
676    private boolean mInDelayedStop = false;
677
678    // sometimes telephony gives us this data before boot is complete and we can't store it
679    // until after, so the write is deferred
680    private volatile String mPersistedCountryCode;
681
682    // Supplicant doesn't like setting the same country code multiple times (it may drop
683    // currently connected network), so we save the country code here to avoid redundency
684    private String mLastSetCountryCode;
685
686    /* Default parent state */
687    private State mDefaultState = new DefaultState();
688    /* Temporary initial state */
689    private State mInitialState = new InitialState();
690    /* Driver loaded, waiting for supplicant to start */
691    private State mSupplicantStartingState = new SupplicantStartingState();
692    /* Driver loaded and supplicant ready */
693    private State mSupplicantStartedState = new SupplicantStartedState();
694    /* Waiting for supplicant to stop and monitor to exit */
695    private State mSupplicantStoppingState = new SupplicantStoppingState();
696    /* Driver start issued, waiting for completed event */
697    private State mDriverStartingState = new DriverStartingState();
698    /* Driver started */
699    private State mDriverStartedState = new DriverStartedState();
700    /* Wait until p2p is disabled
701     * This is a special state which is entered right after we exit out of DriverStartedState
702     * before transitioning to another state.
703     */
704    private State mWaitForP2pDisableState = new WaitForP2pDisableState();
705    /* Driver stopping */
706    private State mDriverStoppingState = new DriverStoppingState();
707    /* Driver stopped */
708    private State mDriverStoppedState = new DriverStoppedState();
709    /* Scan for networks, no connection will be established */
710    private State mScanModeState = new ScanModeState();
711    /* Connecting to an access point */
712    private State mConnectModeState = new ConnectModeState();
713    /* Connected at 802.11 (L2) level */
714    private State mL2ConnectedState = new L2ConnectedState();
715    /* fetching IP after connection to access point (assoc+auth complete) */
716    private State mObtainingIpState = new ObtainingIpState();
717    /* Waiting for link quality verification to be complete */
718    private State mVerifyingLinkState = new VerifyingLinkState();
719    /* Connected with IP addr */
720    private State mConnectedState = new ConnectedState();
721    /* Roaming */
722    private State mRoamingState = new RoamingState();
723    /* disconnect issued, waiting for network disconnect confirmation */
724    private State mDisconnectingState = new DisconnectingState();
725    /* Network is not connected, supplicant assoc+auth is not complete */
726    private State mDisconnectedState = new DisconnectedState();
727    /* Waiting for WPS to be completed*/
728    private State mWpsRunningState = new WpsRunningState();
729
730    /* Soft ap is starting up */
731    private State mSoftApStartingState = new SoftApStartingState();
732    /* Soft ap is running */
733    private State mSoftApStartedState = new SoftApStartedState();
734    /* Soft ap is running and we are waiting for tether notification */
735    private State mTetheringState = new TetheringState();
736    /* Soft ap is running and we are tethered through connectivity service */
737    private State mTetheredState = new TetheredState();
738    /* Waiting for untether confirmation before stopping soft Ap */
739    private State mUntetheringState = new UntetheringState();
740
741    private class TetherStateChange {
742        ArrayList<String> available;
743        ArrayList<String> active;
744        TetherStateChange(ArrayList<String> av, ArrayList<String> ac) {
745            available = av;
746            active = ac;
747        }
748    }
749
750    public static class SimAuthRequestData {
751        int networkId;
752        int protocol;
753        String ssid;
754        String[] challenges;
755    }
756
757    public static class SimAuthResponseData {
758        int id;
759        String Kc1;
760        String SRES1;
761        String Kc2;
762        String SRES2;
763        String Kc3;
764        String SRES3;
765    }
766
767    /**
768     * One of  {@link WifiManager#WIFI_STATE_DISABLED},
769     *         {@link WifiManager#WIFI_STATE_DISABLING},
770     *         {@link WifiManager#WIFI_STATE_ENABLED},
771     *         {@link WifiManager#WIFI_STATE_ENABLING},
772     *         {@link WifiManager#WIFI_STATE_UNKNOWN}
773     *
774     */
775    private final AtomicInteger mWifiState = new AtomicInteger(WIFI_STATE_DISABLED);
776
777    /**
778     * One of  {@link WifiManager#WIFI_AP_STATE_DISABLED},
779     *         {@link WifiManager#WIFI_AP_STATE_DISABLING},
780     *         {@link WifiManager#WIFI_AP_STATE_ENABLED},
781     *         {@link WifiManager#WIFI_AP_STATE_ENABLING},
782     *         {@link WifiManager#WIFI_AP_STATE_FAILED}
783     *
784     */
785    private final AtomicInteger mWifiApState = new AtomicInteger(WIFI_AP_STATE_DISABLED);
786
787    private static final int SCAN_REQUEST = 0;
788    private static final String ACTION_START_SCAN =
789        "com.android.server.WifiManager.action.START_SCAN";
790
791    private static final String DELAYED_STOP_COUNTER = "DelayedStopCounter";
792    private static final int DRIVER_STOP_REQUEST = 0;
793    private static final String ACTION_DELAYED_DRIVER_STOP =
794        "com.android.server.WifiManager.action.DELAYED_DRIVER_STOP";
795
796    private static final String ACTION_REFRESH_BATCHED_SCAN =
797            "com.android.server.WifiManager.action.REFRESH_BATCHED_SCAN";
798    /**
799     * Keep track of whether WIFI is running.
800     */
801    private boolean mIsRunning = false;
802
803    /**
804     * Keep track of whether we last told the battery stats we had started.
805     */
806    private boolean mReportedRunning = false;
807
808    /**
809     * Most recently set source of starting WIFI.
810     */
811    private final WorkSource mRunningWifiUids = new WorkSource();
812
813    /**
814     * The last reported UIDs that were responsible for starting WIFI.
815     */
816    private final WorkSource mLastRunningWifiUids = new WorkSource();
817
818    private final IBatteryStats mBatteryStats;
819
820    private BatchedScanSettings mBatchedScanSettings = null;
821
822    /**
823     * Track the worksource/cost of the current settings and track what's been noted
824     * to the battery stats, so we can mark the end of the previous when changing.
825     */
826    private WorkSource mBatchedScanWorkSource = null;
827    private int mBatchedScanCsph = 0;
828    private WorkSource mNotedBatchedScanWorkSource = null;
829    private int mNotedBatchedScanCsph = 0;
830
831    private String mTcpBufferSizes = null;
832
833    // Used for debug and stats gathering
834    private static int sScanAlarmIntentCount = 0;
835
836    public WifiStateMachine(Context context, String wlanInterface,
837            WifiTrafficPoller trafficPoller){
838        super("WifiStateMachine");
839        mContext = context;
840        mInterfaceName = wlanInterface;
841        mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, NETWORKTYPE, "");
842        mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
843                BatteryStats.SERVICE_NAME));
844
845        IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
846        mNwService = INetworkManagementService.Stub.asInterface(b);
847
848        mP2pSupported = mContext.getPackageManager().hasSystemFeature(
849                PackageManager.FEATURE_WIFI_DIRECT);
850
851        mWifiNative = new WifiNative(mInterfaceName);
852        mWifiConfigStore = new WifiConfigStore(context, mWifiNative);
853        mWifiAutoJoinController = new WifiAutoJoinController(context, this,
854                mWifiConfigStore, mWifiConnectionStatistics, mWifiNative);
855        mWifiMonitor = new WifiMonitor(this, mWifiNative);
856        mWifiInfo = new WifiInfo();
857        mSupplicantStateTracker = new SupplicantStateTracker(context, this, mWifiConfigStore,
858                getHandler());
859        mLinkProperties = new LinkProperties();
860
861        IBinder s1 = ServiceManager.getService(Context.WIFI_P2P_SERVICE);
862        mWifiP2pServiceImpl = (WifiP2pServiceImpl)IWifiP2pManager.Stub.asInterface(s1);
863
864        mNetworkInfo.setIsAvailable(false);
865        mLastBssid = null;
866        mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
867        mLastSignalLevel = -1;
868
869        mNetlinkTracker = new NetlinkTracker(mInterfaceName, new NetlinkTracker.Callback() {
870            public void update() {
871                sendMessage(CMD_UPDATE_LINKPROPERTIES);
872            }
873        });
874        try {
875            mNwService.registerObserver(mNetlinkTracker);
876        } catch (RemoteException e) {
877            loge("Couldn't register netlink tracker: " + e.toString());
878        }
879
880        mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
881        Intent scanIntent = new Intent(ACTION_START_SCAN, null);
882        mScanIntent = PendingIntent.getBroadcast(mContext, SCAN_REQUEST, scanIntent, 0);
883
884        Intent batchedIntent = new Intent(ACTION_REFRESH_BATCHED_SCAN, null);
885        mBatchedScanIntervalIntent = PendingIntent.getBroadcast(mContext, 0, batchedIntent, 0);
886
887        mDefaultFrameworkScanIntervalMs = mContext.getResources().getInteger(
888                R.integer.config_wifi_framework_scan_interval);
889
890        mDriverStopDelayMs = mContext.getResources().getInteger(
891                R.integer.config_wifi_driver_stop_delay);
892
893        mBackgroundScanSupported = mContext.getResources().getBoolean(
894                R.bool.config_wifi_background_scan_support);
895
896        mPrimaryDeviceType = mContext.getResources().getString(
897                R.string.config_wifi_p2p_device_type);
898
899        mUserWantsSuspendOpt.set(Settings.Global.getInt(mContext.getContentResolver(),
900                    Settings.Global.WIFI_SUSPEND_OPTIMIZATIONS_ENABLED, 1) == 1);
901
902        mNetworkCapabilitiesFilter.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
903        mNetworkCapabilitiesFilter.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
904        mNetworkCapabilitiesFilter.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
905        mNetworkCapabilitiesFilter.setLinkUpstreamBandwidthKbps(1024 * 1024);
906        mNetworkCapabilitiesFilter.setLinkDownstreamBandwidthKbps(1024 * 1024);
907        // TODO - needs to be a bit more dynamic
908        mNetworkCapabilities = new NetworkCapabilities(mNetworkCapabilitiesFilter);
909
910        mContext.registerReceiver(
911            new BroadcastReceiver() {
912                @Override
913                public void onReceive(Context context, Intent intent) {
914                    ArrayList<String> available = intent.getStringArrayListExtra(
915                            ConnectivityManager.EXTRA_AVAILABLE_TETHER);
916                    ArrayList<String> active = intent.getStringArrayListExtra(
917                            ConnectivityManager.EXTRA_ACTIVE_TETHER);
918                    sendMessage(CMD_TETHER_STATE_CHANGE, new TetherStateChange(available, active));
919                }
920            },new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED));
921
922        mContext.registerReceiver(
923                new BroadcastReceiver() {
924                    @Override
925                    public void onReceive(Context context, Intent intent) {
926                        sScanAlarmIntentCount++;
927                        startScan(SCAN_ALARM_SOURCE, -2, null, null);
928                        if (VDBG)
929                            loge("WiFiStateMachine SCAN ALARM");
930                    }
931                },
932                new IntentFilter(ACTION_START_SCAN));
933
934        IntentFilter filter = new IntentFilter();
935        filter.addAction(Intent.ACTION_SCREEN_ON);
936        filter.addAction(Intent.ACTION_SCREEN_OFF);
937        filter.addAction(ACTION_REFRESH_BATCHED_SCAN);
938        mContext.registerReceiver(
939                new BroadcastReceiver() {
940                    @Override
941                    public void onReceive(Context context, Intent intent) {
942                        String action = intent.getAction();
943
944                        if (action.equals(Intent.ACTION_SCREEN_ON)) {
945                            sendMessage(CMD_SCREEN_STATE_CHANGED, 1);
946                        } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
947                            sendMessage(CMD_SCREEN_STATE_CHANGED, 0);
948                        } else if (action.equals(ACTION_REFRESH_BATCHED_SCAN)) {
949                            startNextBatchedScanAsync();
950                        }
951                    }
952                }, filter);
953
954        mContext.registerReceiver(
955                new BroadcastReceiver() {
956                    @Override
957                    public void onReceive(Context context, Intent intent) {
958                       int counter = intent.getIntExtra(DELAYED_STOP_COUNTER, 0);
959                       sendMessage(CMD_DELAYED_STOP_DRIVER, counter, 0);
960                    }
961                },
962                new IntentFilter(ACTION_DELAYED_DRIVER_STOP));
963
964        mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor(
965                Settings.Global.WIFI_SUSPEND_OPTIMIZATIONS_ENABLED), false,
966                new ContentObserver(getHandler()) {
967                    @Override
968                    public void onChange(boolean selfChange) {
969                        mUserWantsSuspendOpt.set(Settings.Global.getInt(mContext.getContentResolver(),
970                                Settings.Global.WIFI_SUSPEND_OPTIMIZATIONS_ENABLED, 1) == 1);
971                    }
972                });
973
974        mContext.registerReceiver(
975                new BroadcastReceiver() {
976                    @Override
977                    public void onReceive(Context context, Intent intent) {
978                        sendMessage(CMD_BOOT_COMPLETED);
979                    }
980                },
981                new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
982
983        mScanResultCache = new LruCache<String, ScanResult>(SCAN_RESULT_CACHE_SIZE);
984
985        PowerManager powerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
986        mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, getName());
987
988        mSuspendWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "WifiSuspend");
989        mSuspendWakeLock.setReferenceCounted(false);
990
991        mTcpBufferSizes = mContext.getResources().getString(
992                com.android.internal.R.string.config_wifi_tcp_buffers);
993
994        addState(mDefaultState);
995            addState(mInitialState, mDefaultState);
996            addState(mSupplicantStartingState, mDefaultState);
997            addState(mSupplicantStartedState, mDefaultState);
998                addState(mDriverStartingState, mSupplicantStartedState);
999                addState(mDriverStartedState, mSupplicantStartedState);
1000                    addState(mScanModeState, mDriverStartedState);
1001                    addState(mConnectModeState, mDriverStartedState);
1002                        addState(mL2ConnectedState, mConnectModeState);
1003                            addState(mObtainingIpState, mL2ConnectedState);
1004                            addState(mVerifyingLinkState, mL2ConnectedState);
1005                            addState(mConnectedState, mL2ConnectedState);
1006                            addState(mRoamingState, mL2ConnectedState);
1007                        addState(mDisconnectingState, mConnectModeState);
1008                        addState(mDisconnectedState, mConnectModeState);
1009                        addState(mWpsRunningState, mConnectModeState);
1010                addState(mWaitForP2pDisableState, mSupplicantStartedState);
1011                addState(mDriverStoppingState, mSupplicantStartedState);
1012                addState(mDriverStoppedState, mSupplicantStartedState);
1013            addState(mSupplicantStoppingState, mDefaultState);
1014            addState(mSoftApStartingState, mDefaultState);
1015            addState(mSoftApStartedState, mDefaultState);
1016                addState(mTetheringState, mSoftApStartedState);
1017                addState(mTetheredState, mSoftApStartedState);
1018                addState(mUntetheringState, mSoftApStartedState);
1019
1020        setInitialState(mInitialState);
1021
1022        setLogRecSize(3000);
1023        setLogOnlyTransitions(false);
1024        if (VDBG) setDbg(true);
1025
1026        //start the state machine
1027        start();
1028
1029        final Intent intent = new Intent(WifiManager.WIFI_SCAN_AVAILABLE);
1030        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1031        intent.putExtra(WifiManager.EXTRA_SCAN_AVAILABLE, WIFI_STATE_DISABLED);
1032        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
1033    }
1034
1035    private int mVerboseLoggingLevel = 0;
1036
1037    int getVerboseLoggingLevel() {
1038        return mVerboseLoggingLevel;
1039    }
1040
1041    void enableVerboseLogging(int verbose) {
1042        mVerboseLoggingLevel = verbose;
1043        if (verbose > 0) {
1044            DBG = true;
1045            VDBG = true;
1046            PDBG = true;
1047            mLogMessages = true;
1048            mWifiNative.setSupplicantLogLevel("DEBUG");
1049        } else {
1050            DBG = false;
1051            VDBG = false;
1052            PDBG = false;
1053            mLogMessages = false;
1054            mWifiNative.setSupplicantLogLevel("INFO");
1055        }
1056        mWifiAutoJoinController.enableVerboseLogging(verbose);
1057        mWifiMonitor.enableVerboseLogging(verbose);
1058        mWifiNative.enableVerboseLogging(verbose);
1059        mWifiConfigStore.enableVerboseLogging(verbose);
1060        mSupplicantStateTracker.enableVerboseLogging(verbose);
1061    }
1062
1063    private int mAggressiveHandover = 0;
1064
1065    int getAggressiveHandover() {
1066        return mAggressiveHandover;
1067    }
1068
1069    void enableAggressiveHandover(int enabled) {
1070        mAggressiveHandover = enabled;
1071    }
1072
1073    public void setAllowScansWithTraffic(int enabled) {
1074        mWifiConfigStore.alwaysEnableScansWhileAssociated = enabled;
1075    }
1076
1077    public int getAllowScansWithTraffic() {
1078        return mWifiConfigStore.alwaysEnableScansWhileAssociated;
1079    }
1080
1081    /*
1082     *
1083     * Framework scan control
1084     */
1085
1086    private boolean mAlarmEnabled = false;
1087    /* This is set from the overlay config file or from a secure setting.
1088     * A value of 0 disables scanning in the framework.
1089     */
1090    private long mFrameworkScanIntervalMs = 10000;
1091
1092    private long mCurrentScanAlarmMs = 10000;
1093    private void setScanAlarm(boolean enabled, int delayMilli) {
1094        if (PDBG) {
1095            loge("setScanAlarm " + enabled
1096                    + " period " + mCurrentScanAlarmMs
1097                    + " initial delay " + delayMilli);
1098        }
1099        if (mCurrentScanAlarmMs <= 0) enabled = false;
1100        if (enabled == mAlarmEnabled) return;
1101        if (enabled) {
1102            long initialDelayMilli;
1103            if (delayMilli <= 0) {
1104                // scan now
1105                startScan(SCAN_ALARM_SOURCE, 0, null, null);
1106                initialDelayMilli = mCurrentScanAlarmMs;
1107            } else {
1108                initialDelayMilli = delayMilli;
1109            }
1110
1111            int type = AlarmManager.RTC;
1112
1113            /* Set RTC_WAKEUP alarms if PNO is not supported - because no one is */
1114            /* going to wake up the host processor to look for access points */
1115            if (mBackgroundScanSupported == false)
1116                type = AlarmManager.RTC_WAKEUP;
1117
1118            mAlarmManager.setRepeating(type,
1119                    System.currentTimeMillis() + initialDelayMilli,
1120                    mCurrentScanAlarmMs,
1121                    mScanIntent);
1122            mAlarmEnabled = true;
1123        } else {
1124            mAlarmManager.cancel(mScanIntent);
1125            mAlarmEnabled = false;
1126        }
1127    }
1128
1129    private boolean setRandomMacOui() {
1130        String oui = mContext.getResources().getString(
1131                R.string.config_wifi_random_mac_oui, GOOGLE_OUI);
1132        String[] ouiParts = oui.split("-");
1133        byte[] ouiBytes = new byte[3];
1134        ouiBytes[0] = (byte) (Integer.parseInt(ouiParts[0], 16) & 0xFF);
1135        ouiBytes[1] = (byte) (Integer.parseInt(ouiParts[1], 16) & 0xFF);
1136        ouiBytes[2] = (byte) (Integer.parseInt(ouiParts[2], 16) & 0xFF);
1137
1138        logd("Setting OUI to " + oui);
1139        return mWifiNative.setScanningMacOui(ouiBytes);
1140    }
1141
1142    /*********************************************************
1143     * Methods exposed for public use
1144     ********************************************************/
1145
1146    public Messenger getMessenger() {
1147        return new Messenger(getHandler());
1148    }
1149
1150    public WifiMonitor getWifiMonitor() {
1151        return mWifiMonitor;
1152    }
1153
1154    /**
1155     * TODO: doc
1156     */
1157    public boolean syncPingSupplicant(AsyncChannel channel) {
1158        Message resultMsg = channel.sendMessageSynchronously(CMD_PING_SUPPLICANT);
1159        boolean result = (resultMsg.arg1 != FAILURE);
1160        resultMsg.recycle();
1161        return result;
1162    }
1163
1164    public List<WifiChannel> syncGetChannelList(AsyncChannel channel) {
1165        Message resultMsg = channel.sendMessageSynchronously(CMD_GET_CAPABILITY_FREQ);
1166        List<WifiChannel> list = null;
1167        if (resultMsg.obj != null) {
1168            list = new ArrayList<WifiChannel>();
1169            String freqs = (String) resultMsg.obj;
1170            String[] lines = freqs.split("\n");
1171            for (String line : lines)
1172                if (line.contains("MHz")) {
1173                    // line format: " 52 = 5260 MHz (NO_IBSS) (DFS)"
1174                    WifiChannel c = new WifiChannel();
1175                    String[] prop = line.split(" ");
1176                    if (prop.length < 5) continue;
1177                    try {
1178                        c.channelNum = Integer.parseInt(prop[1]);
1179                        c.freqMHz = Integer.parseInt(prop[3]);
1180                    } catch (NumberFormatException e) { }
1181                    c.isDFS = line.contains("(DFS)");
1182                    list.add(c);
1183                } else if (line.contains("Mode[B] Channels:")) {
1184                    // B channels are the same as G channels, skipped
1185                    break;
1186                }
1187        }
1188        resultMsg.recycle();
1189        return (list != null && list.size() > 0) ? list : null;
1190    }
1191
1192
1193    /**
1194     * Initiate a wifi scan. If workSource is not null, blame is given to it, otherwise blame is
1195     * given to callingUid.
1196     *
1197     * @param callingUid The uid initiating the wifi scan. Blame will be given here unless
1198     *                   workSource is specified.
1199     * @param workSource If not null, blame is given to workSource.
1200     * @param settings Scan settings, see {@link ScanSettings}.
1201     */
1202    public void startScan(int callingUid, int scanCounter,
1203                          ScanSettings settings, WorkSource workSource) {
1204        Bundle bundle = new Bundle();
1205        bundle.putParcelable(CUSTOMIZED_SCAN_SETTING, settings);
1206        bundle.putParcelable(CUSTOMIZED_SCAN_WORKSOURCE, workSource);
1207        bundle.putLong(SCAN_REQUEST_TIME, System.currentTimeMillis());
1208        sendMessage(CMD_START_SCAN, callingUid, scanCounter, bundle);
1209    }
1210
1211    /**
1212     * start or stop batched scanning using the given settings
1213     */
1214    public void setBatchedScanSettings(BatchedScanSettings settings, int callingUid, int csph,
1215            WorkSource workSource) {
1216        Bundle bundle = new Bundle();
1217        bundle.putParcelable(BATCHED_SETTING, settings);
1218        bundle.putParcelable(BATCHED_WORKSOURCE, workSource);
1219        sendMessage(CMD_SET_BATCHED_SCAN, callingUid, csph, bundle);
1220    }
1221
1222    public List<BatchedScanResult> syncGetBatchedScanResultsList() {
1223        synchronized (mBatchedScanResults) {
1224            List<BatchedScanResult> batchedScanList =
1225                    new ArrayList<BatchedScanResult>(mBatchedScanResults.size());
1226            for(BatchedScanResult result: mBatchedScanResults) {
1227                batchedScanList.add(new BatchedScanResult(result));
1228            }
1229            return batchedScanList;
1230        }
1231    }
1232
1233    public void requestBatchedScanPoll() {
1234        sendMessage(CMD_POLL_BATCHED_SCAN);
1235    }
1236
1237    private void startBatchedScan() {
1238        if (mBatchedScanSettings == null) return;
1239
1240        if (mDhcpActive) {
1241            if (DBG) log("not starting Batched Scans due to DHCP");
1242            return;
1243        }
1244
1245        // first grab any existing data
1246        retrieveBatchedScanData();
1247
1248        if (PDBG) loge("try  starting Batched Scans due to DHCP");
1249
1250
1251        mAlarmManager.cancel(mBatchedScanIntervalIntent);
1252
1253        String scansExpected = mWifiNative.setBatchedScanSettings(mBatchedScanSettings);
1254        try {
1255            mExpectedBatchedScans = Integer.parseInt(scansExpected);
1256            setNextBatchedAlarm(mExpectedBatchedScans);
1257            if (mExpectedBatchedScans > 0) noteBatchedScanStart();
1258        } catch (NumberFormatException e) {
1259            stopBatchedScan();
1260            loge("Exception parsing WifiNative.setBatchedScanSettings response " + e);
1261        }
1262    }
1263
1264    // called from BroadcastListener
1265    private void startNextBatchedScanAsync() {
1266        sendMessage(CMD_START_NEXT_BATCHED_SCAN);
1267    }
1268
1269    private void startNextBatchedScan() {
1270        // first grab any existing data
1271        retrieveBatchedScanData();
1272
1273        setNextBatchedAlarm(mExpectedBatchedScans);
1274    }
1275
1276    private void handleBatchedScanPollRequest() {
1277        if (DBG) {
1278            log("handleBatchedScanPoll Request - mBatchedScanMinPollTime=" +
1279                    mBatchedScanMinPollTime + " , mBatchedScanSettings=" +
1280                    mBatchedScanSettings);
1281        }
1282        // if there is no appropriate PollTime that's because we either aren't
1283        // batching or we've already set a time for a poll request
1284        if (mBatchedScanMinPollTime == 0) return;
1285        if (mBatchedScanSettings == null) return;
1286
1287        long now = System.currentTimeMillis();
1288
1289        if (now > mBatchedScanMinPollTime) {
1290            // do the poll and reset our timers
1291            startNextBatchedScan();
1292        } else {
1293            mAlarmManager.setExact(AlarmManager.RTC_WAKEUP, mBatchedScanMinPollTime,
1294                    mBatchedScanIntervalIntent);
1295            mBatchedScanMinPollTime = 0;
1296        }
1297    }
1298
1299    // return true if new/different
1300    private boolean recordBatchedScanSettings(int responsibleUid, int csph, Bundle bundle) {
1301        BatchedScanSettings settings = bundle.getParcelable(BATCHED_SETTING);
1302        WorkSource responsibleWorkSource = bundle.getParcelable(BATCHED_WORKSOURCE);
1303
1304        if (DBG) {
1305            log("set batched scan to " + settings + " for uid=" + responsibleUid +
1306                    ", worksource=" + responsibleWorkSource);
1307        }
1308        if (settings != null) {
1309            if (settings.equals(mBatchedScanSettings)) return false;
1310        } else {
1311            if (mBatchedScanSettings == null) return false;
1312        }
1313        mBatchedScanSettings = settings;
1314        if (responsibleWorkSource == null) responsibleWorkSource = new WorkSource(responsibleUid);
1315        mBatchedScanWorkSource = responsibleWorkSource;
1316        mBatchedScanCsph = csph;
1317        return true;
1318    }
1319
1320    private void stopBatchedScan() {
1321        mAlarmManager.cancel(mBatchedScanIntervalIntent);
1322        retrieveBatchedScanData();
1323        mWifiNative.setBatchedScanSettings(null);
1324        noteBatchedScanStop();
1325    }
1326
1327    private void setNextBatchedAlarm(int scansExpected) {
1328
1329        if (mBatchedScanSettings == null || scansExpected < 1) return;
1330
1331        mBatchedScanMinPollTime = System.currentTimeMillis() +
1332                mBatchedScanSettings.scanIntervalSec * 1000;
1333
1334        if (mBatchedScanSettings.maxScansPerBatch < scansExpected) {
1335            scansExpected = mBatchedScanSettings.maxScansPerBatch;
1336        }
1337
1338        int secToFull = mBatchedScanSettings.scanIntervalSec;
1339        secToFull *= scansExpected;
1340
1341        int debugPeriod = SystemProperties.getInt("wifi.batchedScan.pollPeriod", 0);
1342        if (debugPeriod > 0) secToFull = debugPeriod;
1343
1344        // set the alarm to do the next poll.  We set it a little short as we'd rather
1345        // wake up wearly than miss a scan due to buffer overflow
1346        mAlarmManager.setExact(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()
1347                + ((secToFull - (mBatchedScanSettings.scanIntervalSec / 2)) * 1000),
1348                mBatchedScanIntervalIntent);
1349    }
1350
1351    /**
1352     * Start reading new scan data
1353     * Data comes in as:
1354     * "scancount=5\n"
1355     * "nextcount=5\n"
1356     *   "apcount=3\n"
1357     *   "trunc\n" (optional)
1358     *     "bssid=...\n"
1359     *     "ssid=...\n"
1360     *     "freq=...\n" (in Mhz)
1361     *     "level=...\n"
1362     *     "dist=...\n" (in cm)
1363     *     "distsd=...\n" (standard deviation, in cm)
1364     *     "===="
1365     *     "bssid=...\n"
1366     *     etc
1367     *     "===="
1368     *     "bssid=...\n"
1369     *     etc
1370     *     "%%%%"
1371     *   "apcount=2\n"
1372     *     "bssid=...\n"
1373     *     etc
1374     *     "%%%%
1375     *   etc
1376     *   "----"
1377     */
1378    private final static boolean DEBUG_PARSE = false;
1379    private void retrieveBatchedScanData() {
1380        String rawData = mWifiNative.getBatchedScanResults();
1381        if (DEBUG_PARSE) log("rawData = " + rawData);
1382        mBatchedScanMinPollTime = 0;
1383        if (rawData == null || rawData.equalsIgnoreCase("OK")) {
1384            loge("Unexpected BatchedScanResults :" + rawData);
1385            return;
1386        }
1387
1388        int scanCount = 0;
1389        final String END_OF_BATCHES = "----";
1390        final String SCANCOUNT = "scancount=";
1391        final String TRUNCATED = "trunc";
1392        final String AGE = "age=";
1393        final String DIST = "dist=";
1394        final String DISTSD = "distSd=";
1395
1396        String splitData[] = rawData.split("\n");
1397        int n = 0;
1398        if (splitData[n].startsWith(SCANCOUNT)) {
1399            try {
1400                scanCount = Integer.parseInt(splitData[n++].substring(SCANCOUNT.length()));
1401            } catch (NumberFormatException e) {
1402                loge("scancount parseInt Exception from " + splitData[n]);
1403            }
1404        } else log("scancount not found");
1405        if (scanCount == 0) {
1406            loge("scanCount==0 - aborting");
1407            return;
1408        }
1409
1410        final Intent intent = new Intent(WifiManager.BATCHED_SCAN_RESULTS_AVAILABLE_ACTION);
1411        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1412
1413        synchronized (mBatchedScanResults) {
1414            mBatchedScanResults.clear();
1415            BatchedScanResult batchedScanResult = new BatchedScanResult();
1416
1417            String bssid = null;
1418            WifiSsid wifiSsid = null;
1419            int level = 0;
1420            int freq = 0;
1421            int dist, distSd;
1422            long tsf = 0;
1423            dist = distSd = ScanResult.UNSPECIFIED;
1424            final long now = SystemClock.elapsedRealtime();
1425            final int bssidStrLen = BSSID_STR.length();
1426
1427            while (true) {
1428                while (n < splitData.length) {
1429                    if (DEBUG_PARSE) logd("parsing " + splitData[n]);
1430                    if (splitData[n].equals(END_OF_BATCHES)) {
1431                        if (n+1 != splitData.length) {
1432                            loge("didn't consume " + (splitData.length-n));
1433                        }
1434                        if (mBatchedScanResults.size() > 0) {
1435                            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
1436                        }
1437                        logd("retrieveBatchedScanResults X");
1438                        return;
1439                    }
1440                    if ((splitData[n].equals(END_STR)) || splitData[n].equals(DELIMITER_STR)) {
1441                        if (bssid != null) {
1442                            batchedScanResult.scanResults.add(new ScanResult(
1443                                    wifiSsid, bssid, "", level, freq, tsf, dist, distSd));
1444                            wifiSsid = null;
1445                            bssid = null;
1446                            level = 0;
1447                            freq = 0;
1448                            tsf = 0;
1449                            dist = distSd = ScanResult.UNSPECIFIED;
1450                        }
1451                        if (splitData[n].equals(END_STR)) {
1452                            if (batchedScanResult.scanResults.size() != 0) {
1453                                mBatchedScanResults.add(batchedScanResult);
1454                                batchedScanResult = new BatchedScanResult();
1455                            } else {
1456                                logd("Found empty batch");
1457                            }
1458                        }
1459                    } else if (splitData[n].equals(TRUNCATED)) {
1460                        batchedScanResult.truncated = true;
1461                    } else if (splitData[n].startsWith(BSSID_STR)) {
1462                        bssid = new String(splitData[n].getBytes(), bssidStrLen,
1463                                splitData[n].length() - bssidStrLen);
1464                    } else if (splitData[n].startsWith(FREQ_STR)) {
1465                        try {
1466                            freq = Integer.parseInt(splitData[n].substring(FREQ_STR.length()));
1467                        } catch (NumberFormatException e) {
1468                            loge("Invalid freqency: " + splitData[n]);
1469                            freq = 0;
1470                        }
1471                    } else if (splitData[n].startsWith(AGE)) {
1472                        try {
1473                            tsf = now - Long.parseLong(splitData[n].substring(AGE.length()));
1474                            tsf *= 1000; // convert mS -> uS
1475                        } catch (NumberFormatException e) {
1476                            loge("Invalid timestamp: " + splitData[n]);
1477                            tsf = 0;
1478                        }
1479                    } else if (splitData[n].startsWith(SSID_STR)) {
1480                        wifiSsid = WifiSsid.createFromAsciiEncoded(
1481                                splitData[n].substring(SSID_STR.length()));
1482                    } else if (splitData[n].startsWith(LEVEL_STR)) {
1483                        try {
1484                            level = Integer.parseInt(splitData[n].substring(LEVEL_STR.length()));
1485                            if (level > 0) level -= 256;
1486                        } catch (NumberFormatException e) {
1487                            loge("Invalid level: " + splitData[n]);
1488                            level = 0;
1489                        }
1490                    } else if (splitData[n].startsWith(DIST)) {
1491                        try {
1492                            dist = Integer.parseInt(splitData[n].substring(DIST.length()));
1493                        } catch (NumberFormatException e) {
1494                            loge("Invalid distance: " + splitData[n]);
1495                            dist = ScanResult.UNSPECIFIED;
1496                        }
1497                    } else if (splitData[n].startsWith(DISTSD)) {
1498                        try {
1499                            distSd = Integer.parseInt(splitData[n].substring(DISTSD.length()));
1500                        } catch (NumberFormatException e) {
1501                            loge("Invalid distanceSd: " + splitData[n]);
1502                            distSd = ScanResult.UNSPECIFIED;
1503                        }
1504                    } else {
1505                        loge("Unable to parse batched scan result line: " + splitData[n]);
1506                    }
1507                    n++;
1508                }
1509                rawData = mWifiNative.getBatchedScanResults();
1510                if (DEBUG_PARSE) log("reading more data:\n" + rawData);
1511                if (rawData == null) {
1512                    loge("Unexpected null BatchedScanResults");
1513                    return;
1514                }
1515                splitData = rawData.split("\n");
1516                if (splitData.length == 0 || splitData[0].equals("ok")) {
1517                    loge("batch scan results just ended!");
1518                    if (mBatchedScanResults.size() > 0) {
1519                        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
1520                    }
1521                    return;
1522                }
1523                n = 0;
1524            }
1525        }
1526    }
1527
1528    private long mDisconnectedTimeStamp = 0;
1529
1530    public long getDisconnectedTimeMilli() {
1531        if (getCurrentState() == mDisconnectedState
1532                && mDisconnectedTimeStamp != 0) {
1533            long now_ms = System.currentTimeMillis();
1534            return now_ms - mDisconnectedTimeStamp;
1535        }
1536        return 0;
1537    }
1538
1539    // Keeping track of scan requests
1540    private long lastStartScanTimeStamp = 0;
1541    private long lastScanDuration = 0;
1542    // Last connect attempt is used to prevent scan requests:
1543    //  - for a period of 10 seconds after attempting to connect
1544    private long lastConnectAttempt = 0;
1545    private String lastScanFreqs = null;
1546
1547    // For debugging, keep track of last message status handling
1548    // TODO, find an equivalent mechanism as part of parent class
1549    private static int MESSAGE_HANDLING_STATUS_PROCESSED = 2;
1550    private static int MESSAGE_HANDLING_STATUS_OK = 1;
1551    private static int MESSAGE_HANDLING_STATUS_UNKNOWN = 0;
1552    private static int MESSAGE_HANDLING_STATUS_REFUSED = -1;
1553    private static int MESSAGE_HANDLING_STATUS_FAIL = -2;
1554    private static int MESSAGE_HANDLING_STATUS_BUFFERED = -3;
1555    private static int MESSAGE_HANDLING_STATUS_DEFERRED = -4;
1556    private static int MESSAGE_HANDLING_STATUS_DISCARD = -5;
1557    private static int MESSAGE_HANDLING_STATUS_LOOPED = -6;
1558    private static int MESSAGE_HANDLING_STATUS_HANDLING_ERROR = -7;
1559
1560    private int messageHandlingStatus = 0;
1561
1562    //TODO: this is used only to track connection attempts, however the link state and packet per
1563    //TODO: second logic should be folded into that
1564    private boolean isScanAllowed() {
1565        long now = System.currentTimeMillis();
1566        if (lastConnectAttempt != 0 && (now - lastConnectAttempt) < 10000) {
1567            return false;
1568        }
1569        return true;
1570    }
1571
1572    private int mOnTime = 0;
1573    private int mTxTime = 0;
1574    private int mRxTime = 0;
1575    private int mOnTimeStartScan = 0;
1576    private int mTxTimeStartScan = 0;
1577    private int mRxTimeStartScan = 0;
1578    private int mOnTimeScan = 0;
1579    private int mTxTimeScan = 0;
1580    private int mRxTimeScan = 0;
1581    private int mOnTimeThisScan = 0;
1582    private int mTxTimeThisScan = 0;
1583    private int mRxTimeThisScan = 0;
1584
1585    private int mOnTimeScreenStateChange = 0;
1586    private int mOnTimeAtLastReport = 0;
1587    private long lastOntimeReportTimeStamp = 0;
1588    private long lastScreenStateChangeTimeStamp = 0;
1589    private int mOnTimeLastReport = 0;
1590    private int mTxTimeLastReport = 0;
1591    private int mRxTimeLastReport = 0;
1592
1593    private long lastLinkLayerStatsUpdate = 0;
1594
1595    String reportOnTime() {
1596        long now = System.currentTimeMillis();
1597        StringBuilder sb = new StringBuilder();
1598        // Report stats since last report
1599        int on = mOnTime - mOnTimeLastReport;
1600        mOnTimeLastReport = mOnTime;
1601        int tx = mTxTime - mTxTimeLastReport;
1602        mTxTimeLastReport = mTxTime;
1603        int rx = mRxTime - mRxTimeLastReport;
1604        mRxTimeLastReport = mRxTime;
1605        int period = (int)(now - lastOntimeReportTimeStamp);
1606        lastOntimeReportTimeStamp = now;
1607        sb.append(String.format("[on:%d tx:%d rx:%d period:%d]", on, tx, rx, period));
1608        // Report stats since Screen State Changed
1609        on = mOnTime - mOnTimeScreenStateChange;
1610        period = (int)(now - lastScreenStateChangeTimeStamp);
1611        sb.append(String.format(" from screen [on:%d period:%d]", on, period));
1612        return sb.toString();
1613    }
1614
1615    WifiLinkLayerStats getWifiLinkLayerStats(boolean dbg) {
1616        WifiLinkLayerStats stats = null;
1617        if (mWifiLinkLayerStatsSupported > 0) {
1618            String name = "wlan0";
1619            stats = mWifiNative.getWifiLinkLayerStats(name);
1620            if (name != null && stats == null && mWifiLinkLayerStatsSupported > 0) {
1621                mWifiLinkLayerStatsSupported -= 1;
1622            } else if (stats != null) {
1623                lastLinkLayerStatsUpdate = System.currentTimeMillis();
1624                mOnTime = stats.on_time;
1625                mTxTime = stats.tx_time;
1626                mRxTime = stats.rx_time;
1627                if (dbg) {
1628                    loge("WifiLinkLayerStats:");
1629                    loge(stats.toString());
1630                }
1631            }
1632        }
1633        return stats;
1634    }
1635
1636    void startRadioScanStats() {
1637        WifiLinkLayerStats stats = getWifiLinkLayerStats(false);
1638        if (stats != null) {
1639            mOnTimeStartScan = stats.on_time;
1640            mTxTimeStartScan = stats.tx_time;
1641            mRxTimeStartScan = stats.rx_time;
1642            mOnTime = stats.on_time;
1643            mTxTime = stats.tx_time;
1644            mRxTime = stats.rx_time;
1645        }
1646    }
1647
1648    void closeRadioScanStats() {
1649        WifiLinkLayerStats stats = getWifiLinkLayerStats(false);
1650        if (stats != null) {
1651            mOnTimeThisScan = stats.on_time - mOnTimeStartScan;
1652            mTxTimeThisScan = stats.tx_time - mTxTimeStartScan;
1653            mRxTimeThisScan = stats.rx_time - mRxTimeStartScan;
1654            mOnTimeScan += mOnTimeThisScan;
1655            mTxTimeScan += mTxTimeThisScan;
1656            mRxTimeScan += mRxTimeThisScan;
1657        }
1658    }
1659
1660    // If workSource is not null, blame is given to it, otherwise blame is given to callingUid.
1661    private void noteScanStart(int callingUid, WorkSource workSource) {
1662        long now = System.currentTimeMillis();
1663        lastStartScanTimeStamp = now;
1664        lastScanDuration = 0;
1665        if (DBG) {
1666            String ts = String.format("[%,d ms]", now);
1667            if (workSource != null) {
1668                loge(ts + " noteScanStart" + workSource.toString()
1669                        + " uid " + Integer.toString(callingUid));
1670            } else {
1671                loge(ts + " noteScanstart no scan source");
1672            }
1673        }
1674        startRadioScanStats();
1675        if (mScanWorkSource == null && ((callingUid != UNKNOWN_SCAN_SOURCE
1676                && callingUid != SCAN_ALARM_SOURCE)
1677                || workSource != null)) {
1678            mScanWorkSource = workSource != null ? workSource : new WorkSource(callingUid);
1679            try {
1680                mBatteryStats.noteWifiScanStartedFromSource(mScanWorkSource);
1681            } catch (RemoteException e) {
1682                log(e.toString());
1683            }
1684        }
1685    }
1686
1687    private void noteScanEnd() {
1688        long now = System.currentTimeMillis();
1689        if (lastStartScanTimeStamp != 0) {
1690            lastScanDuration = now - lastStartScanTimeStamp;
1691        }
1692        lastStartScanTimeStamp = 0;
1693        if (DBG) {
1694            String ts = String.format("[%,d ms]", now);
1695            if (mScanWorkSource != null)
1696                loge(ts + " noteScanEnd " + mScanWorkSource.toString());
1697            else
1698                loge(ts + " noteScanEnd no scan source");
1699        }
1700        if (mScanWorkSource != null) {
1701            try {
1702                mBatteryStats.noteWifiScanStoppedFromSource(mScanWorkSource);
1703            } catch (RemoteException e) {
1704                log(e.toString());
1705            } finally {
1706                mScanWorkSource = null;
1707            }
1708        }
1709    }
1710
1711    private void noteBatchedScanStart() {
1712        if (PDBG) loge("noteBatchedScanstart()");
1713        // note the end of a previous scan set
1714        if (mNotedBatchedScanWorkSource != null &&
1715                (mNotedBatchedScanWorkSource.equals(mBatchedScanWorkSource) == false ||
1716                 mNotedBatchedScanCsph != mBatchedScanCsph)) {
1717            try {
1718                mBatteryStats.noteWifiBatchedScanStoppedFromSource(mNotedBatchedScanWorkSource);
1719            } catch (RemoteException e) {
1720                log(e.toString());
1721            } finally {
1722                mNotedBatchedScanWorkSource = null;
1723                mNotedBatchedScanCsph = 0;
1724            }
1725        }
1726        // note the start of the new
1727        try {
1728            mBatteryStats.noteWifiBatchedScanStartedFromSource(mBatchedScanWorkSource,
1729                    mBatchedScanCsph);
1730            mNotedBatchedScanWorkSource = mBatchedScanWorkSource;
1731            mNotedBatchedScanCsph = mBatchedScanCsph;
1732        } catch (RemoteException e) {
1733            log(e.toString());
1734        }
1735    }
1736
1737    private void noteBatchedScanStop() {
1738        if (PDBG) loge("noteBatchedScanstop()");
1739
1740        if (mNotedBatchedScanWorkSource != null) {
1741            try {
1742                mBatteryStats.noteWifiBatchedScanStoppedFromSource(mNotedBatchedScanWorkSource);
1743            } catch (RemoteException e) {
1744                log(e.toString());
1745            } finally {
1746                mNotedBatchedScanWorkSource = null;
1747                mNotedBatchedScanCsph = 0;
1748            }
1749        }
1750    }
1751
1752    private void handleScanRequest(int type, Message message) {
1753        // unbundle parameters
1754        Bundle bundle = (Bundle) message.obj;
1755        ScanSettings settings = bundle.getParcelable(CUSTOMIZED_SCAN_SETTING);
1756        WorkSource workSource = bundle.getParcelable(CUSTOMIZED_SCAN_WORKSOURCE);
1757
1758        // parse scan settings
1759        String freqs = null;
1760        if (settings != null && settings.channelSet != null) {
1761            StringBuilder sb = new StringBuilder();
1762            boolean first = true;
1763            for (WifiChannel channel : settings.channelSet) {
1764                if (!first) sb.append(','); else first = false;
1765                sb.append(channel.freqMHz);
1766            }
1767            freqs = sb.toString();
1768        }
1769
1770        // call wifi native to start the scan
1771        if (startScanNative(type, freqs)) {
1772            // only count battery consumption if scan request is accepted
1773            noteScanStart(message.arg1, workSource);
1774            // a full scan covers everything, clearing scan request buffer
1775            if (freqs == null)
1776                mBufferedScanMsg.clear();
1777            messageHandlingStatus = MESSAGE_HANDLING_STATUS_OK;
1778            return;
1779        }
1780
1781        // if reach here, scan request is rejected
1782
1783        if (!mIsScanOngoing) {
1784            // if rejection is NOT due to ongoing scan (e.g. bad scan parameters),
1785
1786            // discard this request and pop up the next one
1787            if (mBufferedScanMsg.size() > 0) {
1788                sendMessage(mBufferedScanMsg.remove());
1789            }
1790            messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
1791        } else if (!mIsFullScanOngoing) {
1792            // if rejection is due to an ongoing scan, and the ongoing one is NOT a full scan,
1793            // buffer the scan request to make sure specified channels will be scanned eventually
1794            if (freqs == null)
1795                mBufferedScanMsg.clear();
1796            if (mBufferedScanMsg.size() < SCAN_REQUEST_BUFFER_MAX_SIZE) {
1797                Message msg = obtainMessage(CMD_START_SCAN,
1798                        message.arg1, message.arg2, bundle);
1799                mBufferedScanMsg.add(msg);
1800            } else {
1801                // if too many requests in buffer, combine them into a single full scan
1802                bundle = new Bundle();
1803                bundle.putParcelable(CUSTOMIZED_SCAN_SETTING, null);
1804                bundle.putParcelable(CUSTOMIZED_SCAN_WORKSOURCE, workSource);
1805                Message msg = obtainMessage(CMD_START_SCAN, message.arg1, message.arg2, bundle);
1806                mBufferedScanMsg.clear();
1807                mBufferedScanMsg.add(msg);
1808            }
1809            messageHandlingStatus = MESSAGE_HANDLING_STATUS_LOOPED;
1810        } else {
1811            // mIsScanOngoing and mIsFullScanOngoing
1812            messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
1813        }
1814    }
1815
1816
1817    /** return true iff scan request is accepted */
1818    private boolean startScanNative(int type, String freqs) {
1819        if (mWifiNative.scan(type, freqs)) {
1820            mIsScanOngoing = true;
1821            mIsFullScanOngoing = (freqs == null);
1822            lastScanFreqs = freqs;
1823            return true;
1824        }
1825        return false;
1826    }
1827
1828    /**
1829     * TODO: doc
1830     */
1831    public void setSupplicantRunning(boolean enable) {
1832        if (enable) {
1833            sendMessage(CMD_START_SUPPLICANT);
1834        } else {
1835            sendMessage(CMD_STOP_SUPPLICANT);
1836        }
1837    }
1838
1839    /**
1840     * TODO: doc
1841     */
1842    public void setHostApRunning(WifiConfiguration wifiConfig, boolean enable) {
1843        if (enable) {
1844            sendMessage(CMD_START_AP, wifiConfig);
1845        } else {
1846            sendMessage(CMD_STOP_AP);
1847        }
1848    }
1849
1850    public void setWifiApConfiguration(WifiConfiguration config) {
1851        mWifiApConfigChannel.sendMessage(CMD_SET_AP_CONFIG, config);
1852    }
1853
1854    public WifiConfiguration syncGetWifiApConfiguration() {
1855        Message resultMsg = mWifiApConfigChannel.sendMessageSynchronously(CMD_REQUEST_AP_CONFIG);
1856        WifiConfiguration ret = (WifiConfiguration) resultMsg.obj;
1857        resultMsg.recycle();
1858        return ret;
1859    }
1860
1861    /**
1862     * TODO: doc
1863     */
1864    public int syncGetWifiState() {
1865        return mWifiState.get();
1866    }
1867
1868    /**
1869     * TODO: doc
1870     */
1871    public String syncGetWifiStateByName() {
1872        switch (mWifiState.get()) {
1873            case WIFI_STATE_DISABLING:
1874                return "disabling";
1875            case WIFI_STATE_DISABLED:
1876                return "disabled";
1877            case WIFI_STATE_ENABLING:
1878                return "enabling";
1879            case WIFI_STATE_ENABLED:
1880                return "enabled";
1881            case WIFI_STATE_UNKNOWN:
1882                return "unknown state";
1883            default:
1884                return "[invalid state]";
1885        }
1886    }
1887
1888    /**
1889     * TODO: doc
1890     */
1891    public int syncGetWifiApState() {
1892        return mWifiApState.get();
1893    }
1894
1895    /**
1896     * TODO: doc
1897     */
1898    public String syncGetWifiApStateByName() {
1899        switch (mWifiApState.get()) {
1900            case WIFI_AP_STATE_DISABLING:
1901                return "disabling";
1902            case WIFI_AP_STATE_DISABLED:
1903                return "disabled";
1904            case WIFI_AP_STATE_ENABLING:
1905                return "enabling";
1906            case WIFI_AP_STATE_ENABLED:
1907                return "enabled";
1908            case WIFI_AP_STATE_FAILED:
1909                return "failed";
1910            default:
1911                return "[invalid state]";
1912        }
1913    }
1914
1915    /**
1916     * Get status information for the current connection, if any.
1917     * @return a {@link WifiInfo} object containing information about the current connection
1918     *
1919     */
1920    public WifiInfo syncRequestConnectionInfo() {
1921        return mWifiInfo;
1922    }
1923
1924    public DhcpResults syncGetDhcpResults() {
1925        synchronized (mDhcpResultsLock) {
1926            return new DhcpResults(mDhcpResults);
1927        }
1928    }
1929
1930    /**
1931     * TODO: doc
1932     */
1933    public void setDriverStart(boolean enable) {
1934        if (enable) {
1935            sendMessage(CMD_START_DRIVER);
1936        } else {
1937            sendMessage(CMD_STOP_DRIVER);
1938        }
1939    }
1940
1941    /**
1942     * TODO: doc
1943     */
1944    public void setOperationalMode(int mode) {
1945        if (DBG) log("setting operational mode to " + String.valueOf(mode));
1946        sendMessage(CMD_SET_OPERATIONAL_MODE, mode, 0);
1947    }
1948
1949    /**
1950     * TODO: doc
1951     */
1952    public List<ScanResult> syncGetScanResultsList() {
1953        synchronized (mScanResultCache) {
1954            List<ScanResult> scanList = new ArrayList<ScanResult>();
1955            for(ScanResult result: mScanResults) {
1956                scanList.add(new ScanResult(result));
1957            }
1958            return scanList;
1959        }
1960    }
1961
1962    /**
1963     * Get unsynchronized pointer to scan result list
1964     * Can be called only from AutoJoinController which runs in the WifiStateMachine context
1965     */
1966    public List<ScanResult> getScanResultsListNoCopyUnsync() {
1967        return mScanResults;
1968    }
1969
1970    /**
1971     * Disconnect from Access Point
1972     */
1973    public void disconnectCommand() {
1974        sendMessage(CMD_DISCONNECT);
1975    }
1976
1977    /**
1978     * Initiate a reconnection to AP
1979     */
1980    public void reconnectCommand() {
1981        sendMessage(CMD_RECONNECT);
1982    }
1983
1984    /**
1985     * Initiate a re-association to AP
1986     */
1987    public void reassociateCommand() {
1988        sendMessage(CMD_REASSOCIATE);
1989    }
1990
1991    /**
1992     * Reload networks and then reconnect; helps load correct data for TLS networks
1993     */
1994
1995    public void reloadTlsNetworksAndReconnect() {
1996        sendMessage(CMD_RELOAD_TLS_AND_RECONNECT);
1997    }
1998
1999    /**
2000     * Add a network synchronously
2001     *
2002     * @return network id of the new network
2003     */
2004    public int syncAddOrUpdateNetwork(AsyncChannel channel, WifiConfiguration config) {
2005        Message resultMsg = channel.sendMessageSynchronously(CMD_ADD_OR_UPDATE_NETWORK, config);
2006        int result = resultMsg.arg1;
2007        resultMsg.recycle();
2008        return result;
2009    }
2010
2011    /**
2012     * Get configured networks synchronously
2013     * @param channel
2014     * @return
2015     */
2016
2017    public List<WifiConfiguration> syncGetConfiguredNetworks(int uuid, AsyncChannel channel) {
2018        Message resultMsg = channel.sendMessageSynchronously(CMD_GET_CONFIGURED_NETWORKS, uuid);
2019        List<WifiConfiguration> result = (List<WifiConfiguration>) resultMsg.obj;
2020        resultMsg.recycle();
2021        return result;
2022    }
2023
2024    public List<WifiConfiguration> syncGetPrivilegedConfiguredNetwork(AsyncChannel channel) {
2025        Message resultMsg = channel.sendMessageSynchronously(
2026                CMD_GET_PRIVILEGED_CONFIGURED_NETWORKS);
2027        List<WifiConfiguration> result = (List<WifiConfiguration>) resultMsg.obj;
2028        resultMsg.recycle();
2029        return result;
2030    }
2031
2032
2033    /**
2034     * Get connection statistics synchronously
2035     * @param channel
2036     * @return
2037     */
2038
2039    public WifiConnectionStatistics syncGetConnectionStatistics(AsyncChannel channel) {
2040        Message resultMsg = channel.sendMessageSynchronously(CMD_GET_CONNECTION_STATISTICS);
2041        WifiConnectionStatistics result = (WifiConnectionStatistics) resultMsg.obj;
2042        resultMsg.recycle();
2043        return result;
2044    }
2045
2046    /**
2047     * Get adaptors synchronously
2048     */
2049
2050    public int syncGetSupportedFeatures(AsyncChannel channel) {
2051        Message resultMsg = channel.sendMessageSynchronously(CMD_GET_SUPPORTED_FEATURES);
2052        int supportedFeatureSet = resultMsg.arg1;
2053        resultMsg.recycle();
2054        return supportedFeatureSet;
2055    }
2056
2057    /**
2058     * Get link layers stats for adapter synchronously
2059     */
2060    public WifiLinkLayerStats syncGetLinkLayerStats(AsyncChannel channel) {
2061        Message resultMsg = channel.sendMessageSynchronously(CMD_GET_LINK_LAYER_STATS);
2062        WifiLinkLayerStats result = (WifiLinkLayerStats) resultMsg.obj;
2063        resultMsg.recycle();
2064        return result;
2065    }
2066
2067    /**
2068     * Delete a network
2069     *
2070     * @param networkId id of the network to be removed
2071     */
2072    public boolean syncRemoveNetwork(AsyncChannel channel, int networkId) {
2073        Message resultMsg = channel.sendMessageSynchronously(CMD_REMOVE_NETWORK, networkId);
2074        boolean result = (resultMsg.arg1 != FAILURE);
2075        resultMsg.recycle();
2076        return result;
2077    }
2078
2079    /**
2080     * Enable a network
2081     *
2082     * @param netId network id of the network
2083     * @param disableOthers true, if all other networks have to be disabled
2084     * @return {@code true} if the operation succeeds, {@code false} otherwise
2085     */
2086    public boolean syncEnableNetwork(AsyncChannel channel, int netId, boolean disableOthers) {
2087        Message resultMsg = channel.sendMessageSynchronously(CMD_ENABLE_NETWORK, netId,
2088                disableOthers ? 1 : 0);
2089        boolean result = (resultMsg.arg1 != FAILURE);
2090        resultMsg.recycle();
2091        return result;
2092    }
2093
2094    /**
2095     * Disable a network
2096     *
2097     * @param netId network id of the network
2098     * @return {@code true} if the operation succeeds, {@code false} otherwise
2099     */
2100    public boolean syncDisableNetwork(AsyncChannel channel, int netId) {
2101        Message resultMsg = channel.sendMessageSynchronously(WifiManager.DISABLE_NETWORK, netId);
2102        boolean result = (resultMsg.arg1 != WifiManager.DISABLE_NETWORK_FAILED);
2103        resultMsg.recycle();
2104        return result;
2105    }
2106
2107    /**
2108     * Retrieves a WPS-NFC configuration token for the specified network
2109     * @return a hex string representation of the WPS-NFC configuration token
2110     */
2111    public String syncGetWpsNfcConfigurationToken(int netId) {
2112        return mWifiNative.getNfcWpsConfigurationToken(netId);
2113    }
2114
2115    /**
2116     * Blacklist a BSSID. This will avoid the AP if there are
2117     * alternate APs to connect
2118     *
2119     * @param bssid BSSID of the network
2120     */
2121    public void addToBlacklist(String bssid) {
2122        sendMessage(CMD_BLACKLIST_NETWORK, bssid);
2123    }
2124
2125    /**
2126     * Clear the blacklist list
2127     *
2128     */
2129    public void clearBlacklist() {
2130        sendMessage(CMD_CLEAR_BLACKLIST);
2131    }
2132
2133    public void enableRssiPolling(boolean enabled) {
2134       sendMessage(CMD_ENABLE_RSSI_POLL, enabled ? 1 : 0, 0);
2135    }
2136
2137    public void enableAllNetworks() {
2138        sendMessage(CMD_ENABLE_ALL_NETWORKS);
2139    }
2140
2141    /**
2142     * Start filtering Multicast v4 packets
2143     */
2144    public void startFilteringMulticastV4Packets() {
2145        mFilteringMulticastV4Packets.set(true);
2146        sendMessage(CMD_START_PACKET_FILTERING, MULTICAST_V4, 0);
2147    }
2148
2149    /**
2150     * Stop filtering Multicast v4 packets
2151     */
2152    public void stopFilteringMulticastV4Packets() {
2153        mFilteringMulticastV4Packets.set(false);
2154        sendMessage(CMD_STOP_PACKET_FILTERING, MULTICAST_V4, 0);
2155    }
2156
2157    /**
2158     * Start filtering Multicast v4 packets
2159     */
2160    public void startFilteringMulticastV6Packets() {
2161        sendMessage(CMD_START_PACKET_FILTERING, MULTICAST_V6, 0);
2162    }
2163
2164    /**
2165     * Stop filtering Multicast v4 packets
2166     */
2167    public void stopFilteringMulticastV6Packets() {
2168        sendMessage(CMD_STOP_PACKET_FILTERING, MULTICAST_V6, 0);
2169    }
2170
2171    /**
2172     * Set high performance mode of operation.
2173     * Enabling would set active power mode and disable suspend optimizations;
2174     * disabling would set auto power mode and enable suspend optimizations
2175     * @param enable true if enable, false otherwise
2176     */
2177    public void setHighPerfModeEnabled(boolean enable) {
2178        sendMessage(CMD_SET_HIGH_PERF_MODE, enable ? 1 : 0, 0);
2179    }
2180
2181    /**
2182     * Set the country code
2183     * @param countryCode following ISO 3166 format
2184     * @param persist {@code true} if the setting should be remembered.
2185     */
2186    public void setCountryCode(String countryCode, boolean persist) {
2187        // If it's a good country code, apply after the current
2188        // wifi connection is terminated; ignore resetting of code
2189        // for now (it is unclear what the chipset should do when
2190        // country code is reset)
2191        int countryCodeSequence = mCountryCodeSequence.incrementAndGet();
2192        if (TextUtils.isEmpty(countryCode)) {
2193            log("Ignoring resetting of country code");
2194        } else {
2195            sendMessage(CMD_SET_COUNTRY_CODE, countryCodeSequence, persist ? 1 : 0, countryCode);
2196        }
2197    }
2198
2199    /**
2200     * Set the operational frequency band
2201     * @param band
2202     * @param persist {@code true} if the setting should be remembered.
2203     */
2204    public void setFrequencyBand(int band, boolean persist) {
2205        if (persist) {
2206            Settings.Global.putInt(mContext.getContentResolver(),
2207                    Settings.Global.WIFI_FREQUENCY_BAND,
2208                    band);
2209        }
2210        sendMessage(CMD_SET_FREQUENCY_BAND, band, 0);
2211    }
2212
2213    /**
2214     * Enable TDLS for a specific MAC address
2215     */
2216    public void enableTdls(String remoteMacAddress, boolean enable) {
2217        int enabler = enable ? 1 : 0;
2218        sendMessage(CMD_ENABLE_TDLS, enabler, 0, remoteMacAddress);
2219    }
2220
2221    /**
2222     * Returns the operational frequency band
2223     */
2224    public int getFrequencyBand() {
2225        return mFrequencyBand.get();
2226    }
2227
2228    /**
2229     * Returns the wifi configuration file
2230     */
2231    public String getConfigFile() {
2232        return mWifiConfigStore.getConfigFile();
2233    }
2234
2235    /**
2236     * Send a message indicating bluetooth adapter connection state changed
2237     */
2238    public void sendBluetoothAdapterStateChange(int state) {
2239        sendMessage(CMD_BLUETOOTH_ADAPTER_STATE_CHANGE, state, 0);
2240    }
2241
2242    /**
2243     * Save configuration on supplicant
2244     *
2245     * @return {@code true} if the operation succeeds, {@code false} otherwise
2246     *
2247     * TODO: deprecate this
2248     */
2249    public boolean syncSaveConfig(AsyncChannel channel) {
2250        Message resultMsg = channel.sendMessageSynchronously(CMD_SAVE_CONFIG);
2251        boolean result = (resultMsg.arg1 != FAILURE);
2252        resultMsg.recycle();
2253        return result;
2254    }
2255
2256    public void updateBatteryWorkSource(WorkSource newSource) {
2257        synchronized (mRunningWifiUids) {
2258            try {
2259                if (newSource != null) {
2260                    mRunningWifiUids.set(newSource);
2261                }
2262                if (mIsRunning) {
2263                    if (mReportedRunning) {
2264                        // If the work source has changed since last time, need
2265                        // to remove old work from battery stats.
2266                        if (mLastRunningWifiUids.diff(mRunningWifiUids)) {
2267                            mBatteryStats.noteWifiRunningChanged(mLastRunningWifiUids,
2268                                    mRunningWifiUids);
2269                            mLastRunningWifiUids.set(mRunningWifiUids);
2270                        }
2271                    } else {
2272                        // Now being started, report it.
2273                        mBatteryStats.noteWifiRunning(mRunningWifiUids);
2274                        mLastRunningWifiUids.set(mRunningWifiUids);
2275                        mReportedRunning = true;
2276                    }
2277                } else {
2278                    if (mReportedRunning) {
2279                        // Last reported we were running, time to stop.
2280                        mBatteryStats.noteWifiStopped(mLastRunningWifiUids);
2281                        mLastRunningWifiUids.clear();
2282                        mReportedRunning = false;
2283                    }
2284                }
2285                mWakeLock.setWorkSource(newSource);
2286            } catch (RemoteException ignore) {
2287            }
2288        }
2289    }
2290
2291    @Override
2292    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2293        super.dump(fd, pw, args);
2294        mSupplicantStateTracker.dump(fd, pw, args);
2295        pw.println("mLinkProperties " + mLinkProperties);
2296        pw.println("mWifiInfo " + mWifiInfo);
2297        pw.println("mDhcpResults " + mDhcpResults);
2298        pw.println("mNetworkInfo " + mNetworkInfo);
2299        pw.println("mLastSignalLevel " + mLastSignalLevel);
2300        pw.println("mLastBssid " + mLastBssid);
2301        pw.println("mLastNetworkId " + mLastNetworkId);
2302        pw.println("mOperationalMode " + mOperationalMode);
2303        pw.println("mUserWantsSuspendOpt " + mUserWantsSuspendOpt);
2304        pw.println("mSuspendOptNeedsDisabled " + mSuspendOptNeedsDisabled);
2305        pw.println("Supplicant status " + mWifiNative.status(true));
2306        pw.println("mEnableBackgroundScan " + mEnableBackgroundScan);
2307        pw.println("mLastSetCountryCode " + mLastSetCountryCode);
2308        pw.println("mPersistedCountryCode " + mPersistedCountryCode);
2309        pw.println();
2310        mWifiConfigStore.dump(fd, pw, args);
2311    }
2312
2313    /*********************************************************
2314     * Internal private functions
2315     ********************************************************/
2316
2317    private void logStateAndMessage(Message message, String state) {
2318        messageHandlingStatus = 0;
2319        if (mLogMessages) {
2320            //long now = SystemClock.elapsedRealtimeNanos();
2321            //String ts = String.format("[%,d us]", now/1000);
2322
2323            loge( " " + state + " " + getLogRecString(message));
2324        }
2325    }
2326
2327    /**
2328     * Return the additional string to be logged by LogRec, default
2329     *
2330     * @param msg that was processed
2331     * @return information to be logged as a String
2332     */
2333    protected String getLogRecString(Message msg) {
2334        WifiConfiguration config;
2335        Long now;
2336        String report;
2337        StringBuilder sb = new StringBuilder();
2338        if (mScreenOn) {
2339            sb.append("!");
2340        }
2341        if (messageHandlingStatus != MESSAGE_HANDLING_STATUS_UNKNOWN) {
2342            sb.append("(").append(messageHandlingStatus).append(")");
2343        }
2344        sb.append(smToString(msg));
2345        if (msg.sendingUid > 0 && msg.sendingUid != Process.WIFI_UID) {
2346            sb.append(" uid=" + msg.sendingUid);
2347        }
2348        switch (msg.what) {
2349            case CMD_START_SCAN:
2350                now = System.currentTimeMillis();
2351                sb.append(" ");
2352                sb.append(Integer.toString(msg.arg1));
2353                sb.append(" ");
2354                sb.append(Integer.toString(msg.arg2));
2355                sb.append(" ic=");
2356                sb.append(Integer.toString(sScanAlarmIntentCount));
2357                if (msg.obj != null) {
2358                    Bundle bundle = (Bundle)msg.obj;
2359                    Long request = bundle.getLong(SCAN_REQUEST_TIME, 0);
2360                    if (request != 0) {
2361                        sb.append(" proc(ms):").append(now - request);
2362                    }
2363                }
2364                if (mIsScanOngoing) sb.append(" onGoing");
2365                if (mIsFullScanOngoing) sb.append(" full");
2366                if (lastStartScanTimeStamp != 0) {
2367                    sb.append(" started:").append(lastStartScanTimeStamp);
2368                    sb.append(",").append(now - lastStartScanTimeStamp);
2369                }
2370                if (lastScanDuration != 0) {
2371                    sb.append(" dur:").append(lastScanDuration);
2372                }
2373                sb.append(" rssi=").append(mWifiInfo.getRssi());
2374                sb.append(" f=").append(mWifiInfo.getFrequency());
2375                sb.append(" sc=").append(mWifiInfo.score);
2376                sb.append(" link=").append(mWifiInfo.getLinkSpeed());
2377                sb.append(String.format(" tx=%.1f,", mWifiInfo.txSuccessRate));
2378                sb.append(String.format(" %.1f,", mWifiInfo.txRetriesRate));
2379                sb.append(String.format(" %.1f ", mWifiInfo.txBadRate));
2380                sb.append(String.format(" rx=%.1f", mWifiInfo.rxSuccessRate));
2381                if (lastScanFreqs != null) {
2382                    sb.append(" list=").append(lastScanFreqs);
2383                } else {
2384                    sb.append(" fiv=").append(fullBandConnectedTimeIntervalMilli);
2385                }
2386                report = reportOnTime();
2387                if (report != null) {
2388                    sb.append(" ").append(report);
2389                }
2390                break;
2391            case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
2392                sb.append(" ");
2393                sb.append(Integer.toString(msg.arg1));
2394                sb.append(" ");
2395                sb.append(Integer.toString(msg.arg2));
2396                StateChangeResult stateChangeResult = (StateChangeResult) msg.obj;
2397                if (stateChangeResult != null) {
2398                    sb.append(stateChangeResult.toString());
2399                }
2400                break;
2401            case WifiManager.SAVE_NETWORK:
2402            case WifiStateMachine.CMD_AUTO_SAVE_NETWORK:
2403                sb.append(" ");
2404                sb.append(Integer.toString(msg.arg1));
2405                sb.append(" ");
2406                sb.append(Integer.toString(msg.arg2));
2407                if (lastSavedConfigurationAttempt != null) {
2408                    sb.append(" ").append(lastSavedConfigurationAttempt.configKey());
2409                    sb.append(" nid=").append(lastSavedConfigurationAttempt.networkId);
2410                    if (lastSavedConfigurationAttempt.hiddenSSID) {
2411                        sb.append(" hidden");
2412                    }
2413                    if (lastSavedConfigurationAttempt.preSharedKey != null
2414                            && !lastSavedConfigurationAttempt.preSharedKey.equals("*")) {
2415                        sb.append(" hasPSK");
2416                    }
2417                    if (lastSavedConfigurationAttempt.ephemeral) {
2418                        sb.append(" ephemeral");
2419                    }
2420                    if (lastSavedConfigurationAttempt.selfAdded) {
2421                        sb.append(" selfAdded");
2422                    }
2423                    sb.append(" cuid=").append(lastSavedConfigurationAttempt.creatorUid);
2424                    sb.append(" suid=").append(lastSavedConfigurationAttempt.lastUpdateUid);
2425                }
2426                break;
2427            case WifiManager.FORGET_NETWORK:
2428                sb.append(" ");
2429                sb.append(Integer.toString(msg.arg1));
2430                sb.append(" ");
2431                sb.append(Integer.toString(msg.arg2));
2432                if (lastForgetConfigurationAttempt != null) {
2433                    sb.append(" ").append(lastForgetConfigurationAttempt.configKey());
2434                    sb.append(" nid=").append(lastForgetConfigurationAttempt.networkId);
2435                    if (lastForgetConfigurationAttempt.hiddenSSID) {
2436                        sb.append(" hidden");
2437                    }
2438                    if (lastForgetConfigurationAttempt.preSharedKey != null) {
2439                        sb.append(" hasPSK");
2440                    }
2441                    if (lastForgetConfigurationAttempt.ephemeral) {
2442                        sb.append(" ephemeral");
2443                    }
2444                    if (lastForgetConfigurationAttempt.selfAdded) {
2445                        sb.append(" selfAdded");
2446                    }
2447                    sb.append(" cuid=").append(lastForgetConfigurationAttempt.creatorUid);
2448                    sb.append(" suid=").append(lastForgetConfigurationAttempt.lastUpdateUid);
2449                    sb.append(" ajst=").append(lastForgetConfigurationAttempt.autoJoinStatus);
2450                }
2451                break;
2452            case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
2453                sb.append(" ");
2454                sb.append(Integer.toString(msg.arg1));
2455                sb.append(" ");
2456                sb.append(Integer.toString(msg.arg2));
2457                String bssid = (String)msg.obj;
2458                if (bssid != null && bssid.length()>0) {
2459                    sb.append(" ");
2460                    sb.append(bssid);
2461                }
2462                sb.append(" blacklist=" + Boolean.toString(didBlackListBSSID));
2463                break;
2464            case WifiMonitor.SCAN_RESULTS_EVENT:
2465                sb.append(" ");
2466                sb.append(Integer.toString(msg.arg1));
2467                sb.append(" ");
2468                sb.append(Integer.toString(msg.arg2));
2469                if (mScanResults != null) {
2470                    sb.append(" found=");
2471                    sb.append(mScanResults.size());
2472                }
2473                sb.append(" known=").append(mNumScanResultsKnown);
2474                sb.append(" got=").append(mNumScanResultsReturned);
2475                if (lastScanDuration != 0) {
2476                    sb.append(" dur:").append(lastScanDuration);
2477                }
2478                if (mOnTime != 0) {
2479                    sb.append(" on:").append(mOnTimeThisScan).append(",").append(mOnTimeScan);
2480                    sb.append(",").append(mOnTime);
2481                }
2482                if (mTxTime != 0) {
2483                    sb.append(" tx:").append(mTxTimeThisScan).append(",").append(mTxTimeScan);
2484                    sb.append(",").append(mTxTime);
2485                }
2486                if (mRxTime != 0) {
2487                    sb.append(" rx:").append(mRxTimeThisScan).append(",").append(mRxTimeScan);
2488                    sb.append(",").append(mRxTime);
2489                }
2490                break;
2491            case WifiMonitor.NETWORK_CONNECTION_EVENT:
2492                sb.append(" ");
2493                sb.append(Integer.toString(msg.arg1));
2494                sb.append(" ");
2495                sb.append(Integer.toString(msg.arg2));
2496                sb.append(" ").append(mLastBssid);
2497                sb.append(" nid=").append(mLastNetworkId);
2498                config = getCurrentWifiConfiguration();
2499                if (config != null) {
2500                    sb.append(" ").append(config.configKey());
2501                }
2502                break;
2503            case CMD_TARGET_BSSID:
2504            case CMD_ASSOCIATED_BSSID:
2505                sb.append(" ");
2506                sb.append(Integer.toString(msg.arg1));
2507                sb.append(" ");
2508                sb.append(Integer.toString(msg.arg2));
2509                if (msg.obj != null) {
2510                    sb.append(" BSSID=").append((String)msg.obj);
2511                }
2512                if (mTargetRoamBSSID != null) {
2513                    sb.append(" Target=").append(mTargetRoamBSSID);
2514                }
2515                sb.append(" roam=").append(Integer.toString(mAutoRoaming));
2516                break;
2517            case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
2518                if (msg.obj != null) {
2519                    sb.append(" ").append((String)msg.obj);
2520                }
2521                sb.append(" nid=").append(msg.arg1);
2522                sb.append(" reason=").append(msg.arg2);
2523                if (mLastBssid != null) {
2524                    sb.append(" lastbssid=").append(mLastBssid);
2525                }
2526                if (mWifiInfo.getFrequency() != -1) {
2527                    sb.append(" freq=").append(mWifiInfo.getFrequency());
2528                    sb.append(" rssi=").append(mWifiInfo.getRssi());
2529                }
2530                if (linkDebouncing) {
2531                    sb.append(" debounce");
2532                }
2533                break;
2534            case WifiMonitor.SSID_TEMP_DISABLED:
2535            case WifiMonitor.SSID_REENABLED:
2536                sb.append(" nid=").append(msg.arg1);
2537                if (msg.obj != null) {
2538                    sb.append(" ").append((String)msg.obj);
2539                }
2540                config = getCurrentWifiConfiguration();
2541                if (config != null) {
2542                    sb.append(" cur=").append(config.configKey());
2543                    sb.append(" ajst=").append(config.autoJoinStatus);
2544                    if (config.selfAdded) {
2545                        sb.append(" selfAdded");
2546                    }
2547                    if (config.status != 0) {
2548                        sb.append(" st=").append(config.status);
2549                        sb.append(" rs=").append(config.disableReason);
2550                    }
2551                    if (config.lastConnected != 0) {
2552                        now = System.currentTimeMillis();
2553                        sb.append(" lastconn=").append(now - config.lastConnected).append("(ms)");
2554                    }
2555                    if (mLastBssid != null) {
2556                        sb.append(" lastbssid=").append(mLastBssid);
2557                    }
2558                    if (mWifiInfo.getFrequency() != -1) {
2559                        sb.append(" freq=").append(mWifiInfo.getFrequency());
2560                        sb.append(" rssi=").append(mWifiInfo.getRssi());
2561                        sb.append(" bssid=").append(mWifiInfo.getBSSID());
2562                    }
2563                }
2564                break;
2565            case CMD_RSSI_POLL:
2566            case CMD_UNWANTED_NETWORK:
2567            case WifiManager.RSSI_PKTCNT_FETCH:
2568                sb.append(" ");
2569                sb.append(Integer.toString(msg.arg1));
2570                sb.append(" ");
2571                sb.append(Integer.toString(msg.arg2));
2572                if (mWifiInfo.getSSID() != null)
2573                if (mWifiInfo.getSSID() != null)
2574                    sb.append(" ").append(mWifiInfo.getSSID());
2575                if (mWifiInfo.getBSSID() != null)
2576                    sb.append(" ").append(mWifiInfo.getBSSID());
2577                sb.append(" rssi=").append(mWifiInfo.getRssi());
2578                sb.append(" f=").append(mWifiInfo.getFrequency());
2579                sb.append(" sc=").append(mWifiInfo.score);
2580                sb.append(" link=").append(mWifiInfo.getLinkSpeed());
2581                sb.append(String.format(" tx=%.1f,", mWifiInfo.txSuccessRate));
2582                sb.append(String.format(" %.1f,", mWifiInfo.txRetriesRate));
2583                sb.append(String.format(" %.1f ", mWifiInfo.txBadRate));
2584                sb.append(String.format(" rx=%.1f", mWifiInfo.rxSuccessRate));
2585                report = reportOnTime();
2586                if (report != null) {
2587                    sb.append(" ").append(report);
2588                }
2589                if (wifiScoringReport != null) {
2590                    sb.append(wifiScoringReport);
2591                }
2592                break;
2593            case CMD_AUTO_CONNECT:
2594            case WifiManager.CONNECT_NETWORK:
2595                sb.append(" ");
2596                sb.append(Integer.toString(msg.arg1));
2597                sb.append(" ");
2598                sb.append(Integer.toString(msg.arg2));
2599                config = (WifiConfiguration) msg.obj;
2600                if (config != null) {
2601                    sb.append(" ").append(config.configKey());
2602                    if (config.visibility != null) {
2603                        sb.append(" [").append(config.visibility.num24);
2604                        sb.append(" ,").append(config.visibility.rssi24);
2605                        sb.append(" ;").append(config.visibility.num5);
2606                        sb.append(" ,").append(config.visibility.rssi5).append("]");
2607                    }
2608                }
2609                if (mTargetRoamBSSID != null) {
2610                    sb.append(" ").append(mTargetRoamBSSID);
2611                }
2612                sb.append(" roam=").append(Integer.toString(mAutoRoaming));
2613                break;
2614            case CMD_AUTO_ROAM:
2615                sb.append(" ");
2616                sb.append(Integer.toString(msg.arg1));
2617                sb.append(" ");
2618                sb.append(Integer.toString(msg.arg2));
2619                ScanResult result = (ScanResult)msg.obj;
2620                if (result != null) {
2621                    now = System.currentTimeMillis();
2622                    sb.append(" bssid=").append(result.BSSID);
2623                    sb.append(" rssi=").append(result.level);
2624                    sb.append(" freq=").append(result.frequency);
2625                    if (result.seen > 0 && result.seen < now) {
2626                        sb.append(" seen=").append(now - result.seen);
2627                    } else {
2628                        // Somehow the timestamp for this scan result is inconsistent
2629                        sb.append(" !seen=").append(result.seen);
2630                    }
2631                }
2632                if (mTargetRoamBSSID != null) {
2633                    sb.append(" ").append(mTargetRoamBSSID);
2634                }
2635                sb.append(" roam=").append(Integer.toString(mAutoRoaming));
2636                sb.append(" fail count=").append(Integer.toString(mRoamFailCount));
2637                break;
2638            case CMD_ADD_OR_UPDATE_NETWORK:
2639                sb.append(" ");
2640                sb.append(Integer.toString(msg.arg1));
2641                sb.append(" ");
2642                sb.append(Integer.toString(msg.arg2));
2643                if (msg.obj != null) {
2644                    config = (WifiConfiguration)msg.obj;
2645                    sb.append(" ").append(config.configKey());
2646                    sb.append(" prio=").append(config.priority);
2647                    sb.append(" status=").append(config.status);
2648                    if (config.BSSID != null) {
2649                        sb.append(" ").append(config.BSSID);
2650                    }
2651                    WifiConfiguration curConfig = getCurrentWifiConfiguration();
2652                    if (curConfig != null) {
2653                        if (curConfig.configKey().equals(config.configKey())) {
2654                            sb.append(" is current");
2655                        } else {
2656                            sb.append(" current=").append(curConfig.configKey());
2657                            sb.append(" prio=").append(curConfig.priority);
2658                            sb.append(" status=").append(curConfig.status);
2659                        }
2660                    }
2661                }
2662                break;
2663            case WifiManager.DISABLE_NETWORK:
2664            case CMD_ENABLE_NETWORK:
2665                sb.append(" ");
2666                sb.append(Integer.toString(msg.arg1));
2667                sb.append(" ");
2668                sb.append(Integer.toString(msg.arg2));
2669                String key = mWifiConfigStore.getLastSelectedConfiguration();
2670                if (key != null) {
2671                    sb.append(" last=").append(key);
2672                }
2673                config = mWifiConfigStore.getWifiConfiguration(msg.arg1);
2674                if (config != null && (key == null || !config.configKey().equals(key))) {
2675                    sb.append(" target=").append(key);
2676                }
2677                break;
2678            case CMD_GET_CONFIGURED_NETWORKS:
2679                sb.append(" ");
2680                sb.append(Integer.toString(msg.arg1));
2681                sb.append(" ");
2682                sb.append(Integer.toString(msg.arg2));
2683                sb.append(" num=").append(mWifiConfigStore.getConfiguredNetworksSize());
2684                break;
2685            case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
2686                sb.append(" ");
2687                sb.append(Integer.toString(msg.arg1));
2688                sb.append(" ");
2689                sb.append(Integer.toString(msg.arg2));
2690                sb.append(" txpkts=").append(mWifiInfo.txSuccess);
2691                sb.append(",").append(mWifiInfo.txBad);
2692                sb.append(",").append(mWifiInfo.txRetries);
2693                break;
2694            case DhcpStateMachine.CMD_POST_DHCP_ACTION:
2695                sb.append(" ");
2696                sb.append(Integer.toString(msg.arg1));
2697                sb.append(" ");
2698                sb.append(Integer.toString(msg.arg2));
2699                if (msg.arg1 == DhcpStateMachine.DHCP_SUCCESS) {
2700                    sb.append(" OK ");
2701                } else if (msg.arg1 == DhcpStateMachine.DHCP_FAILURE) {
2702                    sb.append(" FAIL ");
2703                }
2704                if (mLinkProperties != null) {
2705                    if (mLinkProperties.hasIPv4Address()) {
2706                        sb.append(" v4");
2707                    }
2708                    if (mLinkProperties.hasGlobalIPv6Address()) {
2709                        sb.append(" v6");
2710                    }
2711                    if (mLinkProperties.hasIPv4DefaultRoute()) {
2712                        sb.append(" v4r");
2713                    }
2714                    if (mLinkProperties.hasIPv6DefaultRoute()) {
2715                        sb.append(" v6r");
2716                    }
2717                    if (mLinkProperties.hasIPv4DnsServer()) {
2718                        sb.append(" v4dns");
2719                    }
2720                    if (mLinkProperties.hasIPv6DnsServer()) {
2721                        sb.append(" v6dns");
2722                    }
2723                }
2724                break;
2725            case WifiP2pServiceImpl.P2P_CONNECTION_CHANGED:
2726                sb.append(" ");
2727                sb.append(Integer.toString(msg.arg1));
2728                sb.append(" ");
2729                sb.append(Integer.toString(msg.arg2));
2730                if (msg.obj != null) {
2731                    NetworkInfo info = (NetworkInfo)msg.obj;
2732                    NetworkInfo.State state = info.getState();
2733                    NetworkInfo.DetailedState detailedState = info.getDetailedState();
2734                    if (state != null) {
2735                        sb.append(" st=").append(state);
2736                    }
2737                    if (detailedState != null) {
2738                        sb.append("/").append(detailedState);
2739                    }
2740                }
2741                break;
2742            case CMD_IP_CONFIGURATION_LOST:
2743                int count = -1;
2744                WifiConfiguration c = getCurrentWifiConfiguration();
2745                if (c != null) count = c.numIpConfigFailures;
2746                sb.append(" ");
2747                sb.append(Integer.toString(msg.arg1));
2748                sb.append(" ");
2749                sb.append(Integer.toString(msg.arg2));
2750                sb.append(" failures: ");
2751                sb.append(Integer.toString(count));
2752                sb.append("/");
2753                sb.append(Integer.toString(mWifiConfigStore.getMaxDhcpRetries()));
2754                if (mWifiInfo.getBSSID() != null) {
2755                    sb.append(" ").append(mWifiInfo.getBSSID());
2756                }
2757                if (c != null) {
2758                    if (c.scanResultCache != null) {
2759                        for (ScanResult r : c.scanResultCache.values()) {
2760                            if (r.BSSID.equals(mWifiInfo.getBSSID())) {
2761                                sb.append(" ipfail=").append(r.numIpConfigFailures);
2762                                sb.append(",st=").append(r.autoJoinStatus);
2763                            }
2764                        }
2765                    }
2766                    sb.append(" -> ajst=").append(c.autoJoinStatus);
2767                    sb.append(" ").append(c.disableReason);
2768                    sb.append(" txpkts=").append(mWifiInfo.txSuccess);
2769                    sb.append(",").append(mWifiInfo.txBad);
2770                    sb.append(",").append(mWifiInfo.txRetries);
2771                }
2772                break;
2773            case CMD_UPDATE_LINKPROPERTIES:
2774                sb.append(" ");
2775                sb.append(Integer.toString(msg.arg1));
2776                sb.append(" ");
2777                sb.append(Integer.toString(msg.arg2));
2778                if (mLinkProperties != null) {
2779                    if (mLinkProperties.hasIPv4Address()) {
2780                        sb.append(" v4");
2781                    }
2782                    if (mLinkProperties.hasGlobalIPv6Address()) {
2783                        sb.append(" v6");
2784                    }
2785                    if (mLinkProperties.hasIPv4DefaultRoute()) {
2786                        sb.append(" v4r");
2787                    }
2788                    if (mLinkProperties.hasIPv6DefaultRoute()) {
2789                        sb.append(" v6r");
2790                    }
2791                    if (mLinkProperties.hasIPv4DnsServer()) {
2792                        sb.append(" v4dns");
2793                    }
2794                    if (mLinkProperties.hasIPv6DnsServer()) {
2795                        sb.append(" v6dns");
2796                    }
2797                }
2798                break;
2799            case CMD_SET_COUNTRY_CODE:
2800                sb.append(" ");
2801                sb.append(Integer.toString(msg.arg1));
2802                sb.append(" ");
2803                sb.append(Integer.toString(msg.arg2));
2804                if (msg.obj != null) {
2805                    sb.append(" ").append((String)msg.obj);
2806                }
2807                break;
2808            case CMD_ROAM_WATCHDOG_TIMER:
2809                sb.append(" ");
2810                sb.append(Integer.toString(msg.arg1));
2811                sb.append(" ");
2812                sb.append(Integer.toString(msg.arg2));
2813                sb.append(" cur=").append(roamWatchdogCount);
2814                break;
2815            case CMD_DISCONNECTING_WATCHDOG_TIMER:
2816                sb.append(" ");
2817                sb.append(Integer.toString(msg.arg1));
2818                sb.append(" ");
2819                sb.append(Integer.toString(msg.arg2));
2820                sb.append(" cur=").append(disconnectingWatchdogCount);
2821                break;
2822            default:
2823                sb.append(" ");
2824                sb.append(Integer.toString(msg.arg1));
2825                sb.append(" ");
2826                sb.append(Integer.toString(msg.arg2));
2827                break;
2828        }
2829
2830        return sb.toString();
2831    }
2832
2833    private void handleScreenStateChanged(boolean screenOn, boolean startBackgroundScanIfNeeded) {
2834        mScreenOn = screenOn;
2835        if (PDBG) {
2836            loge(" handleScreenStateChanged Enter: screenOn=" + screenOn
2837                    + " mCurrentScanAlarmMs = " + Long.toString(mCurrentScanAlarmMs)
2838                    + " mUserWantsSuspendOpt=" + mUserWantsSuspendOpt
2839                    + " state " + getCurrentState().getName()
2840                    + " suppState:" + mSupplicantStateTracker.getSupplicantStateName());
2841        }
2842        enableRssiPolling(screenOn);
2843        if (screenOn) enableAllNetworks();
2844        if (mUserWantsSuspendOpt.get()) {
2845            if (screenOn) {
2846                sendMessage(CMD_SET_SUSPEND_OPT_ENABLED, 0, 0);
2847            } else {
2848                // Allow 2s for suspend optimizations to be set
2849                mSuspendWakeLock.acquire(2000);
2850                sendMessage(CMD_SET_SUSPEND_OPT_ENABLED, 1, 0);
2851            }
2852        }
2853        mScreenBroadcastReceived.set(true);
2854
2855        getWifiLinkLayerStats(false);
2856        mOnTimeScreenStateChange = mOnTime;
2857        lastScreenStateChangeTimeStamp = lastLinkLayerStatsUpdate;
2858
2859        if (screenOn) {
2860            clearBlacklist();
2861
2862            fullBandConnectedTimeIntervalMilli = mWifiConfigStore.associatedPartialScanPeriodMilli;
2863            // Start the scan alarm so as to enable autojoin
2864            if (getCurrentState() == mConnectedState
2865                    && mWifiConfigStore.enableAutoJoinScanWhenAssociated) {
2866                mCurrentScanAlarmMs = mWifiConfigStore.associatedPartialScanPeriodMilli;
2867                // Scan after 200ms
2868                setScanAlarm(true, 200);
2869            } else if (getCurrentState() == mDisconnectedState) {
2870                mCurrentScanAlarmMs = mDisconnectedScanPeriodMs;
2871                // Scan after 200ms
2872                setScanAlarm(true, 200);
2873            }
2874        } else {
2875            setScanAlarm(false, 0);
2876        }
2877
2878        if (mBackgroundScanSupported) {
2879            mEnableBackgroundScan = (screenOn == false);
2880        }
2881
2882        if (DBG) logd("backgroundScan enabled=" + mEnableBackgroundScan
2883                + " startBackgroundScanIfNeeded:" + startBackgroundScanIfNeeded);
2884
2885        if (startBackgroundScanIfNeeded) {
2886            mWifiNative.enableBackgroundScan(mEnableBackgroundScan);
2887        }
2888
2889        if (DBG) log("handleScreenStateChanged Exit: " + screenOn);
2890    }
2891
2892    private void checkAndSetConnectivityInstance() {
2893        if (mCm == null) {
2894            mCm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
2895        }
2896    }
2897
2898    private boolean startTethering(ArrayList<String> available) {
2899
2900        boolean wifiAvailable = false;
2901
2902        checkAndSetConnectivityInstance();
2903
2904        String[] wifiRegexs = mCm.getTetherableWifiRegexs();
2905
2906        for (String intf : available) {
2907            for (String regex : wifiRegexs) {
2908                if (intf.matches(regex)) {
2909
2910                    InterfaceConfiguration ifcg = null;
2911                    try {
2912                        ifcg = mNwService.getInterfaceConfig(intf);
2913                        if (ifcg != null) {
2914                            /* IP/netmask: 192.168.43.1/255.255.255.0 */
2915                            ifcg.setLinkAddress(new LinkAddress(
2916                                    NetworkUtils.numericToInetAddress("192.168.43.1"), 24));
2917                            ifcg.setInterfaceUp();
2918
2919                            mNwService.setInterfaceConfig(intf, ifcg);
2920                        }
2921                    } catch (Exception e) {
2922                        loge("Error configuring interface " + intf + ", :" + e);
2923                        return false;
2924                    }
2925
2926                    if(mCm.tether(intf) != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
2927                        loge("Error tethering on " + intf);
2928                        return false;
2929                    }
2930                    mTetherInterfaceName = intf;
2931                    return true;
2932                }
2933            }
2934        }
2935        // We found no interfaces to tether
2936        return false;
2937    }
2938
2939    private void stopTethering() {
2940
2941        checkAndSetConnectivityInstance();
2942
2943        /* Clear the interface config to allow dhcp correctly configure new
2944           ip settings */
2945        InterfaceConfiguration ifcg = null;
2946        try {
2947            ifcg = mNwService.getInterfaceConfig(mTetherInterfaceName);
2948            if (ifcg != null) {
2949                ifcg.setLinkAddress(
2950                        new LinkAddress(NetworkUtils.numericToInetAddress("0.0.0.0"), 0));
2951                mNwService.setInterfaceConfig(mTetherInterfaceName, ifcg);
2952            }
2953        } catch (Exception e) {
2954            loge("Error resetting interface " + mTetherInterfaceName + ", :" + e);
2955        }
2956
2957        if (mCm.untether(mTetherInterfaceName) != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
2958            loge("Untether initiate failed!");
2959        }
2960    }
2961
2962    private boolean isWifiTethered(ArrayList<String> active) {
2963
2964        checkAndSetConnectivityInstance();
2965
2966        String[] wifiRegexs = mCm.getTetherableWifiRegexs();
2967        for (String intf : active) {
2968            for (String regex : wifiRegexs) {
2969                if (intf.matches(regex)) {
2970                    return true;
2971                }
2972            }
2973        }
2974        // We found no interfaces that are tethered
2975        return false;
2976    }
2977
2978    /**
2979     * Set the country code from the system setting value, if any.
2980     */
2981    private void setCountryCode() {
2982        String countryCode = Settings.Global.getString(mContext.getContentResolver(),
2983                Settings.Global.WIFI_COUNTRY_CODE);
2984        if (countryCode != null && !countryCode.isEmpty()) {
2985            setCountryCode(countryCode, false);
2986        } else {
2987            //use driver default
2988        }
2989    }
2990
2991    /**
2992     * Set the frequency band from the system setting value, if any.
2993     */
2994    private void setFrequencyBand() {
2995        int band = Settings.Global.getInt(mContext.getContentResolver(),
2996                Settings.Global.WIFI_FREQUENCY_BAND, WifiManager.WIFI_FREQUENCY_BAND_AUTO);
2997        setFrequencyBand(band, false);
2998    }
2999
3000    private void setSuspendOptimizationsNative(int reason, boolean enabled) {
3001        if (DBG) {
3002            log("setSuspendOptimizationsNative: " + reason + " " + enabled
3003                    + " -want " + mUserWantsSuspendOpt.get()
3004                    + " stack:" + Thread.currentThread().getStackTrace()[2].getMethodName()
3005                    +" - "+ Thread.currentThread().getStackTrace()[3].getMethodName()
3006                    +" - "+ Thread.currentThread().getStackTrace()[4].getMethodName()
3007                    +" - "+ Thread.currentThread().getStackTrace()[5].getMethodName());
3008        }
3009        //mWifiNative.setSuspendOptimizations(enabled);
3010
3011        if (enabled) {
3012            mSuspendOptNeedsDisabled &= ~reason;
3013            /* None of dhcp, screen or highperf need it disabled and user wants it enabled */
3014            if (mSuspendOptNeedsDisabled == 0 && mUserWantsSuspendOpt.get()) {
3015                if (DBG) {
3016                    log("setSuspendOptimizationsNative do it " + reason + " " + enabled
3017                            + " stack:" + Thread.currentThread().getStackTrace()[2].getMethodName()
3018                            +" - "+ Thread.currentThread().getStackTrace()[3].getMethodName()
3019                            +" - "+ Thread.currentThread().getStackTrace()[4].getMethodName()
3020                            +" - "+ Thread.currentThread().getStackTrace()[5].getMethodName());
3021                }
3022                mWifiNative.setSuspendOptimizations(true);
3023            }
3024        } else {
3025            mSuspendOptNeedsDisabled |= reason;
3026            mWifiNative.setSuspendOptimizations(false);
3027        }
3028    }
3029
3030    private void setSuspendOptimizations(int reason, boolean enabled) {
3031        if (DBG) log("setSuspendOptimizations: " + reason + " " + enabled);
3032        if (enabled) {
3033            mSuspendOptNeedsDisabled &= ~reason;
3034        } else {
3035            mSuspendOptNeedsDisabled |= reason;
3036        }
3037        if (DBG) log("mSuspendOptNeedsDisabled " + mSuspendOptNeedsDisabled);
3038    }
3039
3040    private void setWifiState(int wifiState) {
3041        final int previousWifiState = mWifiState.get();
3042
3043        try {
3044            if (wifiState == WIFI_STATE_ENABLED) {
3045                mBatteryStats.noteWifiOn();
3046            } else if (wifiState == WIFI_STATE_DISABLED) {
3047                mBatteryStats.noteWifiOff();
3048            }
3049        } catch (RemoteException e) {
3050            loge("Failed to note battery stats in wifi");
3051        }
3052
3053        mWifiState.set(wifiState);
3054
3055        if (DBG) log("setWifiState: " + syncGetWifiStateByName());
3056
3057        final Intent intent = new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION);
3058        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
3059        intent.putExtra(WifiManager.EXTRA_WIFI_STATE, wifiState);
3060        intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_STATE, previousWifiState);
3061        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
3062    }
3063
3064    private void setWifiApState(int wifiApState) {
3065        final int previousWifiApState = mWifiApState.get();
3066
3067        try {
3068            if (wifiApState == WIFI_AP_STATE_ENABLED) {
3069                mBatteryStats.noteWifiOn();
3070            } else if (wifiApState == WIFI_AP_STATE_DISABLED) {
3071                mBatteryStats.noteWifiOff();
3072            }
3073        } catch (RemoteException e) {
3074            loge("Failed to note battery stats in wifi");
3075        }
3076
3077        // Update state
3078        mWifiApState.set(wifiApState);
3079
3080        if (DBG) log("setWifiApState: " + syncGetWifiApStateByName());
3081
3082        final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
3083        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
3084        intent.putExtra(WifiManager.EXTRA_WIFI_AP_STATE, wifiApState);
3085        intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_AP_STATE, previousWifiApState);
3086        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
3087    }
3088
3089    /*
3090    void ageOutScanResults(int age) {
3091        synchronized(mScanResultCache) {
3092            // Trim mScanResults, which prevent WifiStateMachine to return
3093            // obsolete scan results to queriers
3094            long now = System.CurrentTimeMillis();
3095            for (int i = 0; i < mScanResults.size(); i++) {
3096                ScanResult result = mScanResults.get(i);
3097                if ((result.seen > now || (now - result.seen) > age)) {
3098                    mScanResults.remove(i);
3099                }
3100            }
3101        }
3102    }*/
3103
3104    private static final String ID_STR = "id=";
3105    private static final String BSSID_STR = "bssid=";
3106    private static final String FREQ_STR = "freq=";
3107    private static final String LEVEL_STR = "level=";
3108    private static final String TSF_STR = "tsf=";
3109    private static final String FLAGS_STR = "flags=";
3110    private static final String SSID_STR = "ssid=";
3111    private static final String DELIMITER_STR = "====";
3112    private static final String END_STR = "####";
3113
3114    int emptyScanResultCount = 0;
3115
3116    /**
3117     * Format:
3118     *
3119     * id=1
3120     * bssid=68:7f:76:d7:1a:6e
3121     * freq=2412
3122     * level=-44
3123     * tsf=1344626243700342
3124     * flags=[WPA2-PSK-CCMP][WPS][ESS]
3125     * ssid=zfdy
3126     * ====
3127     * id=2
3128     * bssid=68:5f:74:d7:1a:6f
3129     * freq=5180
3130     * level=-73
3131     * tsf=1344626243700373
3132     * flags=[WPA2-PSK-CCMP][WPS][ESS]
3133     * ssid=zuby
3134     * ====
3135     */
3136    private void setScanResults() {
3137        mNumScanResultsKnown = 0;
3138        mNumScanResultsReturned = 0;
3139        String bssid = "";
3140        int level = 0;
3141        int freq = 0;
3142        long tsf = 0;
3143        String flags = "";
3144        WifiSsid wifiSsid = null;
3145        String scanResults;
3146        String tmpResults;
3147        StringBuffer scanResultsBuf = new StringBuffer();
3148        int sid = 0;
3149
3150        while (true) {
3151            tmpResults = mWifiNative.scanResults(sid);
3152            if (TextUtils.isEmpty(tmpResults)) break;
3153            scanResultsBuf.append(tmpResults);
3154            scanResultsBuf.append("\n");
3155            String[] lines = tmpResults.split("\n");
3156            sid = -1;
3157            for (int i=lines.length - 1; i >= 0; i--) {
3158                if (lines[i].startsWith(END_STR)) {
3159                    break;
3160                } else if (lines[i].startsWith(ID_STR)) {
3161                    try {
3162                        sid = Integer.parseInt(lines[i].substring(ID_STR.length())) + 1;
3163                    } catch (NumberFormatException e) {
3164                        // Nothing to do
3165                    }
3166                    break;
3167                }
3168            }
3169            if (sid == -1) break;
3170        }
3171
3172        // Age out scan results, we return all scan results found in the last 12 seconds,
3173        // and NOT all scan results since last scan.
3174        // ageOutScanResults(12000);
3175
3176        scanResults = scanResultsBuf.toString();
3177        if (TextUtils.isEmpty(scanResults)) {
3178            emptyScanResultCount++;
3179            if (emptyScanResultCount > 10) {
3180                // If we got too many empty scan results, the current scan cache is stale,
3181                // hence clear it.
3182                mScanResults = new ArrayList<ScanResult>();
3183            }
3184           return;
3185        }
3186
3187        emptyScanResultCount = 0;
3188
3189        // note that all these splits and substrings keep references to the original
3190        // huge string buffer while the amount we really want is generally pretty small
3191        // so make copies instead (one example b/11087956 wasted 400k of heap here).
3192        synchronized(mScanResultCache) {
3193            mScanResults = new ArrayList<ScanResult>();
3194            String[] lines = scanResults.split("\n");
3195            final int bssidStrLen = BSSID_STR.length();
3196            final int flagLen = FLAGS_STR.length();
3197
3198            for (String line : lines) {
3199                if (line.startsWith(BSSID_STR)) {
3200                    bssid = new String(line.getBytes(), bssidStrLen, line.length() - bssidStrLen);
3201                } else if (line.startsWith(FREQ_STR)) {
3202                    try {
3203                        freq = Integer.parseInt(line.substring(FREQ_STR.length()));
3204                    } catch (NumberFormatException e) {
3205                        freq = 0;
3206                    }
3207                } else if (line.startsWith(LEVEL_STR)) {
3208                    try {
3209                        level = Integer.parseInt(line.substring(LEVEL_STR.length()));
3210                        /* some implementations avoid negative values by adding 256
3211                         * so we need to adjust for that here.
3212                         */
3213                        if (level > 0) level -= 256;
3214                    } catch(NumberFormatException e) {
3215                        level = 0;
3216                    }
3217                } else if (line.startsWith(TSF_STR)) {
3218                    try {
3219                        tsf = Long.parseLong(line.substring(TSF_STR.length()));
3220                    } catch (NumberFormatException e) {
3221                        tsf = 0;
3222                    }
3223                } else if (line.startsWith(FLAGS_STR)) {
3224                    flags = new String(line.getBytes(), flagLen, line.length() - flagLen);
3225                } else if (line.startsWith(SSID_STR)) {
3226                    wifiSsid = WifiSsid.createFromAsciiEncoded(
3227                            line.substring(SSID_STR.length()));
3228                } else if (line.startsWith(DELIMITER_STR) || line.startsWith(END_STR)) {
3229                    if (bssid != null) {
3230                        String ssid = (wifiSsid != null) ? wifiSsid.toString() : WifiSsid.NONE;
3231                        String key = bssid + ssid;
3232                        ScanResult scanResult = mScanResultCache.get(key);
3233                        if (scanResult != null) {
3234                            // TODO: average the RSSI, instead of overwriting it
3235                            scanResult.level = level;
3236                            scanResult.wifiSsid = wifiSsid;
3237                            // Keep existing API
3238                            scanResult.SSID = (wifiSsid != null) ? wifiSsid.toString() :
3239                                    WifiSsid.NONE;
3240                            scanResult.capabilities = flags;
3241                            scanResult.frequency = freq;
3242                            scanResult.timestamp = tsf;
3243                            scanResult.seen = System.currentTimeMillis();
3244                        } else {
3245                            scanResult =
3246                                new ScanResult(
3247                                        wifiSsid, bssid, flags, level, freq, tsf);
3248                            scanResult.seen = System.currentTimeMillis();
3249                            mScanResultCache.put(key, scanResult);
3250                        }
3251                        mNumScanResultsReturned ++; // Keep track of how many scan results we got
3252                                                    // as part of this scan's processing
3253                        mScanResults.add(scanResult);
3254                    }
3255                    bssid = null;
3256                    level = 0;
3257                    freq = 0;
3258                    tsf = 0;
3259                    flags = "";
3260                    wifiSsid = null;
3261                }
3262            }
3263        }
3264        boolean attemptAutoJoin = true;
3265        SupplicantState state = mWifiInfo.getSupplicantState();
3266        if (getCurrentState() == mRoamingState
3267                || getCurrentState() == mObtainingIpState
3268                || getCurrentState() == mScanModeState
3269                || getCurrentState() == mDisconnectingState
3270                || (getCurrentState() == mConnectedState
3271                && !mWifiConfigStore.enableAutoJoinWhenAssociated)
3272                || linkDebouncing
3273                || state == SupplicantState.ASSOCIATING
3274                || state == SupplicantState.AUTHENTICATING
3275                || state == SupplicantState.FOUR_WAY_HANDSHAKE
3276                || state == SupplicantState.GROUP_HANDSHAKE) {
3277            // Dont attempt auto-joining again while we are already attempting to join
3278            // and/or obtaining Ip address
3279            attemptAutoJoin = false;
3280        }
3281        if (DBG) {
3282            loge("wifi setScanResults state" + getCurrentState()
3283                    + " sup_state=" + state
3284                    + " debouncing=" + linkDebouncing);
3285        }
3286        if (attemptAutoJoin) {
3287            messageHandlingStatus = MESSAGE_HANDLING_STATUS_PROCESSED;
3288        }
3289        // Loose last selected configuration if we have been disconnected for 30 minutes
3290        if (getDisconnectedTimeMilli() > 1000 * 60 * 30) {
3291            mWifiConfigStore.setLastSelectedConfiguration(WifiConfiguration.INVALID_NETWORK_ID);
3292        }
3293
3294        if (mWifiConfigStore.enableAutoJoinWhenAssociated) {
3295            synchronized(mScanResultCache) {
3296                // AutoJoincontroller will directly acces the scan result list and update it with
3297                // ScanResult status
3298                mNumScanResultsKnown = mWifiAutoJoinController.newSupplicantResults(attemptAutoJoin);
3299            }
3300        }
3301        if (linkDebouncing) {
3302            // If debouncing, we dont re-select a SSID or BSSID hence
3303            // there is no need to call the network selection code
3304            // in WifiAutoJoinController, instead,
3305            // just try to reconnect to the same SSID by triggering a roam
3306            sendMessage(CMD_AUTO_ROAM, mLastNetworkId, 1, null);
3307        }
3308    }
3309
3310    /*
3311     * Fetch RSSI, linkspeed, and frequency on current connection
3312     */
3313    private void fetchRssiLinkSpeedAndFrequencyNative() {
3314        int newRssi = -1;
3315        int newLinkSpeed = -1;
3316        int newFrequency = -1;
3317
3318        String signalPoll = mWifiNative.signalPoll();
3319
3320        if (signalPoll != null) {
3321            String[] lines = signalPoll.split("\n");
3322            for (String line : lines) {
3323                String[] prop = line.split("=");
3324                if (prop.length < 2) continue;
3325                try {
3326                    if (prop[0].equals("RSSI")) {
3327                        newRssi = Integer.parseInt(prop[1]);
3328                    } else if (prop[0].equals("LINKSPEED")) {
3329                        newLinkSpeed = Integer.parseInt(prop[1]);
3330                    } else if (prop[0].equals("FREQUENCY")) {
3331                        newFrequency = Integer.parseInt(prop[1]);
3332                    }
3333                } catch (NumberFormatException e) {
3334                    //Ignore, defaults on rssi and linkspeed are assigned
3335                }
3336            }
3337        }
3338
3339        if (PDBG) {
3340            loge("fetchRssiLinkSpeedAndFrequencyNative rssi="
3341                    + Integer.toString(newRssi) + " linkspeed="
3342                    + Integer.toString(newLinkSpeed));
3343        }
3344
3345        if (newRssi > WifiInfo.INVALID_RSSI && newRssi < WifiInfo.MAX_RSSI) {
3346        // screen out invalid values
3347            /* some implementations avoid negative values by adding 256
3348             * so we need to adjust for that here.
3349             */
3350            if (newRssi > 0) newRssi -= 256;
3351            mWifiInfo.setRssi(newRssi);
3352            /*
3353             * Rather then sending the raw RSSI out every time it
3354             * changes, we precalculate the signal level that would
3355             * be displayed in the status bar, and only send the
3356             * broadcast if that much more coarse-grained number
3357             * changes. This cuts down greatly on the number of
3358             * broadcasts, at the cost of not informing others
3359             * interested in RSSI of all the changes in signal
3360             * level.
3361             */
3362            int newSignalLevel = WifiManager.calculateSignalLevel(newRssi, WifiManager.RSSI_LEVELS);
3363            if (newSignalLevel != mLastSignalLevel) {
3364                sendRssiChangeBroadcast(newRssi);
3365            }
3366            mLastSignalLevel = newSignalLevel;
3367        } else {
3368            mWifiInfo.setRssi(WifiInfo.INVALID_RSSI);
3369        }
3370
3371        if (newLinkSpeed != -1) {
3372            mWifiInfo.setLinkSpeed(newLinkSpeed);
3373        }
3374        if (newFrequency > 0) {
3375            if (ScanResult.is5GHz(newFrequency)) {
3376                mWifiConnectionStatistics.num5GhzConnected++;
3377            }
3378            if (ScanResult.is24GHz(newFrequency)) {
3379                mWifiConnectionStatistics.num24GhzConnected++;
3380            }
3381            mWifiInfo.setFrequency(newFrequency);
3382        }
3383        mWifiConfigStore.updateConfiguration(mWifiInfo);
3384    }
3385
3386    /**
3387     *  Determine if we need to switch network:
3388     * - the delta determine the urgency to switch and/or or the expected evilness of the disruption
3389     * - match the uregncy of the switch versus the packet usage at the interface
3390     */
3391    boolean shouldSwitchNetwork(int networkDelta) {
3392        int delta;
3393        if (networkDelta <= 0) {
3394            return false;
3395        }
3396        delta = networkDelta;
3397        if (mWifiInfo != null) {
3398            if (!mWifiConfigStore.enableAutoJoinWhenAssociated
3399                    && mWifiInfo.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID) {
3400                // If AutoJoin while associated is not enabled,
3401                // we should never switch network when already associated
3402                delta = -1000;
3403            } else {
3404                // TODO: Look at per AC packet count, do not switch if VO/VI traffic is present
3405                // TODO: at the interface. We should also discriminate between ucast and mcast,
3406                // TODO: since the rxSuccessRate include all the bonjour and Ipv6
3407                // TODO: broadcasts
3408                if ((mWifiInfo.txSuccessRate > 20) || (mWifiInfo.rxSuccessRate > 80)) {
3409                    delta -= 999;
3410                } else if ((mWifiInfo.txSuccessRate > 5) || (mWifiInfo.rxSuccessRate > 30)) {
3411                    delta -= 6;
3412                }
3413                loge("WifiStateMachine shouldSwitchNetwork "
3414                        + " txSuccessRate=" + String.format("%.2f", mWifiInfo.txSuccessRate)
3415                        + " rxSuccessRate=" + String.format("%.2f", mWifiInfo.rxSuccessRate)
3416                        + " delta " + networkDelta + " -> " + delta);
3417            }
3418        } else {
3419            loge("WifiStateMachine shouldSwitchNetwork "
3420                    + " delta " + networkDelta + " -> " + delta);
3421        }
3422        if (delta > 0) {
3423            return true;
3424        }
3425        return false;
3426    }
3427
3428    // Polling has completed, hence we wont have a score anymore
3429    private void cleanWifiScore() {
3430        mWifiInfo.txBadRate = 0;
3431        mWifiInfo.txSuccessRate = 0;
3432        mWifiInfo.txRetriesRate = 0;
3433        mWifiInfo.rxSuccessRate = 0;
3434    }
3435
3436    int mBadLinkspeedcount = 0;
3437
3438    // For debug, provide information about the last scoring operation
3439    String wifiScoringReport = null;
3440    private void calculateWifiScore(WifiLinkLayerStats stats) {
3441        StringBuilder sb = new StringBuilder();
3442        if (stats == null || mWifiLinkLayerStatsSupported <= 0) {
3443            long mTxPkts = TrafficStats.getTxPackets(mInterfaceName);
3444            long mRxPkts = TrafficStats.getRxPackets(mInterfaceName);
3445            mWifiInfo.updatePacketRates(mTxPkts, mRxPkts);
3446        } else {
3447            sb.append(" stats");
3448            mWifiInfo.updatePacketRates(stats);
3449        }
3450        int score = 56; // Starting score, temporarily hardcoded in between 50 and 60
3451        boolean isBadLinkspeed = (mWifiInfo.is24GHz()
3452                && mWifiInfo.getLinkSpeed() < mWifiConfigStore.badLinkSpeed24)
3453                || (mWifiInfo.is5GHz() && mWifiInfo.getLinkSpeed()
3454                < mWifiConfigStore.badLinkSpeed5);
3455        boolean isGoodLinkspeed = (mWifiInfo.is24GHz()
3456                && mWifiInfo.getLinkSpeed() >= mWifiConfigStore.goodLinkSpeed24)
3457                || (mWifiInfo.is5GHz() && mWifiInfo.getLinkSpeed()
3458                >= mWifiConfigStore.goodLinkSpeed5);
3459
3460        if (isBadLinkspeed) {
3461            if (mBadLinkspeedcount < 6)
3462                mBadLinkspeedcount++;
3463        } else {
3464            if (mBadLinkspeedcount > 0)
3465                mBadLinkspeedcount--;
3466        }
3467
3468        if (isBadLinkspeed) sb.append(" bl(").append(mBadLinkspeedcount).append(")");
3469        if (isGoodLinkspeed) sb.append(" gl");
3470
3471        /**
3472         * We want to make sure that we use the 24GHz RSSI thresholds if
3473         * there are 2.4GHz scan results
3474         * otherwise we end up lowering the score based on 5GHz values
3475         * which may cause a switch to LTE before roaming has a chance to try 2.4GHz
3476         * We also might unblacklist the configuation based on 2.4GHz
3477         * thresholds but joining 5GHz anyhow, and failing over to 2.4GHz because 5GHz is not good
3478         */
3479        boolean use24Thresholds = false;
3480        boolean homeNetworkBoost = false;
3481        WifiConfiguration currentConfiguration = getCurrentWifiConfiguration();
3482        if (currentConfiguration != null
3483                && currentConfiguration.scanResultCache != null) {
3484            currentConfiguration.setVisibility(12000);
3485            if (currentConfiguration.visibility != null) {
3486                if (currentConfiguration.visibility.rssi24 != WifiConfiguration.INVALID_RSSI
3487                        && currentConfiguration.visibility.rssi24
3488                        >= (currentConfiguration.visibility.rssi5-2)) {
3489                    use24Thresholds = true;
3490                }
3491            }
3492            if (currentConfiguration.scanResultCache.size() <= 6
3493                && currentConfiguration.allowedKeyManagement.cardinality() == 1
3494                && currentConfiguration.allowedKeyManagement.
3495                    get(WifiConfiguration.KeyMgmt.WPA_PSK) == true) {
3496                // A PSK network with less than 6 known BSSIDs
3497                // This is most likely a home network and thus we want to stick to wifi more
3498                homeNetworkBoost = true;
3499            }
3500        }
3501        if (homeNetworkBoost) sb.append(" hn");
3502        if (use24Thresholds) sb.append(" u24");
3503
3504        int rssi = mWifiInfo.getRssi() - 6 * mAggressiveHandover
3505                + (homeNetworkBoost ? WifiConfiguration.HOME_NETWORK_RSSI_BOOST : 0);
3506        sb.append(String.format(" rssi=%d ag=%d", rssi, mAggressiveHandover));
3507
3508        boolean is24GHz = use24Thresholds || mWifiInfo.is24GHz();
3509
3510        boolean isBadRSSI = (is24GHz && rssi < mWifiConfigStore.thresholdBadRssi24)
3511                || (!is24GHz && rssi < mWifiConfigStore.thresholdBadRssi5);
3512        boolean isLowRSSI = (is24GHz && rssi < mWifiConfigStore.thresholdLowRssi24)
3513                || (!is24GHz && mWifiInfo.getRssi() < mWifiConfigStore.thresholdLowRssi5);
3514        boolean isHighRSSI = (is24GHz && rssi >= mWifiConfigStore.thresholdGoodRssi24)
3515                || (!is24GHz && mWifiInfo.getRssi() >= mWifiConfigStore.thresholdGoodRssi5);
3516
3517        if (isBadRSSI) sb.append(" br");
3518        if (isLowRSSI) sb.append(" lr");
3519        if (isHighRSSI) sb.append(" hr");
3520
3521        int penalizedDueToUserTriggeredDisconnect = 0;        // For debug information
3522        if (currentConfiguration!= null &&
3523                (mWifiInfo.txSuccessRate > 5 || mWifiInfo.rxSuccessRate > 5)) {
3524            if (isBadRSSI) {
3525                currentConfiguration.numTicksAtBadRSSI++;
3526                if (currentConfiguration.numTicksAtBadRSSI > 1000) {
3527                    // We remained associated for a compound amount of time while passing
3528                    // traffic, hence loose the corresponding user triggered disabled stats
3529                    if (currentConfiguration.numUserTriggeredWifiDisableBadRSSI > 0) {
3530                        currentConfiguration.numUserTriggeredWifiDisableBadRSSI--;
3531                    }
3532                    if (currentConfiguration.numUserTriggeredWifiDisableLowRSSI > 0) {
3533                        currentConfiguration.numUserTriggeredWifiDisableLowRSSI--;
3534                    }
3535                    if (currentConfiguration.numUserTriggeredWifiDisableNotHighRSSI > 0) {
3536                        currentConfiguration.numUserTriggeredWifiDisableNotHighRSSI--;
3537                    }
3538                    currentConfiguration.numTicksAtBadRSSI = 0;
3539                }
3540                if (mWifiConfigStore.enableWifiCellularHandoverUserTriggeredAdjustment &&
3541                        (currentConfiguration.numUserTriggeredWifiDisableBadRSSI > 0
3542                        || currentConfiguration.numUserTriggeredWifiDisableLowRSSI > 0
3543                        || currentConfiguration.numUserTriggeredWifiDisableNotHighRSSI > 0)) {
3544                    score = score -5;
3545                    penalizedDueToUserTriggeredDisconnect = 1;
3546                    sb.append(" p1");
3547                }
3548            } else if (isLowRSSI) {
3549                currentConfiguration.numTicksAtLowRSSI++;
3550                if (currentConfiguration.numTicksAtLowRSSI > 1000) {
3551                    // We remained associated for a compound amount of time while passing
3552                    // traffic, hence loose the corresponding user triggered disabled stats
3553                    if (currentConfiguration.numUserTriggeredWifiDisableLowRSSI > 0) {
3554                        currentConfiguration.numUserTriggeredWifiDisableLowRSSI--;
3555                    }
3556                    if (currentConfiguration.numUserTriggeredWifiDisableNotHighRSSI > 0) {
3557                        currentConfiguration.numUserTriggeredWifiDisableNotHighRSSI--;
3558                    }
3559                    currentConfiguration.numTicksAtLowRSSI = 0;
3560                }
3561                if (mWifiConfigStore.enableWifiCellularHandoverUserTriggeredAdjustment &&
3562                        (currentConfiguration.numUserTriggeredWifiDisableLowRSSI > 0
3563                        || currentConfiguration.numUserTriggeredWifiDisableNotHighRSSI > 0)) {
3564                    score = score -5;
3565                    penalizedDueToUserTriggeredDisconnect = 2;
3566                    sb.append(" p2");
3567                }
3568            } else if (!isHighRSSI) {
3569                currentConfiguration.numTicksAtNotHighRSSI++;
3570                if (currentConfiguration.numTicksAtNotHighRSSI > 1000) {
3571                    // We remained associated for a compound amount of time while passing
3572                    // traffic, hence loose the corresponding user triggered disabled stats
3573                    if (currentConfiguration.numUserTriggeredWifiDisableNotHighRSSI > 0) {
3574                        currentConfiguration.numUserTriggeredWifiDisableNotHighRSSI--;
3575                    }
3576                    currentConfiguration.numTicksAtNotHighRSSI = 0;
3577                }
3578                if (mWifiConfigStore.enableWifiCellularHandoverUserTriggeredAdjustment &&
3579                        currentConfiguration.numUserTriggeredWifiDisableNotHighRSSI > 0) {
3580                    score = score -5;
3581                    penalizedDueToUserTriggeredDisconnect = 3;
3582                    sb.append(" p3");
3583                }
3584            }
3585            sb.append(String.format(" ticks %d,%d,%d", currentConfiguration.numTicksAtBadRSSI,
3586                    currentConfiguration.numTicksAtLowRSSI,
3587                    currentConfiguration.numTicksAtNotHighRSSI));
3588        }
3589
3590        if (PDBG) {
3591            String rssiStatus = "";
3592            if (isBadRSSI) rssiStatus += " badRSSI ";
3593            else if (isHighRSSI) rssiStatus += " highRSSI ";
3594            else if (isLowRSSI) rssiStatus += " lowRSSI ";
3595            if (isBadLinkspeed) rssiStatus += " lowSpeed ";
3596            loge("calculateWifiScore freq=" + Integer.toString(mWifiInfo.getFrequency())
3597                            + " speed=" + Integer.toString(mWifiInfo.getLinkSpeed())
3598                            + " score=" + Integer.toString(mWifiInfo.score)
3599                            + rssiStatus
3600                            + " -> txbadrate=" + String.format( "%.2f", mWifiInfo.txBadRate )
3601                            + " txgoodrate=" + String.format("%.2f", mWifiInfo.txSuccessRate)
3602                            + " txretriesrate=" + String.format("%.2f", mWifiInfo.txRetriesRate)
3603                            + " rxrate=" + String.format("%.2f", mWifiInfo.rxSuccessRate)
3604                            + " userTriggerdPenalty" + penalizedDueToUserTriggeredDisconnect);
3605        }
3606
3607        if ((mWifiInfo.txBadRate >= 1) && (mWifiInfo.txSuccessRate < 3)
3608                && (isBadRSSI || isLowRSSI)) {
3609            // Link is stuck
3610            if (mWifiInfo.linkStuckCount < 5)
3611                mWifiInfo.linkStuckCount += 1;
3612            sb.append(String.format(" ls+=%d", mWifiInfo.linkStuckCount));
3613            if (PDBG) loge(" bad link -> stuck count ="
3614                    + Integer.toString(mWifiInfo.linkStuckCount));
3615        } else if (mWifiInfo.txSuccessRate > 2 || mWifiInfo.txBadRate < 0.1) {
3616            if (mWifiInfo.linkStuckCount > 0)
3617                mWifiInfo.linkStuckCount -= 1;
3618            sb.append(String.format(" ls-=%d", mWifiInfo.linkStuckCount));
3619            if (PDBG) loge(" good link -> stuck count ="
3620                    + Integer.toString(mWifiInfo.linkStuckCount));
3621        }
3622
3623        sb.append(String.format(" [%d", score));
3624
3625        if (mWifiInfo.linkStuckCount > 1) {
3626            // Once link gets stuck for more than 3 seconds, start reducing the score
3627            score = score - 2 * (mWifiInfo.linkStuckCount - 1);
3628        }
3629        sb.append(String.format(",%d", score));
3630
3631        if (isBadLinkspeed) {
3632            score -= 4 ;
3633            if (PDBG) {
3634                loge(" isBadLinkspeed   ---> count=" + mBadLinkspeedcount
3635                        + " score=" + Integer.toString(score));
3636            }
3637        } else if ((isGoodLinkspeed) && (mWifiInfo.txSuccessRate > 5)) {
3638            score += 4; // So as bad rssi alone dont kill us
3639        }
3640        sb.append(String.format(",%d", score));
3641
3642        if (isBadRSSI) {
3643            if (mWifiInfo.badRssiCount < 7)
3644                mWifiInfo.badRssiCount += 1;
3645        } else if (isLowRSSI) {
3646            mWifiInfo.lowRssiCount = 1; // Dont increment the lowRssi count above 1
3647            if (mWifiInfo.badRssiCount > 0) {
3648                // Decrement bad Rssi count
3649                mWifiInfo.badRssiCount -= 1;
3650            }
3651        } else {
3652            mWifiInfo.badRssiCount = 0;
3653            mWifiInfo.lowRssiCount = 0;
3654        }
3655
3656        score -= mWifiInfo.badRssiCount * 2 +  mWifiInfo.lowRssiCount ;
3657        sb.append(String.format(",%d", score));
3658
3659        if (PDBG) loge(" badRSSI count" + Integer.toString(mWifiInfo.badRssiCount)
3660                     + " lowRSSI count" + Integer.toString(mWifiInfo.lowRssiCount)
3661                        + " --> score " + Integer.toString(score));
3662
3663
3664        if (isHighRSSI) {
3665            score += 5;
3666            if (PDBG) loge(" isHighRSSI       ---> score=" + Integer.toString(score));
3667        }
3668        sb.append(String.format(",%d]", score));
3669
3670        sb.append(String.format(" brc=%d lrc=%d", mWifiInfo.badRssiCount, mWifiInfo.lowRssiCount));
3671
3672        //sanitize boundaries
3673        if (score > NetworkAgent.WIFI_BASE_SCORE)
3674            score = NetworkAgent.WIFI_BASE_SCORE;
3675        if (score < 0)
3676            score = 0;
3677
3678        //report score
3679        if (score != mWifiInfo.score) {
3680            if (DBG) {
3681                loge("calculateWifiScore() report new score " + Integer.toString(score));
3682            }
3683            mWifiInfo.score = score;
3684            if (mNetworkAgent != null) {
3685                mNetworkAgent.sendNetworkScore(score);
3686            }
3687        }
3688        wifiScoringReport = sb.toString();
3689    }
3690
3691    public double getTxPacketRate() {
3692        if (mWifiInfo != null) {
3693            return mWifiInfo.txSuccessRate;
3694        }
3695        return -1;
3696    }
3697
3698    public double getRxPacketRate() {
3699        if (mWifiInfo != null) {
3700            return mWifiInfo.rxSuccessRate;
3701        }
3702        return -1;
3703    }
3704
3705    /**
3706     * Fetch TX packet counters on current connection
3707     */
3708    private void fetchPktcntNative(RssiPacketCountInfo info) {
3709        String pktcntPoll = mWifiNative.pktcntPoll();
3710
3711        if (pktcntPoll != null) {
3712            String[] lines = pktcntPoll.split("\n");
3713            for (String line : lines) {
3714                String[] prop = line.split("=");
3715                if (prop.length < 2) continue;
3716                try {
3717                    if (prop[0].equals("TXGOOD")) {
3718                        info.txgood = Integer.parseInt(prop[1]);
3719                    } else if (prop[0].equals("TXBAD")) {
3720                        info.txbad = Integer.parseInt(prop[1]);
3721                    }
3722                } catch (NumberFormatException e) {
3723                    // Ignore
3724                }
3725            }
3726        }
3727    }
3728
3729    private boolean clearIPv4Address(String iface) {
3730        try {
3731            InterfaceConfiguration ifcg = new InterfaceConfiguration();
3732            ifcg.setLinkAddress(new LinkAddress("0.0.0.0/0"));
3733            mNwService.setInterfaceConfig(iface, ifcg);
3734            return true;
3735        } catch (RemoteException e) {
3736            return false;
3737        }
3738    }
3739
3740    private boolean isProvisioned(LinkProperties lp) {
3741        // LinkProperties#isProvisioned returns true even if all we have is an IPv4 address and no
3742        // connectivity. This turns out not to be very useful, because we can't distinguish it from
3743        // a state where we have an IPv4 address assigned to the interface but are still running
3744        // DHCP.
3745        // TODO: Fix LinkProperties and remove this function.
3746        if (mWifiConfigStore.isUsingStaticIp(mLastNetworkId)) {
3747            return lp.hasIPv4Address();
3748        } else {
3749            return (lp.hasIPv4Address() && lp.hasIPv4DefaultRoute() && lp.hasIPv4DnsServer()) ||
3750                   (lp.hasGlobalIPv6Address() && lp.hasIPv6DefaultRoute() && lp.hasIPv6DnsServer());
3751        }
3752    }
3753
3754    /**
3755     * Updates mLinkProperties by merging information from various sources.
3756     *
3757     * This is needed because the information in mLinkProperties comes from multiple sources (DHCP,
3758     * netlink, static configuration, ...). When one of these sources of information has updated
3759     * link properties, we can't just assign them to mLinkProperties or we'd lose track of the
3760     * information that came from other sources. Instead, when one of those sources has new
3761     * information, we update the object that tracks the information from that source and then
3762     * call this method to apply the change to mLinkProperties.
3763     *
3764     * The information in mLinkProperties is currently obtained as follows:
3765     * - Interface name: set in the constructor.
3766     * - IPv4 and IPv6 addresses: netlink, passed in by mNetlinkTracker.
3767     * - IPv4 routes, DNS servers, and domains: DHCP.
3768     * - IPv6 routes and DNS servers: netlink, passed in by mNetlinkTracker.
3769     * - HTTP proxy: the wifi config store.
3770     */
3771    private void updateLinkProperties(int reason) {
3772        LinkProperties newLp = new LinkProperties();
3773
3774        // Interface name and proxy are locally configured.
3775        newLp.setInterfaceName(mInterfaceName);
3776        newLp.setHttpProxy(mWifiConfigStore.getProxyProperties(mLastNetworkId));
3777
3778        // IPv4/v6 addresses, IPv6 routes and IPv6 DNS servers come from netlink.
3779        LinkProperties netlinkLinkProperties = mNetlinkTracker.getLinkProperties();
3780        newLp.setLinkAddresses(netlinkLinkProperties.getLinkAddresses());
3781        for (RouteInfo route : netlinkLinkProperties.getRoutes()) {
3782            newLp.addRoute(route);
3783        }
3784        for (InetAddress dns : netlinkLinkProperties.getDnsServers()) {
3785            newLp.addDnsServer(dns);
3786        }
3787
3788        // IPv4 routes, DNS servers and domains come from mDhcpResults.
3789        synchronized (mDhcpResultsLock) {
3790            // Even when we're using static configuration, we don't need to look at the config
3791            // store, because static IP configuration also populates mDhcpResults.
3792            if ((mDhcpResults != null)) {
3793                for (RouteInfo route : mDhcpResults.getRoutes(mInterfaceName)) {
3794                    newLp.addRoute(route);
3795                }
3796                for (InetAddress dns : mDhcpResults.dnsServers) {
3797                    newLp.addDnsServer(dns);
3798                }
3799                newLp.setDomains(mDhcpResults.domains);
3800            }
3801        }
3802
3803        final boolean linkChanged = !newLp.equals(mLinkProperties);
3804        final boolean wasProvisioned = isProvisioned(mLinkProperties);
3805        final boolean isProvisioned = isProvisioned(newLp);
3806        final DetailedState detailedState = getNetworkDetailedState();
3807
3808        if (linkChanged) {
3809            if (DBG) {
3810                log("Link configuration changed for netId: " + mLastNetworkId
3811                        + " old: " + mLinkProperties + " new: " + newLp);
3812            }
3813            mLinkProperties = newLp;
3814            if (TextUtils.isEmpty(mTcpBufferSizes) == false) {
3815                mLinkProperties.setTcpBufferSizes(mTcpBufferSizes);
3816            }
3817            if (mNetworkAgent != null) mNetworkAgent.sendLinkProperties(mLinkProperties);
3818        }
3819
3820        if (DBG) {
3821            StringBuilder sb = new StringBuilder();
3822            sb.append("updateLinkProperties nid: " + mLastNetworkId);
3823            sb.append(" state: " + detailedState);
3824            sb.append(" reason: " + smToString(reason));
3825
3826            if (mLinkProperties != null) {
3827                if (mLinkProperties.hasIPv4Address()) {
3828                    sb.append(" v4");
3829                }
3830                if (mLinkProperties.hasGlobalIPv6Address()) {
3831                    sb.append(" v6");
3832                }
3833                if (mLinkProperties.hasIPv4DefaultRoute()) {
3834                    sb.append(" v4r");
3835                }
3836                if (mLinkProperties.hasIPv6DefaultRoute()) {
3837                    sb.append(" v6r");
3838                }
3839                if (mLinkProperties.hasIPv4DnsServer()) {
3840                    sb.append(" v4dns");
3841                }
3842                if (mLinkProperties.hasIPv6DnsServer()) {
3843                    sb.append(" v6dns");
3844                }
3845                if (isProvisioned) {
3846                    sb.append(" isprov");
3847                }
3848            }
3849            loge(sb.toString());
3850        }
3851
3852        // If we just configured or lost IP configuration, do the needful.
3853        // We don't just call handleSuccessfulIpConfiguration() or handleIpConfigurationLost()
3854        // here because those should only be called if we're attempting to connect or already
3855        // connected, whereas updateLinkProperties can be called at any time.
3856        switch (reason) {
3857            case DhcpStateMachine.DHCP_SUCCESS:
3858            case CMD_STATIC_IP_SUCCESS:
3859                // IPv4 provisioning succeded. Advance to connected state.
3860                sendMessage(CMD_IP_CONFIGURATION_SUCCESSFUL);
3861                if (!isProvisioned) {
3862                    // Can never happen unless DHCP reports success but isProvisioned thinks the
3863                    // resulting configuration is invalid (e.g., no IPv4 address, or the state in
3864                    // mLinkProperties is out of sync with reality, or there's a bug in this code).
3865                    // TODO: disconnect here instead. If our configuration is not usable, there's no
3866                    // point in staying connected, and if mLinkProperties is out of sync with
3867                    // reality, that will cause problems in the future.
3868                    loge("IPv4 config succeeded, but not provisioned");
3869                }
3870                break;
3871
3872            case DhcpStateMachine.DHCP_FAILURE:
3873                // DHCP failed. If we're not already provisioned, give up and disconnect.
3874                // If we're already provisioned (e.g., IPv6-only network), stay connected.
3875                if (!isProvisioned) {
3876                    sendMessage(CMD_IP_CONFIGURATION_LOST);
3877                } else {
3878                    // DHCP failed, but we're provisioned (e.g., if we're on an IPv6-only network).
3879                    sendMessage(CMD_IP_CONFIGURATION_SUCCESSFUL);
3880
3881                    // To be sure we don't get stuck with a non-working network if all we had is
3882                    // IPv4, remove the IPv4 address from the interface (since we're using DHCP,
3883                    // and DHCP failed). If we had an IPv4 address before, the deletion of the
3884                    // address  will cause a CMD_UPDATE_LINKPROPERTIES. If the IPv4 address was
3885                    // necessary for provisioning, its deletion will cause us to disconnect.
3886                    //
3887                    // This shouldn't be needed, because on an IPv4-only network a DHCP failure will
3888                    // have empty DhcpResults and thus empty LinkProperties, and isProvisioned will
3889                    // not return true if we're using DHCP and don't have an IPv4 default route. So
3890                    // for now it's only here for extra redundancy. However, it will increase
3891                    // robustness if we move to getting IPv4 routes from netlink as well.
3892                    loge("DHCP failure: provisioned, clearing IPv4 address.");
3893                    if (!clearIPv4Address(mInterfaceName)) {
3894                        sendMessage(CMD_IP_CONFIGURATION_LOST);
3895                    }
3896                }
3897                break;
3898
3899            case CMD_STATIC_IP_FAILURE:
3900                // Static configuration was invalid, or an error occurred in applying it. Give up.
3901                sendMessage(CMD_IP_CONFIGURATION_LOST);
3902                break;
3903
3904            case CMD_UPDATE_LINKPROPERTIES:
3905                // IP addresses, DNS servers, etc. changed. Act accordingly.
3906                if (wasProvisioned && !isProvisioned) {
3907                    // We no longer have a usable network configuration. Disconnect.
3908                    sendMessage(CMD_IP_CONFIGURATION_LOST);
3909                } else if (!wasProvisioned && isProvisioned) {
3910                    // We have a usable IPv6-only config. Advance to connected state.
3911                    sendMessage(CMD_IP_CONFIGURATION_SUCCESSFUL);
3912                }
3913                if (linkChanged && getNetworkDetailedState() == DetailedState.CONNECTED) {
3914                    // If anything has changed and we're already connected, send out a notification.
3915                    sendLinkConfigurationChangedBroadcast();
3916                }
3917                break;
3918        }
3919    }
3920
3921    /**
3922     * Clears all our link properties.
3923     */
3924     private void clearLinkProperties() {
3925         // Clear the link properties obtained from DHCP and netlink.
3926         synchronized (mDhcpResultsLock) {
3927             if (mDhcpResults != null) {
3928                 mDhcpResults.clear();
3929             }
3930         }
3931         mNetlinkTracker.clearLinkProperties();
3932
3933         // Now clear the merged link properties.
3934         mLinkProperties.clear();
3935         if (mNetworkAgent != null) mNetworkAgent.sendLinkProperties(mLinkProperties);
3936     }
3937
3938     /**
3939      * try to update default route MAC address.
3940      */
3941      private String updateDefaultRouteMacAddress(int timeout) {
3942          String address = null;
3943          for (RouteInfo route : mLinkProperties.getRoutes()) {
3944              if (route.isDefaultRoute() && route.hasGateway()) {
3945                  InetAddress gateway = route.getGateway();
3946                  if (gateway instanceof Inet4Address) {
3947                      if (PDBG) {
3948                          loge("updateDefaultRouteMacAddress found Ipv4 default :"
3949                                  + gateway.getHostAddress());
3950                      }
3951                      address = macAddressFromRoute(gateway.getHostAddress());
3952                     /* The gateway's MAC address is known */
3953                      if ((address == null) && (timeout > 0)) {
3954                          boolean reachable = false;
3955                          try {
3956                              reachable = gateway.isReachable(timeout);
3957                          } catch (Exception e) {
3958                              loge("updateDefaultRouteMacAddress exception reaching :"
3959                                      + gateway.getHostAddress());
3960
3961                          } finally {
3962                              if (reachable == true) {
3963
3964                                  address = macAddressFromRoute(gateway.getHostAddress());
3965                                  if (PDBG) {
3966                                      loge("updateDefaultRouteMacAddress reachable (tried again) :"
3967                                              + gateway.getHostAddress() + " found " + address);
3968                                  }
3969                              }
3970                          }
3971                      }
3972                      if (address != null) {
3973                          mWifiConfigStore.setDefaultGwMacAddress(mLastNetworkId, address);
3974                      }
3975                  }
3976              }
3977          }
3978          return address;
3979      }
3980
3981    private void sendScanResultsAvailableBroadcast() {
3982        Intent intent = new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
3983        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
3984        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
3985    }
3986
3987    private void sendRssiChangeBroadcast(final int newRssi) {
3988        try {
3989            mBatteryStats.noteWifiRssiChanged(newRssi);
3990        } catch (RemoteException e) {
3991            // Won't happen.
3992        }
3993        Intent intent = new Intent(WifiManager.RSSI_CHANGED_ACTION);
3994        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
3995        intent.putExtra(WifiManager.EXTRA_NEW_RSSI, newRssi);
3996        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
3997    }
3998
3999    private void sendNetworkStateChangeBroadcast(String bssid) {
4000        Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
4001        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
4002        intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, new NetworkInfo(mNetworkInfo));
4003        intent.putExtra(WifiManager.EXTRA_LINK_PROPERTIES, new LinkProperties (mLinkProperties));
4004        if (bssid != null)
4005            intent.putExtra(WifiManager.EXTRA_BSSID, bssid);
4006        if (mNetworkInfo.getDetailedState() == DetailedState.VERIFYING_POOR_LINK ||
4007                mNetworkInfo.getDetailedState() == DetailedState.CONNECTED) {
4008            intent.putExtra(WifiManager.EXTRA_WIFI_INFO, new WifiInfo(mWifiInfo));
4009        }
4010        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
4011    }
4012
4013    private void sendLinkConfigurationChangedBroadcast() {
4014        Intent intent = new Intent(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION);
4015        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
4016        intent.putExtra(WifiManager.EXTRA_LINK_PROPERTIES, new LinkProperties(mLinkProperties));
4017        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
4018    }
4019
4020    private void sendSupplicantConnectionChangedBroadcast(boolean connected) {
4021        Intent intent = new Intent(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
4022        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
4023        intent.putExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, connected);
4024        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
4025    }
4026
4027    /**
4028     * Record the detailed state of a network.
4029     * @param state the new {@code DetailedState}
4030     */
4031    private boolean setNetworkDetailedState(NetworkInfo.DetailedState state) {
4032        boolean hidden = false;
4033
4034        if (linkDebouncing || isRoaming()) {
4035            // There is generally a confusion in the system about colluding
4036            // WiFi Layer 2 state (as reported by supplicant) and the Network state
4037            // which leads to multiple confusion.
4038            //
4039            // If link is de-bouncing or roaming, we already have an IP address
4040            // as well we were connected and are doing L2 cycles of
4041            // reconnecting or renewing IP address to check that we still have it
4042            // This L2 link flapping should ne be reflected into the Network state
4043            // which is the state of the WiFi Network visible to Layer 3 and applications
4044            // Note that once debouncing and roaming are completed, we will
4045            // set the Network state to where it should be, or leave it as unchanged
4046            //
4047            hidden = true;
4048        }
4049        if (DBG) {
4050            log("setDetailed state, old ="
4051                    + mNetworkInfo.getDetailedState() + " and new state=" + state
4052                    + " hidden=" + hidden);
4053        }
4054        if (mNetworkInfo.getExtraInfo() != null && mWifiInfo.getSSID() != null) {
4055            // Always indicate that SSID has changed
4056            if (!mNetworkInfo.getExtraInfo().equals(mWifiInfo.getSSID())) {
4057                if (DBG) {
4058                    log("setDetailed state send new extra info"  + mWifiInfo.getSSID());
4059                }
4060                mNetworkInfo.setExtraInfo(mWifiInfo.getSSID());
4061                sendNetworkStateChangeBroadcast(null);
4062            }
4063        }
4064        if (hidden == true) {
4065            return false;
4066        }
4067
4068        if (state != mNetworkInfo.getDetailedState()) {
4069            mNetworkInfo.setDetailedState(state, null, mWifiInfo.getSSID());
4070            if (mNetworkAgent != null) {
4071                mNetworkAgent.sendNetworkInfo(mNetworkInfo);
4072            }
4073            sendNetworkStateChangeBroadcast(null);
4074            return true;
4075        }
4076        return false;
4077    }
4078
4079    private DetailedState getNetworkDetailedState() {
4080        return mNetworkInfo.getDetailedState();
4081    }
4082
4083
4084    private SupplicantState handleSupplicantStateChange(Message message) {
4085        StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
4086        SupplicantState state = stateChangeResult.state;
4087        // Supplicant state change
4088        // [31-13] Reserved for future use
4089        // [8 - 0] Supplicant state (as defined in SupplicantState.java)
4090        // 50023 supplicant_state_changed (custom|1|5)
4091        mWifiInfo.setSupplicantState(state);
4092        // Network id is only valid when we start connecting
4093        if (SupplicantState.isConnecting(state)) {
4094            mWifiInfo.setNetworkId(stateChangeResult.networkId);
4095        } else {
4096            mWifiInfo.setNetworkId(WifiConfiguration.INVALID_NETWORK_ID);
4097        }
4098
4099        mWifiInfo.setBSSID(stateChangeResult.BSSID);
4100        mWifiInfo.setSSID(stateChangeResult.wifiSsid);
4101
4102        mSupplicantStateTracker.sendMessage(Message.obtain(message));
4103
4104        return state;
4105    }
4106
4107    /**
4108     * Resets the Wi-Fi Connections by clearing any state, resetting any sockets
4109     * using the interface, stopping DHCP & disabling interface
4110     */
4111    private void handleNetworkDisconnect() {
4112        if (DBG) log("handleNetworkDisconnect: Stopping DHCP and clearing IP"
4113                + " stack:" + Thread.currentThread().getStackTrace()[2].getMethodName()
4114                +" - "+ Thread.currentThread().getStackTrace()[3].getMethodName()
4115                +" - "+ Thread.currentThread().getStackTrace()[4].getMethodName()
4116                +" - "+ Thread.currentThread().getStackTrace()[5].getMethodName());
4117
4118
4119        clearCurrentConfigBSSID("handleNetworkDisconnect");
4120
4121        stopDhcp();
4122
4123        try {
4124            mNwService.clearInterfaceAddresses(mInterfaceName);
4125            mNwService.disableIpv6(mInterfaceName);
4126        } catch (Exception e) {
4127            loge("Failed to clear addresses or disable ipv6" + e);
4128        }
4129
4130        /* Reset data structures */
4131        mBadLinkspeedcount = 0;
4132        mWifiInfo.reset();
4133        linkDebouncing = false;
4134        /* Reset roaming parameters */
4135        mAutoRoaming = WifiAutoJoinController.AUTO_JOIN_IDLE;
4136        fullBandConnectedTimeIntervalMilli = 20 * 1000; // Start scans at 20 seconds interval
4137
4138        setNetworkDetailedState(DetailedState.DISCONNECTED);
4139        if (mNetworkAgent != null) {
4140            mNetworkAgent.sendNetworkInfo(mNetworkInfo);
4141            mNetworkAgent = null;
4142        }
4143        mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.DISCONNECTED);
4144
4145        /* Clear network properties */
4146        clearLinkProperties();
4147
4148        /* Cend event to CM & network change broadcast */
4149        sendNetworkStateChangeBroadcast(mLastBssid);
4150
4151        /* Cancel auto roam requests */
4152        autoRoamSetBSSID(mLastNetworkId, "any");
4153
4154        mLastBssid= null;
4155        registerDisconnected();
4156        mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
4157    }
4158
4159    private void handleSupplicantConnectionLoss() {
4160        /* Socket connection can be lost when we do a graceful shutdown
4161        * or when the driver is hung. Ensure supplicant is stopped here.
4162        */
4163        mWifiMonitor.killSupplicant(mP2pSupported);
4164        mWifiNative.closeSupplicantConnection();
4165        sendSupplicantConnectionChangedBroadcast(false);
4166        setWifiState(WIFI_STATE_DISABLED);
4167    }
4168
4169    void handlePreDhcpSetup() {
4170        mDhcpActive = true;
4171        if (!mBluetoothConnectionActive) {
4172            /*
4173             * There are problems setting the Wi-Fi driver's power
4174             * mode to active when bluetooth coexistence mode is
4175             * enabled or sense.
4176             * <p>
4177             * We set Wi-Fi to active mode when
4178             * obtaining an IP address because we've found
4179             * compatibility issues with some routers with low power
4180             * mode.
4181             * <p>
4182             * In order for this active power mode to properly be set,
4183             * we disable coexistence mode until we're done with
4184             * obtaining an IP address.  One exception is if we
4185             * are currently connected to a headset, since disabling
4186             * coexistence would interrupt that connection.
4187             */
4188            // Disable the coexistence mode
4189            mWifiNative.setBluetoothCoexistenceMode(
4190                    mWifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED);
4191        }
4192
4193        // Disable power save and suspend optimizations during DHCP
4194        // Note: The order here is important for now. Brcm driver changes
4195        // power settings when we control suspend mode optimizations.
4196        // TODO: Remove this comment when the driver is fixed.
4197        setSuspendOptimizationsNative(SUSPEND_DUE_TO_DHCP, false);
4198        mWifiNative.setPowerSave(false);
4199
4200        stopBatchedScan();
4201        WifiNative.pauseScan();
4202
4203        /* P2p discovery breaks dhcp, shut it down in order to get through this */
4204        Message msg = new Message();
4205        msg.what = WifiP2pServiceImpl.BLOCK_DISCOVERY;
4206        msg.arg1 = WifiP2pServiceImpl.ENABLED;
4207        msg.arg2 = DhcpStateMachine.CMD_PRE_DHCP_ACTION_COMPLETE;
4208        msg.obj = mDhcpStateMachine;
4209        mWifiP2pChannel.sendMessage(msg);
4210    }
4211
4212
4213    void startDhcp() {
4214        if (mDhcpStateMachine == null) {
4215            mDhcpStateMachine = DhcpStateMachine.makeDhcpStateMachine(
4216                    mContext, WifiStateMachine.this, mInterfaceName);
4217
4218        }
4219        mDhcpStateMachine.registerForPreDhcpNotification();
4220        mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_START_DHCP);
4221    }
4222
4223    void renewDhcp() {
4224        if (mDhcpStateMachine == null) {
4225            mDhcpStateMachine = DhcpStateMachine.makeDhcpStateMachine(
4226                    mContext, WifiStateMachine.this, mInterfaceName);
4227
4228        }
4229        mDhcpStateMachine.registerForPreDhcpNotification();
4230        mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_RENEW_DHCP);
4231    }
4232
4233    void stopDhcp() {
4234        if (mDhcpStateMachine != null) {
4235            /* In case we were in middle of DHCP operation restore back powermode */
4236            handlePostDhcpSetup();
4237            mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_STOP_DHCP);
4238        }
4239    }
4240
4241    void handlePostDhcpSetup() {
4242        /* Restore power save and suspend optimizations */
4243        setSuspendOptimizationsNative(SUSPEND_DUE_TO_DHCP, true);
4244        mWifiNative.setPowerSave(true);
4245
4246        mWifiP2pChannel.sendMessage(WifiP2pServiceImpl.BLOCK_DISCOVERY, WifiP2pServiceImpl.DISABLED);
4247
4248        // Set the coexistence mode back to its default value
4249        mWifiNative.setBluetoothCoexistenceMode(
4250                mWifiNative.BLUETOOTH_COEXISTENCE_MODE_SENSE);
4251
4252        mDhcpActive = false;
4253
4254        startBatchedScan();
4255        WifiNative.restartScan();
4256    }
4257
4258    private void handleIPv4Success(DhcpResults dhcpResults, int reason) {
4259
4260        if (PDBG) {
4261            loge("wifistatemachine handleIPv4Success <" + dhcpResults.toString() + ">");
4262            loge("link address " + dhcpResults.ipAddress);
4263        }
4264
4265        synchronized (mDhcpResultsLock) {
4266            mDhcpResults = dhcpResults;
4267        }
4268
4269        Inet4Address addr = (Inet4Address) dhcpResults.ipAddress.getAddress();
4270        if (isRoaming()) {
4271            if (addr instanceof Inet4Address) {
4272                int previousAddress = mWifiInfo.getIpAddress();
4273                int newAddress = NetworkUtils.inetAddressToInt(addr);
4274                if (previousAddress != newAddress) {
4275                    loge("handleIPv4Success, roaming and address changed" +
4276                            mWifiInfo + " got: " + addr);
4277                } else {
4278
4279                }
4280            } else {
4281                loge("handleIPv4Success, roaming and didnt get an IPv4 address" +
4282                        addr.toString());
4283            }
4284        }
4285        mWifiInfo.setInetAddress(addr);
4286        mWifiInfo.setMeteredHint(dhcpResults.hasMeteredHint());
4287        updateLinkProperties(reason);
4288    }
4289
4290    private void handleSuccessfulIpConfiguration() {
4291        mLastSignalLevel = -1; // Force update of signal strength
4292        WifiConfiguration c = getCurrentWifiConfiguration();
4293        // Reset IP failure tracking
4294        if (c != null) {
4295            c.numConnectionFailures = 0;
4296        }
4297        if (c != null) {
4298            ScanResult result = getCurrentScanResult();
4299            if (result == null) {
4300                loge("WifiStateMachine: handleSuccessfulIpConfiguration and no scan results" +
4301                        c.configKey());
4302            } else {
4303                // Clear the per BSSID failure count
4304                result.numIpConfigFailures = 0;
4305                // Clear the WHOLE BSSID blacklist, which means supplicant is free to retry
4306                // any BSSID, even though it may already have a non zero ip failure count,
4307                // this will typically happen if the user walks away and come back to his arrea
4308                // TODO: implement blacklisting based on a timer, i.e. keep BSSID blacklisted
4309                // in supplicant for a couple of hours or a day
4310                mWifiNative.clearBlacklist();
4311            }
4312        }
4313    }
4314
4315    private void handleIPv4Failure(int reason) {
4316        synchronized(mDhcpResultsLock) {
4317             if (mDhcpResults != null) {
4318                 mDhcpResults.clear();
4319             }
4320        }
4321        if (PDBG) {
4322            loge("wifistatemachine handleIPv4Failure");
4323        }
4324        updateLinkProperties(reason);
4325    }
4326
4327    private void handleIpConfigurationLost() {
4328        mWifiInfo.setInetAddress(null);
4329        mWifiInfo.setMeteredHint(false);
4330
4331        mWifiConfigStore.handleSSIDStateChange(mLastNetworkId, false,
4332                "DHCP FAILURE", mWifiInfo.getBSSID());
4333
4334        /* DHCP times out after about 30 seconds, we do a
4335         * disconnect thru supplicant, we will let autojoin retry connecting to the network
4336         */
4337        mWifiNative.disconnect();
4338    }
4339
4340    /* Current design is to not set the config on a running hostapd but instead
4341     * stop and start tethering when user changes config on a running access point
4342     *
4343     * TODO: Add control channel setup through hostapd that allows changing config
4344     * on a running daemon
4345     */
4346    private void startSoftApWithConfig(final WifiConfiguration config) {
4347        // Start hostapd on a separate thread
4348        new Thread(new Runnable() {
4349            public void run() {
4350                try {
4351                    mNwService.startAccessPoint(config, mInterfaceName);
4352                } catch (Exception e) {
4353                    loge("Exception in softap start " + e);
4354                    try {
4355                        mNwService.stopAccessPoint(mInterfaceName);
4356                        mNwService.startAccessPoint(config, mInterfaceName);
4357                    } catch (Exception e1) {
4358                        loge("Exception in softap re-start " + e1);
4359                        sendMessage(CMD_START_AP_FAILURE);
4360                        return;
4361                    }
4362                }
4363                if (DBG) log("Soft AP start successful");
4364                sendMessage(CMD_START_AP_SUCCESS);
4365            }
4366        }).start();
4367    }
4368
4369    /*
4370     * Read a MAC address in /proc/arp/table, used by WifistateMachine
4371     * so as to record MAC address of default gateway.
4372     **/
4373    private String macAddressFromRoute(String ipAddress) {
4374        String macAddress = null;
4375        BufferedReader reader = null;
4376        try {
4377            reader = new BufferedReader(new FileReader("/proc/net/arp"));
4378
4379            // Skip over the line bearing colum titles
4380            String line = reader.readLine();
4381
4382            while ((line = reader.readLine()) != null) {
4383                String[] tokens = line.split("[ ]+");
4384                if (tokens.length < 6) {
4385                    continue;
4386                }
4387
4388                // ARP column format is
4389                // Address HWType HWAddress Flags Mask IFace
4390                String ip = tokens[0];
4391                String mac = tokens[3];
4392
4393                if (ipAddress.equals(ip)) {
4394                    macAddress = mac;
4395                    break;
4396                }
4397            }
4398
4399            if (macAddress == null) {
4400                loge("Did not find remoteAddress {" + ipAddress + "} in " +
4401                        "/proc/net/arp");
4402            }
4403
4404        } catch (FileNotFoundException e) {
4405            loge("Could not open /proc/net/arp to lookup mac address");
4406        } catch (IOException e) {
4407            loge("Could not read /proc/net/arp to lookup mac address");
4408        } finally {
4409            try {
4410                if (reader != null) {
4411                    reader.close();
4412                }
4413            } catch (IOException e) {
4414                // Do nothing
4415            }
4416        }
4417        return macAddress;
4418
4419    }
4420
4421    private class WifiNetworkFactory extends NetworkFactory {
4422        public WifiNetworkFactory(Looper l, Context c, String TAG, NetworkCapabilities f) {
4423            super(l, c, TAG, f);
4424        }
4425        protected void startNetwork() {
4426            // TODO
4427            // Enter association mode.
4428        }
4429        protected void stopNetwork() {
4430            // TODO
4431            // Stop associating.
4432        }
4433    }
4434    /********************************************************
4435     * HSM states
4436     *******************************************************/
4437
4438    class DefaultState extends State {
4439        @Override
4440        public boolean processMessage(Message message) {
4441            logStateAndMessage(message, getClass().getSimpleName());
4442
4443            switch (message.what) {
4444                case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: {
4445                    AsyncChannel ac = (AsyncChannel) message.obj;
4446                    if (ac == mWifiP2pChannel) {
4447                        if (message.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
4448                            mWifiP2pChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
4449                        } else {
4450                            loge("WifiP2pService connection failure, error=" + message.arg1);
4451                        }
4452                    } else {
4453                        loge("got HALF_CONNECTED for unknown channel");
4454                    }
4455                    break;
4456                }
4457                case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
4458                    AsyncChannel ac = (AsyncChannel) message.obj;
4459                    if (ac == mWifiP2pChannel) {
4460                        loge("WifiP2pService channel lost, message.arg1 =" + message.arg1);
4461                        //TODO: Re-establish connection to state machine after a delay
4462                        // mWifiP2pChannel.connect(mContext, getHandler(),
4463                        // mWifiP2pManager.getMessenger());
4464                    }
4465                    break;
4466                }
4467                case CMD_BLUETOOTH_ADAPTER_STATE_CHANGE:
4468                    mBluetoothConnectionActive = (message.arg1 !=
4469                            BluetoothAdapter.STATE_DISCONNECTED);
4470                    break;
4471                    /* Synchronous call returns */
4472                case CMD_PING_SUPPLICANT:
4473                case CMD_ENABLE_NETWORK:
4474                case CMD_ADD_OR_UPDATE_NETWORK:
4475                case CMD_REMOVE_NETWORK:
4476                case CMD_SAVE_CONFIG:
4477                    replyToMessage(message, message.what, FAILURE);
4478                    break;
4479                case CMD_GET_CAPABILITY_FREQ:
4480                    replyToMessage(message, message.what, null);
4481                    break;
4482                case CMD_GET_CONFIGURED_NETWORKS:
4483                    replyToMessage(message, message.what, (List<WifiConfiguration>) null);
4484                    break;
4485                case CMD_GET_PRIVILEGED_CONFIGURED_NETWORKS:
4486                    replyToMessage(message, message.what, (List<WifiConfiguration>) null);
4487                    break;
4488                case CMD_ENABLE_RSSI_POLL:
4489                    mEnableRssiPolling = (message.arg1 == 1);
4490                    break;
4491                case CMD_SET_HIGH_PERF_MODE:
4492                    if (message.arg1 == 1) {
4493                        setSuspendOptimizations(SUSPEND_DUE_TO_HIGH_PERF, false);
4494                    } else {
4495                        setSuspendOptimizations(SUSPEND_DUE_TO_HIGH_PERF, true);
4496                    }
4497                    break;
4498                case CMD_BOOT_COMPLETED:
4499                    String countryCode = mPersistedCountryCode;
4500                    if (TextUtils.isEmpty(countryCode) == false) {
4501                        Settings.Global.putString(mContext.getContentResolver(),
4502                                Settings.Global.WIFI_COUNTRY_CODE,
4503                                countryCode);
4504                        // It may be that the state transition that should send this info
4505                        // to the driver happened between mPersistedCountryCode getting set
4506                        // and now, so simply persisting it here would mean we have sent
4507                        // nothing to the driver.  Send the cmd so it might be set now.
4508                        int sequenceNum = mCountryCodeSequence.incrementAndGet();
4509                        sendMessageAtFrontOfQueue(CMD_SET_COUNTRY_CODE,
4510                                sequenceNum, 0, countryCode);
4511                    }
4512
4513                    checkAndSetConnectivityInstance();
4514                    mNetworkFactory = new WifiNetworkFactory(getHandler().getLooper(), mContext,
4515                            NETWORKTYPE, mNetworkCapabilitiesFilter);
4516                    mNetworkFactory.setScoreFilter(60);
4517                    mCm.registerNetworkFactory(new Messenger(mNetworkFactory), NETWORKTYPE);
4518                    break;
4519                case CMD_SET_BATCHED_SCAN:
4520                    recordBatchedScanSettings(message.arg1, message.arg2, (Bundle)message.obj);
4521                    break;
4522                case CMD_POLL_BATCHED_SCAN:
4523                    handleBatchedScanPollRequest();
4524                    break;
4525                case CMD_START_NEXT_BATCHED_SCAN:
4526                    startNextBatchedScan();
4527                    break;
4528                case CMD_SCREEN_STATE_CHANGED:
4529                    handleScreenStateChanged(message.arg1 != 0,
4530                            /* startBackgroundScanIfNeeded = */ false);
4531                    break;
4532                    /* Discard */
4533                case CMD_START_SCAN:
4534                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
4535                    break;
4536                case CMD_START_SUPPLICANT:
4537                case CMD_STOP_SUPPLICANT:
4538                case CMD_STOP_SUPPLICANT_FAILED:
4539                case CMD_START_DRIVER:
4540                case CMD_STOP_DRIVER:
4541                case CMD_DELAYED_STOP_DRIVER:
4542                case CMD_DRIVER_START_TIMED_OUT:
4543                case CMD_START_AP:
4544                case CMD_START_AP_SUCCESS:
4545                case CMD_START_AP_FAILURE:
4546                case CMD_STOP_AP:
4547                case CMD_TETHER_STATE_CHANGE:
4548                case CMD_TETHER_NOTIFICATION_TIMED_OUT:
4549                case CMD_DISCONNECT:
4550                case CMD_RECONNECT:
4551                case CMD_REASSOCIATE:
4552                case CMD_RELOAD_TLS_AND_RECONNECT:
4553                case WifiMonitor.SUP_CONNECTION_EVENT:
4554                case WifiMonitor.SUP_DISCONNECTION_EVENT:
4555                case WifiMonitor.NETWORK_CONNECTION_EVENT:
4556                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
4557                case WifiMonitor.SCAN_RESULTS_EVENT:
4558                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
4559                case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
4560                case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
4561                case WifiMonitor.WPS_OVERLAP_EVENT:
4562                case CMD_BLACKLIST_NETWORK:
4563                case CMD_CLEAR_BLACKLIST:
4564                case CMD_SET_OPERATIONAL_MODE:
4565                case CMD_SET_COUNTRY_CODE:
4566                case CMD_SET_FREQUENCY_BAND:
4567                case CMD_RSSI_POLL:
4568                case CMD_ENABLE_ALL_NETWORKS:
4569                case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
4570                case DhcpStateMachine.CMD_POST_DHCP_ACTION:
4571                /* Handled by WifiApConfigStore */
4572                case CMD_SET_AP_CONFIG:
4573                case CMD_SET_AP_CONFIG_COMPLETED:
4574                case CMD_REQUEST_AP_CONFIG:
4575                case CMD_RESPONSE_AP_CONFIG:
4576                case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
4577                case WifiWatchdogStateMachine.GOOD_LINK_DETECTED:
4578                case CMD_NO_NETWORKS_PERIODIC_SCAN:
4579                case CMD_DISABLE_P2P_RSP:
4580                case WifiMonitor.SUP_REQUEST_IDENTITY:
4581                case CMD_TEST_NETWORK_DISCONNECT:
4582                case CMD_OBTAINING_IP_ADDRESS_WATCHDOG_TIMER:
4583                case WifiMonitor.SUP_REQUEST_SIM_AUTH:
4584                case CMD_TARGET_BSSID:
4585                case CMD_AUTO_CONNECT:
4586                case CMD_AUTO_ROAM:
4587                case CMD_AUTO_SAVE_NETWORK:
4588                case CMD_ASSOCIATED_BSSID:
4589                case CMD_UNWANTED_NETWORK:
4590                case CMD_DISCONNECTING_WATCHDOG_TIMER:
4591                case CMD_ROAM_WATCHDOG_TIMER:
4592                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
4593                    break;
4594                case DhcpStateMachine.CMD_ON_QUIT:
4595                    mDhcpStateMachine = null;
4596                    break;
4597                case CMD_SET_SUSPEND_OPT_ENABLED:
4598                    if (message.arg1 == 1) {
4599                        mSuspendWakeLock.release();
4600                        setSuspendOptimizations(SUSPEND_DUE_TO_SCREEN, true);
4601                    } else {
4602                        setSuspendOptimizations(SUSPEND_DUE_TO_SCREEN, false);
4603                    }
4604                    break;
4605                case WifiMonitor.DRIVER_HUNG_EVENT:
4606                    setSupplicantRunning(false);
4607                    setSupplicantRunning(true);
4608                    break;
4609                case WifiManager.CONNECT_NETWORK:
4610                    replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
4611                            WifiManager.BUSY);
4612                    break;
4613                case WifiManager.FORGET_NETWORK:
4614                    replyToMessage(message, WifiManager.FORGET_NETWORK_FAILED,
4615                            WifiManager.BUSY);
4616                    break;
4617                case WifiManager.SAVE_NETWORK:
4618                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
4619                    replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED,
4620                            WifiManager.BUSY);
4621                    break;
4622                case WifiManager.START_WPS:
4623                    replyToMessage(message, WifiManager.WPS_FAILED,
4624                            WifiManager.BUSY);
4625                    break;
4626                case WifiManager.CANCEL_WPS:
4627                    replyToMessage(message, WifiManager.CANCEL_WPS_FAILED,
4628                            WifiManager.BUSY);
4629                    break;
4630                case WifiManager.DISABLE_NETWORK:
4631                    replyToMessage(message, WifiManager.DISABLE_NETWORK_FAILED,
4632                            WifiManager.BUSY);
4633                    break;
4634                case WifiManager.RSSI_PKTCNT_FETCH:
4635                    replyToMessage(message, WifiManager.RSSI_PKTCNT_FETCH_FAILED,
4636                            WifiManager.BUSY);
4637                    break;
4638                case CMD_GET_SUPPORTED_FEATURES:
4639                    if (WifiNative.startHal()) {
4640                        int featureSet = WifiNative.getSupportedFeatureSet();
4641                        replyToMessage(message, message.what, featureSet);
4642                    } else {
4643                        replyToMessage(message, message.what, 0);
4644                    }
4645                    break;
4646                case CMD_GET_LINK_LAYER_STATS:
4647                    // Not supported hence reply with error message
4648                    replyToMessage(message, message.what, null);
4649                    break;
4650                case WifiP2pServiceImpl.P2P_CONNECTION_CHANGED:
4651                    NetworkInfo info = (NetworkInfo) message.obj;
4652                    mP2pConnected.set(info.isConnected());
4653                    break;
4654                case WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST:
4655                    mTemporarilyDisconnectWifi = (message.arg1 == 1);
4656                    replyToMessage(message, WifiP2pServiceImpl.DISCONNECT_WIFI_RESPONSE);
4657                    break;
4658                /* Link configuration (IP address, DNS, ...) changes notified via netlink */
4659                case CMD_UPDATE_LINKPROPERTIES:
4660                    updateLinkProperties(CMD_UPDATE_LINKPROPERTIES);
4661                    break;
4662                case CMD_IP_CONFIGURATION_SUCCESSFUL:
4663                case CMD_IP_CONFIGURATION_LOST:
4664                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
4665                    break;
4666                case CMD_GET_CONNECTION_STATISTICS:
4667                    replyToMessage(message, message.what, mWifiConnectionStatistics);
4668                    break;
4669                default:
4670                    loge("Error! unhandled message" + message);
4671                    break;
4672            }
4673            return HANDLED;
4674        }
4675    }
4676
4677    class InitialState extends State {
4678        @Override
4679        public void enter() {
4680            mWifiNative.unloadDriver();
4681
4682            if (mWifiP2pChannel == null) {
4683                mWifiP2pChannel = new AsyncChannel();
4684                mWifiP2pChannel.connect(mContext, getHandler(),
4685                    mWifiP2pServiceImpl.getP2pStateMachineMessenger());
4686            }
4687
4688            if (mWifiApConfigChannel == null) {
4689                mWifiApConfigChannel = new AsyncChannel();
4690                WifiApConfigStore wifiApConfigStore = WifiApConfigStore.makeWifiApConfigStore(
4691                        mContext, getHandler());
4692                wifiApConfigStore.loadApConfiguration();
4693                mWifiApConfigChannel.connectSync(mContext, getHandler(),
4694                        wifiApConfigStore.getMessenger());
4695            }
4696        }
4697        @Override
4698        public boolean processMessage(Message message) {
4699            logStateAndMessage(message, getClass().getSimpleName());
4700            switch (message.what) {
4701                case CMD_START_SUPPLICANT:
4702                    if (mWifiNative.loadDriver()) {
4703                        try {
4704                            mNwService.wifiFirmwareReload(mInterfaceName, "STA");
4705                        } catch (Exception e) {
4706                            loge("Failed to reload STA firmware " + e);
4707                            // Continue
4708                        }
4709
4710                        try {
4711                            // A runtime crash can leave the interface up and
4712                            // IP addresses configured, and this affects
4713                            // connectivity when supplicant starts up.
4714                            // Ensure interface is down and we have no IP
4715                            // addresses before a supplicant start.
4716                            mNwService.setInterfaceDown(mInterfaceName);
4717                            mNwService.clearInterfaceAddresses(mInterfaceName);
4718
4719                            // Set privacy extensions
4720                            mNwService.setInterfaceIpv6PrivacyExtensions(mInterfaceName, true);
4721
4722                           // IPv6 is enabled only as long as access point is connected since:
4723                           // - IPv6 addresses and routes stick around after disconnection
4724                           // - kernel is unaware when connected and fails to start IPv6 negotiation
4725                           // - kernel can start autoconfiguration when 802.1x is not complete
4726                            mNwService.disableIpv6(mInterfaceName);
4727                        } catch (RemoteException re) {
4728                            loge("Unable to change interface settings: " + re);
4729                        } catch (IllegalStateException ie) {
4730                            loge("Unable to change interface settings: " + ie);
4731                        }
4732
4733                       /* Stop a running supplicant after a runtime restart
4734                        * Avoids issues with drivers that do not handle interface down
4735                        * on a running supplicant properly.
4736                        */
4737                        mWifiMonitor.killSupplicant(mP2pSupported);
4738                        if(mWifiNative.startSupplicant(mP2pSupported)) {
4739                            setWifiState(WIFI_STATE_ENABLING);
4740                            if (DBG) log("Supplicant start successful");
4741                            mWifiMonitor.startMonitoring();
4742                            transitionTo(mSupplicantStartingState);
4743                        } else {
4744                            loge("Failed to start supplicant!");
4745                        }
4746                    } else {
4747                        loge("Failed to load driver");
4748                    }
4749                    break;
4750                case CMD_START_AP:
4751                    if (mWifiNative.loadDriver()) {
4752                        setWifiApState(WIFI_AP_STATE_ENABLING);
4753                        transitionTo(mSoftApStartingState);
4754                    } else {
4755                        loge("Failed to load driver for softap");
4756                    }
4757                default:
4758                    return NOT_HANDLED;
4759            }
4760            return HANDLED;
4761        }
4762    }
4763
4764    class SupplicantStartingState extends State {
4765        private void initializeWpsDetails() {
4766            String detail;
4767            detail = SystemProperties.get("ro.product.name", "");
4768            if (!mWifiNative.setDeviceName(detail)) {
4769                loge("Failed to set device name " +  detail);
4770            }
4771            detail = SystemProperties.get("ro.product.manufacturer", "");
4772            if (!mWifiNative.setManufacturer(detail)) {
4773                loge("Failed to set manufacturer " + detail);
4774            }
4775            detail = SystemProperties.get("ro.product.model", "");
4776            if (!mWifiNative.setModelName(detail)) {
4777                loge("Failed to set model name " + detail);
4778            }
4779            detail = SystemProperties.get("ro.product.model", "");
4780            if (!mWifiNative.setModelNumber(detail)) {
4781                loge("Failed to set model number " + detail);
4782            }
4783            detail = SystemProperties.get("ro.serialno", "");
4784            if (!mWifiNative.setSerialNumber(detail)) {
4785                loge("Failed to set serial number " + detail);
4786            }
4787            if (!mWifiNative.setConfigMethods("physical_display virtual_push_button")) {
4788                loge("Failed to set WPS config methods");
4789            }
4790            if (!mWifiNative.setDeviceType(mPrimaryDeviceType)) {
4791                loge("Failed to set primary device type " + mPrimaryDeviceType);
4792            }
4793        }
4794
4795        @Override
4796        public boolean processMessage(Message message) {
4797            logStateAndMessage(message, getClass().getSimpleName());
4798
4799            switch(message.what) {
4800                case WifiMonitor.SUP_CONNECTION_EVENT:
4801                    if (DBG) log("Supplicant connection established");
4802                    setWifiState(WIFI_STATE_ENABLED);
4803                    mSupplicantRestartCount = 0;
4804                    /* Reset the supplicant state to indicate the supplicant
4805                     * state is not known at this time */
4806                    mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
4807                    /* Initialize data structures */
4808                    mLastBssid = null;
4809                    mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
4810                    mLastSignalLevel = -1;
4811
4812                    mWifiInfo.setMacAddress(mWifiNative.getMacAddress());
4813                    mWifiNative.enableSaveConfig();
4814                    mWifiConfigStore.loadAndEnableAllNetworks();
4815                    if (mWifiConfigStore.enableVerboseLogging > 0) {
4816                        enableVerboseLogging(mWifiConfigStore.enableVerboseLogging);
4817                    }
4818                    if (mWifiConfigStore.associatedPartialScanPeriodMilli < 0) {
4819                        mWifiConfigStore.associatedPartialScanPeriodMilli = 0;
4820                    }
4821                    initializeWpsDetails();
4822
4823                    sendSupplicantConnectionChangedBroadcast(true);
4824                    transitionTo(mDriverStartedState);
4825                    break;
4826                case WifiMonitor.SUP_DISCONNECTION_EVENT:
4827                    if (++mSupplicantRestartCount <= SUPPLICANT_RESTART_TRIES) {
4828                        loge("Failed to setup control channel, restart supplicant");
4829                        mWifiMonitor.killSupplicant(mP2pSupported);
4830                        transitionTo(mInitialState);
4831                        sendMessageDelayed(CMD_START_SUPPLICANT, SUPPLICANT_RESTART_INTERVAL_MSECS);
4832                    } else {
4833                        loge("Failed " + mSupplicantRestartCount +
4834                                " times to start supplicant, unload driver");
4835                        mSupplicantRestartCount = 0;
4836                        setWifiState(WIFI_STATE_UNKNOWN);
4837                        transitionTo(mInitialState);
4838                    }
4839                    break;
4840                case CMD_START_SUPPLICANT:
4841                case CMD_STOP_SUPPLICANT:
4842                case CMD_START_AP:
4843                case CMD_STOP_AP:
4844                case CMD_START_DRIVER:
4845                case CMD_STOP_DRIVER:
4846                case CMD_SET_OPERATIONAL_MODE:
4847                case CMD_SET_COUNTRY_CODE:
4848                case CMD_SET_FREQUENCY_BAND:
4849                case CMD_START_PACKET_FILTERING:
4850                case CMD_STOP_PACKET_FILTERING:
4851                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_DEFERRED;
4852                    deferMessage(message);
4853                    break;
4854                default:
4855                    return NOT_HANDLED;
4856            }
4857            return HANDLED;
4858        }
4859    }
4860
4861    class SupplicantStartedState extends State {
4862        @Override
4863        public void enter() {
4864            /* Wifi is available as long as we have a connection to supplicant */
4865            mNetworkInfo.setIsAvailable(true);
4866            if (mNetworkAgent != null) mNetworkAgent.sendNetworkInfo(mNetworkInfo);
4867
4868            int defaultInterval = mContext.getResources().getInteger(
4869                    R.integer.config_wifi_supplicant_scan_interval);
4870
4871            mSupplicantScanIntervalMs = Settings.Global.getLong(mContext.getContentResolver(),
4872                    Settings.Global.WIFI_SUPPLICANT_SCAN_INTERVAL_MS,
4873                    defaultInterval);
4874
4875            mWifiNative.setScanInterval((int)mSupplicantScanIntervalMs / 1000);
4876            mWifiNative.setExternalSim(true);
4877
4878            setRandomMacOui();
4879            mWifiNative.enableAutoConnect(false);
4880        }
4881
4882        @Override
4883        public boolean processMessage(Message message) {
4884            logStateAndMessage(message, getClass().getSimpleName());
4885
4886            switch(message.what) {
4887                case CMD_STOP_SUPPLICANT:   /* Supplicant stopped by user */
4888                    if (mP2pSupported) {
4889                        transitionTo(mWaitForP2pDisableState);
4890                    } else {
4891                        transitionTo(mSupplicantStoppingState);
4892                    }
4893                    break;
4894                case WifiMonitor.SUP_DISCONNECTION_EVENT:  /* Supplicant connection lost */
4895                    loge("Connection lost, restart supplicant");
4896                    handleSupplicantConnectionLoss();
4897                    handleNetworkDisconnect();
4898                    mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
4899                    if (mP2pSupported) {
4900                        transitionTo(mWaitForP2pDisableState);
4901                    } else {
4902                        transitionTo(mInitialState);
4903                    }
4904                    sendMessageDelayed(CMD_START_SUPPLICANT, SUPPLICANT_RESTART_INTERVAL_MSECS);
4905                    break;
4906                case WifiMonitor.SCAN_RESULTS_EVENT:
4907                    closeRadioScanStats();
4908                    noteScanEnd();
4909                    setScanResults();
4910                    if (mIsFullScanOngoing) {
4911                        /* Just updated results from full scan, let apps know about this */
4912                        sendScanResultsAvailableBroadcast();
4913                    }
4914                    mIsScanOngoing = false;
4915                    mIsFullScanOngoing = false;
4916                    if (mBufferedScanMsg.size() > 0)
4917                        sendMessage(mBufferedScanMsg.remove());
4918                    break;
4919                case CMD_PING_SUPPLICANT:
4920                    boolean ok = mWifiNative.ping();
4921                    replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
4922                    break;
4923                case CMD_GET_CAPABILITY_FREQ:
4924                    String freqs = mWifiNative.getFreqCapability();
4925                    replyToMessage(message, message.what, freqs);
4926                    break;
4927                case CMD_START_AP:
4928                    /* Cannot start soft AP while in client mode */
4929                    loge("Failed to start soft AP with a running supplicant");
4930                    setWifiApState(WIFI_AP_STATE_FAILED);
4931                    break;
4932                case CMD_SET_OPERATIONAL_MODE:
4933                    mOperationalMode = message.arg1;
4934                    break;
4935                case CMD_TARGET_BSSID:
4936                    // Trying to associate to this BSSID
4937                    if (message.obj != null) {
4938                        mTargetRoamBSSID = (String) message.obj;
4939                    }
4940                    break;
4941                case CMD_GET_LINK_LAYER_STATS:
4942                    WifiLinkLayerStats stats = getWifiLinkLayerStats(DBG);
4943                    if (stats == null) {
4944                        // When firmware doesnt support link layer stats, return an empty object
4945                        stats = new WifiLinkLayerStats();
4946                    }
4947                    replyToMessage(message, message.what, stats);
4948                    break;
4949                default:
4950                    return NOT_HANDLED;
4951            }
4952            return HANDLED;
4953        }
4954
4955        @Override
4956        public void exit() {
4957            mNetworkInfo.setIsAvailable(false);
4958            if (mNetworkAgent != null) mNetworkAgent.sendNetworkInfo(mNetworkInfo);
4959        }
4960    }
4961
4962    class SupplicantStoppingState extends State {
4963        @Override
4964        public void enter() {
4965            /* Send any reset commands to supplicant before shutting it down */
4966            handleNetworkDisconnect();
4967            if (mDhcpStateMachine != null) {
4968                mDhcpStateMachine.doQuit();
4969            }
4970
4971            if (DBG) log("stopping supplicant");
4972            mWifiMonitor.stopSupplicant();
4973
4974            /* Send ourselves a delayed message to indicate failure after a wait time */
4975            sendMessageDelayed(obtainMessage(CMD_STOP_SUPPLICANT_FAILED,
4976                    ++mSupplicantStopFailureToken, 0), SUPPLICANT_RESTART_INTERVAL_MSECS);
4977            setWifiState(WIFI_STATE_DISABLING);
4978            mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
4979        }
4980        @Override
4981        public boolean processMessage(Message message) {
4982            logStateAndMessage(message, getClass().getSimpleName());
4983
4984            switch(message.what) {
4985                case WifiMonitor.SUP_CONNECTION_EVENT:
4986                    loge("Supplicant connection received while stopping");
4987                    break;
4988                case WifiMonitor.SUP_DISCONNECTION_EVENT:
4989                    if (DBG) log("Supplicant connection lost");
4990                    handleSupplicantConnectionLoss();
4991                    transitionTo(mInitialState);
4992                    break;
4993                case CMD_STOP_SUPPLICANT_FAILED:
4994                    if (message.arg1 == mSupplicantStopFailureToken) {
4995                        loge("Timed out on a supplicant stop, kill and proceed");
4996                        handleSupplicantConnectionLoss();
4997                        transitionTo(mInitialState);
4998                    }
4999                    break;
5000                case CMD_START_SUPPLICANT:
5001                case CMD_STOP_SUPPLICANT:
5002                case CMD_START_AP:
5003                case CMD_STOP_AP:
5004                case CMD_START_DRIVER:
5005                case CMD_STOP_DRIVER:
5006                case CMD_SET_OPERATIONAL_MODE:
5007                case CMD_SET_COUNTRY_CODE:
5008                case CMD_SET_FREQUENCY_BAND:
5009                case CMD_START_PACKET_FILTERING:
5010                case CMD_STOP_PACKET_FILTERING:
5011                    deferMessage(message);
5012                    break;
5013                default:
5014                    return NOT_HANDLED;
5015            }
5016            return HANDLED;
5017        }
5018    }
5019
5020    class DriverStartingState extends State {
5021        private int mTries;
5022        @Override
5023        public void enter() {
5024            mTries = 1;
5025            /* Send ourselves a delayed message to start driver a second time */
5026            sendMessageDelayed(obtainMessage(CMD_DRIVER_START_TIMED_OUT,
5027                        ++mDriverStartToken, 0), DRIVER_START_TIME_OUT_MSECS);
5028        }
5029        @Override
5030        public boolean processMessage(Message message) {
5031            logStateAndMessage(message, getClass().getSimpleName());
5032
5033            switch(message.what) {
5034               case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
5035                    SupplicantState state = handleSupplicantStateChange(message);
5036                    /* If suplicant is exiting out of INTERFACE_DISABLED state into
5037                     * a state that indicates driver has started, it is ready to
5038                     * receive driver commands
5039                     */
5040                    if (SupplicantState.isDriverActive(state)) {
5041                        transitionTo(mDriverStartedState);
5042                    }
5043                    break;
5044                case CMD_DRIVER_START_TIMED_OUT:
5045                    if (message.arg1 == mDriverStartToken) {
5046                        if (mTries >= 2) {
5047                            loge("Failed to start driver after " + mTries);
5048                            transitionTo(mDriverStoppedState);
5049                        } else {
5050                            loge("Driver start failed, retrying");
5051                            mWakeLock.acquire();
5052                            mWifiNative.startDriver();
5053                            mWakeLock.release();
5054
5055                            ++mTries;
5056                            /* Send ourselves a delayed message to start driver again */
5057                            sendMessageDelayed(obtainMessage(CMD_DRIVER_START_TIMED_OUT,
5058                                        ++mDriverStartToken, 0), DRIVER_START_TIME_OUT_MSECS);
5059                        }
5060                    }
5061                    break;
5062                    /* Queue driver commands & connection events */
5063                case CMD_START_DRIVER:
5064                case CMD_STOP_DRIVER:
5065                case WifiMonitor.NETWORK_CONNECTION_EVENT:
5066                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
5067                case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
5068                case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
5069                case WifiMonitor.WPS_OVERLAP_EVENT:
5070                case CMD_SET_COUNTRY_CODE:
5071                case CMD_SET_FREQUENCY_BAND:
5072                case CMD_START_PACKET_FILTERING:
5073                case CMD_STOP_PACKET_FILTERING:
5074                case CMD_START_SCAN:
5075                case CMD_DISCONNECT:
5076                case CMD_REASSOCIATE:
5077                case CMD_RECONNECT:
5078                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_DEFERRED;
5079                    deferMessage(message);
5080                    break;
5081                case WifiMonitor.SCAN_RESULTS_EVENT:
5082                    // Loose scan results obtained in Driver Starting state, they can only confuse
5083                    // the state machine
5084                    break;
5085                default:
5086                    return NOT_HANDLED;
5087            }
5088            return HANDLED;
5089        }
5090    }
5091
5092    class DriverStartedState extends State {
5093        @Override
5094        public void enter() {
5095
5096            if (PDBG) {
5097                loge("Driverstarted State enter");
5098            }
5099            mIsRunning = true;
5100            mInDelayedStop = false;
5101            mDelayedStopCounter++;
5102            updateBatteryWorkSource(null);
5103            /**
5104             * Enable bluetooth coexistence scan mode when bluetooth connection is active.
5105             * When this mode is on, some of the low-level scan parameters used by the
5106             * driver are changed to reduce interference with bluetooth
5107             */
5108            mWifiNative.setBluetoothCoexistenceScanMode(mBluetoothConnectionActive);
5109            /* set country code */
5110            setCountryCode();
5111            /* set frequency band of operation */
5112            setFrequencyBand();
5113            /* initialize network state */
5114            setNetworkDetailedState(DetailedState.DISCONNECTED);
5115
5116            /* Remove any filtering on Multicast v6 at start */
5117            mWifiNative.stopFilteringMulticastV6Packets();
5118
5119            /* Reset Multicast v4 filtering state */
5120            if (mFilteringMulticastV4Packets.get()) {
5121                mWifiNative.startFilteringMulticastV4Packets();
5122            } else {
5123                mWifiNative.stopFilteringMulticastV4Packets();
5124            }
5125
5126            mDhcpActive = false;
5127
5128            startBatchedScan();
5129
5130            if (mOperationalMode != CONNECT_MODE) {
5131                mWifiNative.disconnect();
5132                mWifiConfigStore.disableAllNetworks();
5133                if (mOperationalMode == SCAN_ONLY_WITH_WIFI_OFF_MODE) {
5134                    setWifiState(WIFI_STATE_DISABLED);
5135                }
5136                transitionTo(mScanModeState);
5137            } else {
5138
5139                // Status pulls in the current supplicant state and network connection state
5140                // events over the monitor connection. This helps framework sync up with
5141                // current supplicant state
5142                // TODO: actually check th supplicant status string and make sure the supplicant
5143                // is in disconnecte4d state.
5144                mWifiNative.status();
5145                // Transitioning to Disconnected state will trigger a scan and subsequently AutoJoin
5146                transitionTo(mDisconnectedState);
5147            }
5148
5149            // We may have missed screen update at boot
5150            if (mScreenBroadcastReceived.get() == false) {
5151                PowerManager powerManager = (PowerManager)mContext.getSystemService(
5152                        Context.POWER_SERVICE);
5153                handleScreenStateChanged(powerManager.isScreenOn(),
5154                        /* startBackgroundScanIfNeeded = */ false);
5155            } else {
5156                // Set the right suspend mode settings
5157                mWifiNative.setSuspendOptimizations(mSuspendOptNeedsDisabled == 0
5158                        && mUserWantsSuspendOpt.get());
5159            }
5160            mWifiNative.setPowerSave(true);
5161
5162            if (mP2pSupported) {
5163                if (mOperationalMode == CONNECT_MODE) {
5164                    mWifiP2pChannel.sendMessage(WifiStateMachine.CMD_ENABLE_P2P);
5165                } else {
5166                    // P2P statemachine starts in disabled state, and is not enabled until
5167                    // CMD_ENABLE_P2P is sent from here; so, nothing needs to be done to
5168                    // keep it disabled.
5169                }
5170            }
5171
5172            final Intent intent = new Intent(WifiManager.WIFI_SCAN_AVAILABLE);
5173            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
5174            intent.putExtra(WifiManager.EXTRA_SCAN_AVAILABLE, WIFI_STATE_ENABLED);
5175            mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
5176
5177            if (PDBG) {
5178                loge("Driverstarted State enter done");
5179            }
5180        }
5181
5182        @Override
5183        public boolean processMessage(Message message) {
5184            logStateAndMessage(message, getClass().getSimpleName());
5185
5186            switch(message.what) {
5187                case CMD_START_SCAN:
5188                    handleScanRequest(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, message);
5189                    break;
5190                case CMD_SET_BATCHED_SCAN:
5191                    if (recordBatchedScanSettings(message.arg1, message.arg2,
5192                            (Bundle)message.obj)) {
5193                        if (mBatchedScanSettings != null) {
5194                            startBatchedScan();
5195                        } else {
5196                            stopBatchedScan();
5197                        }
5198                    }
5199                    break;
5200                case CMD_SET_COUNTRY_CODE:
5201                    String country = (String) message.obj;
5202                    final boolean persist = (message.arg2 == 1);
5203                    final int sequence = message.arg1;
5204                    if (sequence != mCountryCodeSequence.get()) {
5205                        if (DBG) log("set country code ignored due to sequnce num");
5206                        break;
5207                    }
5208                    if (DBG) log("set country code " + country);
5209                    if (persist) {
5210                        mPersistedCountryCode = country;
5211                        Settings.Global.putString(mContext.getContentResolver(),
5212                                Settings.Global.WIFI_COUNTRY_CODE,
5213                                country);
5214                    }
5215                    country = country.toUpperCase(Locale.ROOT);
5216                    if (mLastSetCountryCode == null
5217                            || country.equals(mLastSetCountryCode) == false) {
5218                        if (mWifiNative.setCountryCode(country)) {
5219                            mLastSetCountryCode = country;
5220                        } else {
5221                            loge("Failed to set country code " + country);
5222                        }
5223                    }
5224                    mWifiP2pChannel.sendMessage(WifiP2pServiceImpl.SET_COUNTRY_CODE, country);
5225                    break;
5226                case CMD_SET_FREQUENCY_BAND:
5227                    int band =  message.arg1;
5228                    if (DBG) log("set frequency band " + band);
5229                    if (mWifiNative.setBand(band)) {
5230
5231                        if (PDBG)  loge("did set frequency band " + band);
5232
5233                        mFrequencyBand.set(band);
5234                        // Flush old data - like scan results
5235                        mWifiNative.bssFlush();
5236                        // Fetch the latest scan results when frequency band is set
5237                        startScanNative(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, null);
5238
5239                        if (PDBG)  loge("done set frequency band " + band);
5240
5241                    } else {
5242                        loge("Failed to set frequency band " + band);
5243                    }
5244                    break;
5245                case CMD_BLUETOOTH_ADAPTER_STATE_CHANGE:
5246                    mBluetoothConnectionActive = (message.arg1 !=
5247                            BluetoothAdapter.STATE_DISCONNECTED);
5248                    mWifiNative.setBluetoothCoexistenceScanMode(mBluetoothConnectionActive);
5249                    break;
5250                case CMD_STOP_DRIVER:
5251                    int mode = message.arg1;
5252
5253                    /* Already doing a delayed stop */
5254                    if (mInDelayedStop) {
5255                        if (DBG) log("Already in delayed stop");
5256                        break;
5257                    }
5258                    /* disconnect right now, but leave the driver running for a bit */
5259                    mWifiConfigStore.disableAllNetworks();
5260
5261                    mInDelayedStop = true;
5262                    mDelayedStopCounter++;
5263                    if (DBG) log("Delayed stop message " + mDelayedStopCounter);
5264
5265                    /* send regular delayed shut down */
5266                    Intent driverStopIntent = new Intent(ACTION_DELAYED_DRIVER_STOP, null);
5267                    driverStopIntent.putExtra(DELAYED_STOP_COUNTER, mDelayedStopCounter);
5268                    mDriverStopIntent = PendingIntent.getBroadcast(mContext,
5269                            DRIVER_STOP_REQUEST, driverStopIntent,
5270                            PendingIntent.FLAG_UPDATE_CURRENT);
5271
5272                    mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()
5273                            + mDriverStopDelayMs, mDriverStopIntent);
5274                    break;
5275                case CMD_START_DRIVER:
5276                    if (mInDelayedStop) {
5277                        mInDelayedStop = false;
5278                        mDelayedStopCounter++;
5279                        mAlarmManager.cancel(mDriverStopIntent);
5280                        if (DBG) log("Delayed stop ignored due to start");
5281                        if (mOperationalMode == CONNECT_MODE) {
5282                            mWifiConfigStore.enableAllNetworks();
5283                        }
5284                    }
5285                    break;
5286                case CMD_DELAYED_STOP_DRIVER:
5287                    if (DBG) log("delayed stop " + message.arg1 + " " + mDelayedStopCounter);
5288                    if (message.arg1 != mDelayedStopCounter) break;
5289                    if (getCurrentState() != mDisconnectedState) {
5290                        mWifiNative.disconnect();
5291                        handleNetworkDisconnect();
5292                    }
5293                    mWakeLock.acquire();
5294                    mWifiNative.stopDriver();
5295                    mWakeLock.release();
5296                    if (mP2pSupported) {
5297                        transitionTo(mWaitForP2pDisableState);
5298                    } else {
5299                        transitionTo(mDriverStoppingState);
5300                    }
5301                    break;
5302                case CMD_START_PACKET_FILTERING:
5303                    if (message.arg1 == MULTICAST_V6) {
5304                        mWifiNative.startFilteringMulticastV6Packets();
5305                    } else if (message.arg1 == MULTICAST_V4) {
5306                        mWifiNative.startFilteringMulticastV4Packets();
5307                    } else {
5308                        loge("Illegal arugments to CMD_START_PACKET_FILTERING");
5309                    }
5310                    break;
5311                case CMD_STOP_PACKET_FILTERING:
5312                    if (message.arg1 == MULTICAST_V6) {
5313                        mWifiNative.stopFilteringMulticastV6Packets();
5314                    } else if (message.arg1 == MULTICAST_V4) {
5315                        mWifiNative.stopFilteringMulticastV4Packets();
5316                    } else {
5317                        loge("Illegal arugments to CMD_STOP_PACKET_FILTERING");
5318                    }
5319                    break;
5320                case CMD_SET_SUSPEND_OPT_ENABLED:
5321                    if (message.arg1 == 1) {
5322                        setSuspendOptimizationsNative(SUSPEND_DUE_TO_SCREEN, true);
5323                        mSuspendWakeLock.release();
5324                    } else {
5325                        setSuspendOptimizationsNative(SUSPEND_DUE_TO_SCREEN, false);
5326                    }
5327                    break;
5328                case CMD_SET_HIGH_PERF_MODE:
5329                    if (message.arg1 == 1) {
5330                        setSuspendOptimizationsNative(SUSPEND_DUE_TO_HIGH_PERF, false);
5331                    } else {
5332                        setSuspendOptimizationsNative(SUSPEND_DUE_TO_HIGH_PERF, true);
5333                    }
5334                    break;
5335                case CMD_ENABLE_TDLS:
5336                    if (message.obj != null) {
5337                        String remoteAddress = (String) message.obj;
5338                        boolean enable = (message.arg1 == 1);
5339                        mWifiNative.startTdls(remoteAddress, enable);
5340                    }
5341                    break;
5342                default:
5343                    return NOT_HANDLED;
5344            }
5345            return HANDLED;
5346        }
5347        @Override
5348        public void exit() {
5349            mIsRunning = false;
5350            updateBatteryWorkSource(null);
5351            mScanResults = new ArrayList<ScanResult>();
5352
5353            stopBatchedScan();
5354
5355            final Intent intent = new Intent(WifiManager.WIFI_SCAN_AVAILABLE);
5356            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
5357            intent.putExtra(WifiManager.EXTRA_SCAN_AVAILABLE, WIFI_STATE_DISABLED);
5358            mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
5359            noteScanEnd(); // wrap up any pending request.
5360            mBufferedScanMsg.clear();
5361
5362            mLastSetCountryCode = null;
5363        }
5364    }
5365
5366    class WaitForP2pDisableState extends State {
5367        private State mTransitionToState;
5368        @Override
5369        public void enter() {
5370            switch (getCurrentMessage().what) {
5371                case WifiMonitor.SUP_DISCONNECTION_EVENT:
5372                    mTransitionToState = mInitialState;
5373                    break;
5374                case CMD_DELAYED_STOP_DRIVER:
5375                    mTransitionToState = mDriverStoppingState;
5376                    break;
5377                case CMD_STOP_SUPPLICANT:
5378                    mTransitionToState = mSupplicantStoppingState;
5379                    break;
5380                default:
5381                    mTransitionToState = mDriverStoppingState;
5382                    break;
5383            }
5384            mWifiP2pChannel.sendMessage(WifiStateMachine.CMD_DISABLE_P2P_REQ);
5385        }
5386        @Override
5387        public boolean processMessage(Message message) {
5388            logStateAndMessage(message, getClass().getSimpleName());
5389
5390            switch(message.what) {
5391                case WifiStateMachine.CMD_DISABLE_P2P_RSP:
5392                    transitionTo(mTransitionToState);
5393                    break;
5394                /* Defer wifi start/shut and driver commands */
5395                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
5396                case CMD_START_SUPPLICANT:
5397                case CMD_STOP_SUPPLICANT:
5398                case CMD_START_AP:
5399                case CMD_STOP_AP:
5400                case CMD_START_DRIVER:
5401                case CMD_STOP_DRIVER:
5402                case CMD_SET_OPERATIONAL_MODE:
5403                case CMD_SET_COUNTRY_CODE:
5404                case CMD_SET_FREQUENCY_BAND:
5405                case CMD_START_PACKET_FILTERING:
5406                case CMD_STOP_PACKET_FILTERING:
5407                case CMD_START_SCAN:
5408                case CMD_DISCONNECT:
5409                case CMD_REASSOCIATE:
5410                case CMD_RECONNECT:
5411                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_DEFERRED;
5412                    deferMessage(message);
5413                    break;
5414                default:
5415                    return NOT_HANDLED;
5416            }
5417            return HANDLED;
5418        }
5419    }
5420
5421    class DriverStoppingState extends State {
5422        @Override
5423        public boolean processMessage(Message message) {
5424            logStateAndMessage(message, getClass().getSimpleName());
5425
5426            switch(message.what) {
5427                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
5428                    SupplicantState state = handleSupplicantStateChange(message);
5429                    if (state == SupplicantState.INTERFACE_DISABLED) {
5430                        transitionTo(mDriverStoppedState);
5431                    }
5432                    break;
5433                    /* Queue driver commands */
5434                case CMD_START_DRIVER:
5435                case CMD_STOP_DRIVER:
5436                case CMD_SET_COUNTRY_CODE:
5437                case CMD_SET_FREQUENCY_BAND:
5438                case CMD_START_PACKET_FILTERING:
5439                case CMD_STOP_PACKET_FILTERING:
5440                case CMD_START_SCAN:
5441                case CMD_DISCONNECT:
5442                case CMD_REASSOCIATE:
5443                case CMD_RECONNECT:
5444                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_DEFERRED;
5445                    deferMessage(message);
5446                    break;
5447                default:
5448                    return NOT_HANDLED;
5449            }
5450            return HANDLED;
5451        }
5452    }
5453
5454    class DriverStoppedState extends State {
5455        @Override
5456        public boolean processMessage(Message message) {
5457            logStateAndMessage(message, getClass().getSimpleName());
5458            switch (message.what) {
5459                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
5460                    StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
5461                    SupplicantState state = stateChangeResult.state;
5462                    // A WEXT bug means that we can be back to driver started state
5463                    // unexpectedly
5464                    if (SupplicantState.isDriverActive(state)) {
5465                        transitionTo(mDriverStartedState);
5466                    }
5467                    break;
5468                case CMD_START_DRIVER:
5469                    mWakeLock.acquire();
5470                    mWifiNative.startDriver();
5471                    mWakeLock.release();
5472                    transitionTo(mDriverStartingState);
5473                    break;
5474                default:
5475                    return NOT_HANDLED;
5476            }
5477            return HANDLED;
5478        }
5479    }
5480
5481    class ScanModeState extends State {
5482        private int mLastOperationMode;
5483        @Override
5484        public void enter() {
5485            mLastOperationMode = mOperationalMode;
5486        }
5487        @Override
5488        public boolean processMessage(Message message) {
5489            logStateAndMessage(message, getClass().getSimpleName());
5490
5491            switch(message.what) {
5492                case CMD_SET_OPERATIONAL_MODE:
5493                    if (message.arg1 == CONNECT_MODE) {
5494
5495                        if (mLastOperationMode == SCAN_ONLY_WITH_WIFI_OFF_MODE) {
5496                            setWifiState(WIFI_STATE_ENABLED);
5497                            // Load and re-enable networks when going back to enabled state
5498                            // This is essential for networks to show up after restore
5499                            mWifiConfigStore.loadAndEnableAllNetworks();
5500                            mWifiP2pChannel.sendMessage(CMD_ENABLE_P2P);
5501                        } else {
5502                            mWifiConfigStore.enableAllNetworks();
5503                        }
5504
5505                        mWifiNative.reconnect();
5506
5507                        mOperationalMode = CONNECT_MODE;
5508                        transitionTo(mDisconnectedState);
5509                    } else {
5510                        // Nothing to do
5511                        return HANDLED;
5512                    }
5513                    break;
5514                // Handle scan. All the connection related commands are
5515                // handled only in ConnectModeState
5516                case CMD_START_SCAN:
5517                    handleScanRequest(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, message);
5518                    break;
5519                default:
5520                    return NOT_HANDLED;
5521            }
5522            return HANDLED;
5523        }
5524    }
5525
5526
5527    String smToString(Message message) {
5528        return smToString(message.what);
5529    }
5530
5531    String smToString(int what) {
5532        String s = "unknown";
5533        switch (what) {
5534            case WifiMonitor.DRIVER_HUNG_EVENT:
5535                s = "DRIVER_HUNG_EVENT";
5536                break;
5537            case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
5538                s = "AsyncChannel.CMD_CHANNEL_HALF_CONNECTED";
5539                break;
5540            case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
5541                s = "AsyncChannel.CMD_CHANNEL_DISCONNECTED";
5542                break;
5543            case CMD_SET_FREQUENCY_BAND:
5544                s = "CMD_SET_FREQUENCY_BAND";
5545                break;
5546            case CMD_DELAYED_NETWORK_DISCONNECT:
5547                s = "CMD_DELAYED_NETWORK_DISCONNECT";
5548                break;
5549            case CMD_TEST_NETWORK_DISCONNECT:
5550                s = "CMD_TEST_NETWORK_DISCONNECT";
5551                break;
5552            case CMD_OBTAINING_IP_ADDRESS_WATCHDOG_TIMER:
5553                s = "CMD_OBTAINING_IP_ADDRESS_WATCHDOG_TIMER";
5554                break;
5555            case CMD_START_DRIVER:
5556                s = "CMD_START_DRIVER";
5557                break;
5558            case CMD_STOP_DRIVER:
5559                s = "CMD_STOP_DRIVER";
5560                break;
5561            case CMD_STOP_SUPPLICANT:
5562                s = "CMD_STOP_SUPPLICANT";
5563                break;
5564            case CMD_STOP_SUPPLICANT_FAILED:
5565                s = "CMD_STOP_SUPPLICANT_FAILED";
5566                break;
5567            case CMD_START_SUPPLICANT:
5568                s = "CMD_START_SUPPLICANT";
5569                break;
5570            case CMD_REQUEST_AP_CONFIG:
5571                s = "CMD_REQUEST_AP_CONFIG";
5572                break;
5573            case CMD_RESPONSE_AP_CONFIG:
5574                s = "CMD_RESPONSE_AP_CONFIG";
5575                break;
5576            case CMD_TETHER_STATE_CHANGE:
5577                s = "CMD_TETHER_STATE_CHANGE";
5578                break;
5579            case CMD_TETHER_NOTIFICATION_TIMED_OUT:
5580                s = "CMD_TETHER_NOTIFICATION_TIMED_OUT";
5581                break;
5582            case CMD_BLUETOOTH_ADAPTER_STATE_CHANGE:
5583                s = "CMD_BLUETOOTH_ADAPTER_STATE_CHANGE";
5584                break;
5585            case CMD_ADD_OR_UPDATE_NETWORK:
5586                s = "CMD_ADD_OR_UPDATE_NETWORK";
5587                break;
5588            case CMD_REMOVE_NETWORK:
5589                s = "CMD_REMOVE_NETWORK";
5590                break;
5591            case CMD_ENABLE_NETWORK:
5592                s = "CMD_ENABLE_NETWORK";
5593                break;
5594            case CMD_ENABLE_ALL_NETWORKS:
5595                s = "CMD_ENABLE_ALL_NETWORKS";
5596                break;
5597            case CMD_AUTO_CONNECT:
5598                s = "CMD_AUTO_CONNECT";
5599                break;
5600            case CMD_AUTO_ROAM:
5601                s = "CMD_AUTO_ROAM";
5602                break;
5603            case CMD_AUTO_SAVE_NETWORK:
5604                s = "CMD_AUTO_SAVE_NETWORK";
5605                break;
5606            case CMD_BOOT_COMPLETED:
5607                s = "CMD_BOOT_COMPLETED";
5608                break;
5609            case DhcpStateMachine.CMD_START_DHCP:
5610                s = "CMD_START_DHCP";
5611                break;
5612            case DhcpStateMachine.CMD_STOP_DHCP:
5613                s = "CMD_STOP_DHCP";
5614                break;
5615            case DhcpStateMachine.CMD_RENEW_DHCP:
5616                s = "CMD_RENEW_DHCP";
5617                break;
5618            case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
5619                s = "CMD_PRE_DHCP_ACTION";
5620                break;
5621            case DhcpStateMachine.CMD_POST_DHCP_ACTION:
5622                s = "CMD_POST_DHCP_ACTION";
5623                break;
5624            case DhcpStateMachine.CMD_PRE_DHCP_ACTION_COMPLETE:
5625                s = "CMD_PRE_DHCP_ACTION_COMPLETE";
5626                break;
5627            case DhcpStateMachine.CMD_ON_QUIT:
5628                s = "CMD_ON_QUIT";
5629                break;
5630            case WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST:
5631                s = "WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST";
5632                break;
5633            case WifiManager.DISABLE_NETWORK:
5634                s = "WifiManager.DISABLE_NETWORK";
5635                break;
5636            case CMD_BLACKLIST_NETWORK:
5637                s = "CMD_BLACKLIST_NETWORK";
5638                break;
5639            case CMD_CLEAR_BLACKLIST:
5640                s = "CMD_CLEAR_BLACKLIST";
5641                break;
5642            case CMD_SAVE_CONFIG:
5643                s = "CMD_SAVE_CONFIG";
5644                break;
5645            case CMD_GET_CONFIGURED_NETWORKS:
5646                s = "CMD_GET_CONFIGURED_NETWORKS";
5647                break;
5648            case CMD_GET_SUPPORTED_FEATURES:
5649                s = "CMD_GET_ADAPTORS";
5650                break;
5651            case CMD_UNWANTED_NETWORK:
5652                s = "CMD_UNWANTED_NETWORK";
5653                break;
5654            case CMD_GET_LINK_LAYER_STATS:
5655                s = "CMD_GET_LINK_LAYER_STATS";
5656                break;
5657            case CMD_GET_PRIVILEGED_CONFIGURED_NETWORKS:
5658                s = "CMD_GET_PRIVILEGED_CONFIGURED_NETWORKS";
5659                break;
5660            case CMD_DISCONNECT:
5661                s = "CMD_DISCONNECT";
5662                break;
5663            case CMD_RECONNECT:
5664                s = "CMD_RECONNECT";
5665                break;
5666            case CMD_REASSOCIATE:
5667                s = "CMD_REASSOCIATE";
5668                break;
5669            case CMD_GET_CONNECTION_STATISTICS:
5670                s = "CMD_GET_CONNECTION_STATISTICS";
5671                break;
5672            case CMD_SET_HIGH_PERF_MODE:
5673                s = "CMD_SET_HIGH_PERF_MODE";
5674                break;
5675            case CMD_SET_COUNTRY_CODE:
5676                s = "CMD_SET_COUNTRY_CODE";
5677                break;
5678            case CMD_ENABLE_RSSI_POLL:
5679                s = "CMD_ENABLE_RSSI_POLL";
5680                break;
5681            case CMD_RSSI_POLL:
5682                s = "CMD_RSSI_POLL";
5683                break;
5684            case CMD_START_PACKET_FILTERING:
5685                s = "CMD_START_PACKET_FILTERING";
5686                break;
5687            case CMD_STOP_PACKET_FILTERING:
5688                s = "CMD_STOP_PACKET_FILTERING";
5689                break;
5690            case CMD_SET_SUSPEND_OPT_ENABLED:
5691                s = "CMD_SET_SUSPEND_OPT_ENABLED";
5692                break;
5693            case CMD_NO_NETWORKS_PERIODIC_SCAN:
5694                s = "CMD_NO_NETWORKS_PERIODIC_SCAN";
5695                break;
5696            case CMD_SET_BATCHED_SCAN:
5697                s = "CMD_SET_BATCHED_SCAN";
5698                break;
5699            case CMD_START_NEXT_BATCHED_SCAN:
5700                s = "CMD_START_NEXT_BATCHED_SCAN";
5701                break;
5702            case CMD_POLL_BATCHED_SCAN:
5703                s = "CMD_POLL_BATCHED_SCAN";
5704                break;
5705            case CMD_UPDATE_LINKPROPERTIES:
5706                s = "CMD_UPDATE_LINKPROPERTIES";
5707                break;
5708            case CMD_RELOAD_TLS_AND_RECONNECT:
5709                s = "CMD_RELOAD_TLS_AND_RECONNECT";
5710                break;
5711            case WifiManager.CONNECT_NETWORK:
5712                s = "CONNECT_NETWORK";
5713                break;
5714            case WifiManager.SAVE_NETWORK:
5715                s = "SAVE_NETWORK";
5716                break;
5717            case WifiManager.FORGET_NETWORK:
5718                s = "FORGET_NETWORK";
5719                break;
5720            case WifiMonitor.SUP_CONNECTION_EVENT:
5721                s = "SUP_CONNECTION_EVENT";
5722                break;
5723            case WifiMonitor.SUP_DISCONNECTION_EVENT:
5724                s = "SUP_DISCONNECTION_EVENT";
5725                break;
5726            case WifiMonitor.SCAN_RESULTS_EVENT:
5727                s = "SCAN_RESULTS_EVENT";
5728                break;
5729            case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
5730                s = "SUPPLICANT_STATE_CHANGE_EVENT";
5731                break;
5732            case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
5733                s = "AUTHENTICATION_FAILURE_EVENT";
5734                break;
5735            case WifiMonitor.SSID_TEMP_DISABLED:
5736                s = "SSID_TEMP_DISABLED";
5737                break;
5738            case WifiMonitor.SSID_REENABLED:
5739                s = "SSID_REENABLED";
5740                break;
5741            case WifiMonitor.WPS_SUCCESS_EVENT:
5742                s = "WPS_SUCCESS_EVENT";
5743                break;
5744            case WifiMonitor.WPS_FAIL_EVENT:
5745                s = "WPS_FAIL_EVENT";
5746                break;
5747            case WifiMonitor.SUP_REQUEST_IDENTITY:
5748                s = "SUP_REQUEST_IDENTITY";
5749                break;
5750            case WifiMonitor.NETWORK_CONNECTION_EVENT:
5751                s = "NETWORK_CONNECTION_EVENT";
5752                break;
5753            case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
5754                s = "NETWORK_DISCONNECTION_EVENT";
5755                break;
5756            case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
5757                s = "ASSOCIATION_REJECTION_EVENT";
5758                break;
5759            case CMD_SET_OPERATIONAL_MODE:
5760                s = "CMD_SET_OPERATIONAL_MODE";
5761                break;
5762            case CMD_START_SCAN:
5763                s = "CMD_START_SCAN";
5764                break;
5765            case CMD_DISABLE_P2P_RSP:
5766                s = "CMD_DISABLE_P2P_RSP";
5767                break;
5768            case CMD_DISABLE_P2P_REQ:
5769                s = "CMD_DISABLE_P2P_REQ";
5770                break;
5771            case WifiWatchdogStateMachine.GOOD_LINK_DETECTED:
5772                s = "GOOD_LINK_DETECTED";
5773                break;
5774            case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
5775                s = "POOR_LINK_DETECTED";
5776                break;
5777            case WifiP2pServiceImpl.GROUP_CREATING_TIMED_OUT:
5778                s = "GROUP_CREATING_TIMED_OUT";
5779                break;
5780            case WifiP2pServiceImpl.P2P_CONNECTION_CHANGED:
5781                s = "P2P_CONNECTION_CHANGED";
5782                break;
5783            case WifiP2pServiceImpl.DISCONNECT_WIFI_RESPONSE:
5784                s = "P2P.DISCONNECT_WIFI_RESPONSE";
5785                break;
5786            case WifiP2pServiceImpl.SET_MIRACAST_MODE:
5787                s = "P2P.SET_MIRACAST_MODE";
5788                break;
5789            case WifiP2pServiceImpl.BLOCK_DISCOVERY:
5790                s = "P2P.BLOCK_DISCOVERY";
5791                break;
5792            case WifiP2pServiceImpl.SET_COUNTRY_CODE:
5793                s = "P2P.SET_COUNTRY_CODE";
5794                break;
5795            case WifiManager.CANCEL_WPS:
5796                s = "CANCEL_WPS";
5797                break;
5798            case WifiManager.CANCEL_WPS_FAILED:
5799                s = "CANCEL_WPS_FAILED";
5800                break;
5801            case WifiManager.CANCEL_WPS_SUCCEDED:
5802                s = "CANCEL_WPS_SUCCEDED";
5803                break;
5804            case WifiManager.START_WPS:
5805                s = "START_WPS";
5806                break;
5807            case WifiManager.START_WPS_SUCCEEDED:
5808                s = "START_WPS_SUCCEEDED";
5809                break;
5810            case WifiManager.WPS_FAILED:
5811                s = "WPS_FAILED";
5812                break;
5813            case WifiManager.WPS_COMPLETED:
5814                s = "WPS_COMPLETED";
5815                break;
5816            case WifiManager.RSSI_PKTCNT_FETCH:
5817                s = "RSSI_PKTCNT_FETCH";
5818                break;
5819            case CMD_IP_CONFIGURATION_LOST:
5820                s = "CMD_IP_CONFIGURATION_LOST";
5821                break;
5822            case CMD_IP_CONFIGURATION_SUCCESSFUL:
5823                s = "CMD_IP_CONFIGURATION_SUCCESSFUL";
5824                break;
5825            case CMD_STATIC_IP_SUCCESS:
5826                s = "CMD_STATIC_IP_SUCCESSFUL";
5827                break;
5828            case CMD_STATIC_IP_FAILURE:
5829                s = "CMD_STATIC_IP_FAILURE";
5830                break;
5831            case DhcpStateMachine.DHCP_SUCCESS:
5832                s = "DHCP_SUCCESS";
5833                break;
5834            case DhcpStateMachine.DHCP_FAILURE:
5835                s = "DHCP_FAILURE";
5836                break;
5837            case CMD_TARGET_BSSID:
5838                s = "CMD_TARGET_BSSID";
5839                break;
5840            case CMD_ASSOCIATED_BSSID:
5841                s = "CMD_ASSOCIATED_BSSID";
5842                break;
5843            case CMD_ROAM_WATCHDOG_TIMER:
5844                s = "CMD_ROAM_WATCHDOG_TIMER";
5845                break;
5846            case CMD_SCREEN_STATE_CHANGED:
5847                s = "CMD_SCREEN_STATE_CHANGED";
5848                break;
5849            case CMD_DISCONNECTING_WATCHDOG_TIMER:
5850                s = "CMD_DISCONNECTING_WATCHDOG_TIMER";
5851                break;
5852            default:
5853                s = "what:" + Integer.toString(what);
5854                break;
5855        }
5856        return s;
5857    }
5858
5859    void registerConnected() {
5860       if (mLastNetworkId != WifiConfiguration.INVALID_NETWORK_ID) {
5861           long now_ms = System.currentTimeMillis();
5862           // We are switching away from this configuration,
5863           // hence record the time we were connected last
5864           WifiConfiguration config = mWifiConfigStore.getWifiConfiguration(mLastNetworkId);
5865           if (config != null) {
5866               config.lastConnected = System.currentTimeMillis();
5867               config.autoJoinBailedDueToLowRssi = false;
5868               config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED);
5869               config.numConnectionFailures = 0;
5870               config.numIpConfigFailures = 0;
5871               config.numAuthFailures = 0;
5872               config.numAssociation++;
5873           }
5874           mBadLinkspeedcount = 0;
5875       }
5876    }
5877
5878    void registerDisconnected() {
5879        if (mLastNetworkId != WifiConfiguration.INVALID_NETWORK_ID) {
5880            long now_ms = System.currentTimeMillis();
5881            // We are switching away from this configuration,
5882            // hence record the time we were connected last
5883            WifiConfiguration config = mWifiConfigStore.getWifiConfiguration(mLastNetworkId);
5884            if (config != null) {
5885                config.lastDisconnected = System.currentTimeMillis();
5886            }
5887        }
5888    }
5889
5890    void noteWifiDisabledWhileAssociated() {
5891        // We got disabled by user while we were associated, make note of it
5892        int rssi = mWifiInfo.getRssi();
5893        WifiConfiguration config = getCurrentWifiConfiguration();
5894        if (getCurrentState() == mConnectedState
5895                && rssi != WifiInfo.INVALID_RSSI
5896                && config != null) {
5897            boolean is24GHz = mWifiInfo.is24GHz();
5898            boolean isBadRSSI = (is24GHz && rssi < mWifiConfigStore.thresholdBadRssi24)
5899                    || (!is24GHz && rssi < mWifiConfigStore.thresholdBadRssi5);
5900            boolean isLowRSSI = (is24GHz && rssi < mWifiConfigStore.thresholdLowRssi24)
5901                    || (!is24GHz && mWifiInfo.getRssi() < mWifiConfigStore.thresholdLowRssi5);
5902            boolean isHighRSSI = (is24GHz && rssi >= mWifiConfigStore.thresholdGoodRssi24)
5903                    || (!is24GHz && mWifiInfo.getRssi() >= mWifiConfigStore.thresholdGoodRssi5);
5904            if (isBadRSSI) {
5905                // Take note that we got disabled while RSSI was Bad
5906                config.numUserTriggeredWifiDisableLowRSSI++;
5907            } else if (isLowRSSI) {
5908                // Take note that we got disabled while RSSI was Low
5909                config.numUserTriggeredWifiDisableBadRSSI++;
5910            } else if (!isHighRSSI) {
5911                // Take note that we got disabled while RSSI was Not high
5912                config.numUserTriggeredWifiDisableNotHighRSSI++;
5913            }
5914        }
5915    }
5916
5917    void setInternetAccessState(boolean enabled) {
5918        WifiConfiguration config = getCurrentWifiConfiguration();
5919        if (config != null) {
5920            config.noInternetAccess = enabled;
5921        }
5922    }
5923
5924    WifiConfiguration getCurrentWifiConfiguration() {
5925        if (mLastNetworkId == WifiConfiguration.INVALID_NETWORK_ID) {
5926            return null;
5927        }
5928        return mWifiConfigStore.getWifiConfiguration(mLastNetworkId);
5929    }
5930
5931    ScanResult getCurrentScanResult() {
5932        WifiConfiguration config = getCurrentWifiConfiguration();
5933        if (config == null) {
5934            return null;
5935        }
5936        String BSSID = mWifiInfo.getBSSID();
5937        if (BSSID == null) {
5938            BSSID = mTargetRoamBSSID;
5939        }
5940        if (config.scanResultCache == null) {
5941            return null;
5942        }
5943        return config.scanResultCache.get(BSSID);
5944    }
5945
5946    String getCurrentBSSID() {
5947        if (linkDebouncing) {
5948            return null;
5949        }
5950        return mLastBssid;
5951    }
5952
5953    class ConnectModeState extends State {
5954        @Override
5955        public boolean processMessage(Message message) {
5956            WifiConfiguration config;
5957            int netId;
5958            boolean ok;
5959            boolean didDisconnect;
5960            String bssid;
5961            String ssid;
5962            NetworkUpdateResult result;
5963            logStateAndMessage(message, getClass().getSimpleName());
5964
5965            switch (message.what) {
5966                case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
5967                    didBlackListBSSID = false;
5968                    bssid = (String) message.obj;
5969                    if (bssid == null || TextUtils.isEmpty(bssid)) {
5970                        // If BSSID is null, use the target roam BSSID
5971                        bssid = mTargetRoamBSSID;
5972                    }
5973                    if (bssid != null) {
5974                        // If we have a BSSID, tell configStore to black list it
5975                        synchronized(mScanResultCache) {
5976                            didBlackListBSSID = mWifiConfigStore.handleBSSIDBlackList
5977                                    (mLastNetworkId, bssid, false);
5978                        }
5979                    }
5980                    mSupplicantStateTracker.sendMessage(WifiMonitor.ASSOCIATION_REJECTION_EVENT);
5981                    break;
5982                case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
5983                    mSupplicantStateTracker.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT);
5984                    break;
5985                case WifiMonitor.SSID_TEMP_DISABLED:
5986                case WifiMonitor.SSID_REENABLED:
5987                    String substr = (String) message.obj;
5988                    String en = message.what == WifiMonitor.SSID_TEMP_DISABLED ?
5989                            "temp-disabled" : "re-enabled";
5990                    loge("ConnectModeState SSID state=" + en + " nid="
5991                            + Integer.toString(message.arg1) + " [" + substr + "]");
5992                    synchronized(mScanResultCache) {
5993                        mWifiConfigStore.handleSSIDStateChange(message.arg1, message.what ==
5994                                WifiMonitor.SSID_REENABLED, substr, mWifiInfo.getBSSID());
5995                    }
5996                    break;
5997                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
5998                    SupplicantState state = handleSupplicantStateChange(message);
5999                    // A driver/firmware hang can now put the interface in a down state.
6000                    // We detect the interface going down and recover from it
6001                    if (!SupplicantState.isDriverActive(state)) {
6002                        if (mNetworkInfo.getState() != NetworkInfo.State.DISCONNECTED) {
6003                            handleNetworkDisconnect();
6004                        }
6005                        log("Detected an interface down, restart driver");
6006                        transitionTo(mDriverStoppedState);
6007                        sendMessage(CMD_START_DRIVER);
6008                        break;
6009                    }
6010
6011                    // Supplicant can fail to report a NETWORK_DISCONNECTION_EVENT
6012                    // when authentication times out after a successful connection,
6013                    // we can figure this from the supplicant state. If supplicant
6014                    // state is DISCONNECTED, but the mNetworkInfo says we are not
6015                    // disconnected, we need to handle a disconnection
6016                    if (!linkDebouncing && state == SupplicantState.DISCONNECTED &&
6017                            mNetworkInfo.getState() != NetworkInfo.State.DISCONNECTED) {
6018                        if (DBG) log("Missed CTRL-EVENT-DISCONNECTED, disconnect");
6019                        handleNetworkDisconnect();
6020                        transitionTo(mDisconnectedState);
6021                    }
6022                    break;
6023                case WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST:
6024                    if (message.arg1 == 1) {
6025                        mWifiNative.disconnect();
6026                        mTemporarilyDisconnectWifi = true;
6027                    } else {
6028                        mWifiNative.reconnect();
6029                        mTemporarilyDisconnectWifi = false;
6030                    }
6031                    break;
6032                case CMD_ADD_OR_UPDATE_NETWORK:
6033                    config = (WifiConfiguration) message.obj;
6034                    int res = mWifiConfigStore.addOrUpdateNetwork(config, message.sendingUid);
6035                    if (res < 0) {
6036                        messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
6037                    } else {
6038                        WifiConfiguration curConfig = getCurrentWifiConfiguration();
6039                        if (curConfig != null && config != null) {
6040                            if (curConfig.priority < config.priority
6041                                    && config.status == WifiConfiguration.Status.ENABLED) {
6042                                // Interpret this as a connect attempt
6043                                // Set the last selected configuration so as to allow the system to
6044                                // stick the last user choice without persisting the choice
6045                                mWifiConfigStore.setLastSelectedConfiguration(res);
6046
6047                                // Remember time of last connection attempt
6048                                lastConnectAttempt = System.currentTimeMillis();
6049
6050                                mWifiConnectionStatistics.numWifiManagerJoinAttempt++;
6051
6052                                // As a courtesy to the caller, trigger a scan now
6053                                startScan(ADD_OR_UPDATE_SOURCE, 0, null, null);
6054                            }
6055                        }
6056                    }
6057                    replyToMessage(message, CMD_ADD_OR_UPDATE_NETWORK, res);
6058                    break;
6059                case CMD_REMOVE_NETWORK:
6060                    ok = mWifiConfigStore.removeNetwork(message.arg1);
6061                    if (!ok) {
6062                        messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
6063                    }
6064                    replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
6065                    break;
6066                case CMD_ENABLE_NETWORK:
6067                    boolean others = message.arg2 == 1;
6068                    // Tell autojoin the user did try to select to that network
6069                    // However, do NOT persist the choice by bumping the priority of the network
6070                    if (others) {
6071                        mWifiAutoJoinController.
6072                                updateConfigurationHistory(message.arg1, true, false);
6073                        // Set the last selected configuration so as to allow the system to
6074                        // stick the last user choice without persisting the choice
6075                        mWifiConfigStore.setLastSelectedConfiguration(message.arg1);
6076
6077                        // Remember time of last connection attempt
6078                        lastConnectAttempt = System.currentTimeMillis();
6079
6080                        mWifiConnectionStatistics.numWifiManagerJoinAttempt++;
6081                    }
6082                    // Cancel auto roam requests
6083                    autoRoamSetBSSID(message.arg1, "any");
6084
6085                    ok = mWifiConfigStore.enableNetwork(message.arg1, message.arg2 == 1);
6086                    if (!ok) {
6087                        messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
6088                    }
6089                    replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
6090                    break;
6091                case CMD_ENABLE_ALL_NETWORKS:
6092                    long time = android.os.SystemClock.elapsedRealtime();
6093                    if (time - mLastEnableAllNetworksTime > MIN_INTERVAL_ENABLE_ALL_NETWORKS_MS) {
6094                        mWifiConfigStore.enableAllNetworks();
6095                        mLastEnableAllNetworksTime = time;
6096                    }
6097                    break;
6098                case WifiManager.DISABLE_NETWORK:
6099                    if (mWifiConfigStore.disableNetwork(message.arg1,
6100                            WifiConfiguration.DISABLED_BY_WIFI_MANAGER) == true) {
6101                        replyToMessage(message, WifiManager.DISABLE_NETWORK_SUCCEEDED);
6102                    } else {
6103                        messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
6104                        replyToMessage(message, WifiManager.DISABLE_NETWORK_FAILED,
6105                                WifiManager.ERROR);
6106                    }
6107                    break;
6108                case CMD_BLACKLIST_NETWORK:
6109                    mWifiNative.addToBlacklist((String) message.obj);
6110                    break;
6111                case CMD_CLEAR_BLACKLIST:
6112                    mWifiNative.clearBlacklist();
6113                    break;
6114                case CMD_SAVE_CONFIG:
6115                    ok = mWifiConfigStore.saveConfig();
6116
6117                    if (DBG) loge("wifistatemachine did save config " + ok);
6118                    replyToMessage(message, CMD_SAVE_CONFIG, ok ? SUCCESS : FAILURE);
6119
6120                    // Inform the backup manager about a data change
6121                    IBackupManager ibm = IBackupManager.Stub.asInterface(
6122                            ServiceManager.getService(Context.BACKUP_SERVICE));
6123                    if (ibm != null) {
6124                        try {
6125                            ibm.dataChanged("com.android.providers.settings");
6126                        } catch (Exception e) {
6127                            // Try again later
6128                        }
6129                    }
6130                    break;
6131                case CMD_GET_CONFIGURED_NETWORKS:
6132                    replyToMessage(message, message.what,
6133                            mWifiConfigStore.getConfiguredNetworks());
6134                    break;
6135                case WifiMonitor.SUP_REQUEST_IDENTITY:
6136                    // Supplicant lacks credentials to connect to that network, hence black list
6137                    ssid = (String) message.obj;
6138
6139                    if (targetWificonfiguration != null && ssid != null
6140                            && targetWificonfiguration.SSID != null
6141                            && targetWificonfiguration.SSID.equals("\"" + ssid + "\"")) {
6142                        mWifiConfigStore.handleSSIDStateChange(targetWificonfiguration.networkId,
6143                                false, "AUTH_FAILED no identity", null);
6144                    }
6145                    // Disconnect now, as we don't have any way to fullfill the  supplicant request.
6146                    mWifiConfigStore.setLastSelectedConfiguration
6147                            (WifiConfiguration.INVALID_NETWORK_ID);
6148                    mWifiNative.disconnect();
6149                    break;
6150                case WifiMonitor.SUP_REQUEST_SIM_AUTH:
6151                    logd("Received SUP_REQUEST_SIM_AUTH");
6152                    SimAuthRequestData requestData = (SimAuthRequestData) message.obj;
6153                    if (requestData != null) {
6154                        if (requestData.protocol == WifiEnterpriseConfig.Eap.SIM) {
6155                            handleGsmAuthRequest(requestData);
6156                        } else if (requestData.protocol == WifiEnterpriseConfig.Eap.AKA) {
6157                            handle3GAuthRequest(requestData);
6158                        }
6159                    } else {
6160                        loge("Invalid sim auth request");
6161                    }
6162                    break;
6163                case CMD_GET_PRIVILEGED_CONFIGURED_NETWORKS:
6164                    replyToMessage(message, message.what,
6165                            mWifiConfigStore.getPrivilegedConfiguredNetworks());
6166                    break;
6167                    /* Do a redundant disconnect without transition */
6168                case CMD_DISCONNECT:
6169                    mWifiConfigStore.setLastSelectedConfiguration
6170                            (WifiConfiguration.INVALID_NETWORK_ID);
6171                    mWifiNative.disconnect();
6172                    break;
6173                case CMD_RECONNECT:
6174                    lastConnectAttempt = System.currentTimeMillis();
6175                    mWifiNative.reconnect();
6176                    break;
6177                case CMD_REASSOCIATE:
6178                    lastConnectAttempt = System.currentTimeMillis();
6179                    mWifiNative.reassociate();
6180                    break;
6181                case CMD_RELOAD_TLS_AND_RECONNECT:
6182                    if (mWifiConfigStore.needsUnlockedKeyStore()) {
6183                        logd("Reconnecting to give a chance to un-connected TLS networks");
6184                        mWifiNative.disconnect();
6185                        lastConnectAttempt = System.currentTimeMillis();
6186                        mWifiNative.reconnect();
6187                    }
6188                    break;
6189                case CMD_AUTO_ROAM:
6190                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
6191                    return HANDLED;
6192                case CMD_AUTO_CONNECT:
6193                    /* Work Around: wpa_supplicant can get in a bad state where it returns a non
6194                     * associated status to the STATUS command but somehow-someplace still thinks
6195                     * it is associated and thus will ignore select/reconnect command with
6196                     * following message:
6197                     * "Already associated with the selected network - do nothing"
6198                     *
6199                     * Hence, sends a disconnect to supplicant first.
6200                     */
6201                    didDisconnect = false;
6202                    if (getCurrentState() != mDisconnectedState) {
6203                        /** Supplicant will ignore the reconnect if we are currently associated,
6204                         * hence trigger a disconnect
6205                         */
6206                        didDisconnect = true;
6207                        mWifiNative.disconnect();
6208                    }
6209
6210                    /* connect command coming from auto-join */
6211                    config = (WifiConfiguration) message.obj;
6212                    netId = message.arg1;
6213                    int roam = message.arg2;
6214                    loge("CMD_AUTO_CONNECT sup state "
6215                            + mSupplicantStateTracker.getSupplicantStateName()
6216                            + " my state " + getCurrentState().getName()
6217                            + " nid=" + Integer.toString(netId)
6218                            + " roam=" + Integer.toString(roam));
6219                    if (config == null) {
6220                        loge("AUTO_CONNECT and no config, bail out...");
6221                        break;
6222                    }
6223
6224                    /* Make sure we cancel any previous roam request */
6225                    autoRoamSetBSSID(netId, config.BSSID);
6226
6227                    /* Save the network config */
6228                    loge("CMD_AUTO_CONNECT will save config -> " + config.SSID
6229                            + " nid=" + Integer.toString(netId));
6230                    result = mWifiConfigStore.saveNetwork(config, -1);
6231                    netId = result.getNetworkId();
6232                    loge("CMD_AUTO_CONNECT did save config -> "
6233                            + " nid=" + Integer.toString(netId));
6234
6235                    // Make sure the network is enabled, since supplicant will not reenable it
6236                    mWifiConfigStore.enableNetworkWithoutBroadcast(netId, false);
6237
6238                    if (mWifiConfigStore.selectNetwork(netId) &&
6239                            mWifiNative.reconnect()) {
6240                        lastConnectAttempt = System.currentTimeMillis();
6241                        targetWificonfiguration = mWifiConfigStore.getWifiConfiguration(netId);
6242                        // We selected a better config,
6243                        // maybe because we could not see the last user
6244                        // selection, then forget it. We will remember the selection
6245                        // only if it was persisted.
6246                        mWifiConfigStore.
6247                                setLastSelectedConfiguration(WifiConfiguration.INVALID_NETWORK_ID);
6248
6249                        mAutoRoaming = roam;
6250                        if (isRoaming() || linkDebouncing) {
6251                            transitionTo(mRoamingState);
6252                        } else if (didDisconnect) {
6253                            transitionTo(mDisconnectingState);
6254                        } else {
6255                            transitionTo(mDisconnectedState);
6256                        }
6257                    } else {
6258                        loge("Failed to connect config: " + config + " netId: " + netId);
6259                        replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
6260                                WifiManager.ERROR);
6261                        break;
6262                    }
6263                    break;
6264                case WifiManager.CONNECT_NETWORK:
6265                    /**
6266                     *  The connect message can contain a network id passed as arg1 on message or
6267                     * or a config passed as obj on message.
6268                     * For a new network, a config is passed to create and connect.
6269                     * For an existing network, a network id is passed
6270                     */
6271                    netId = message.arg1;
6272                    config = (WifiConfiguration) message.obj;
6273                    mWifiConnectionStatistics.numWifiManagerJoinAttempt++;
6274
6275                    /* Save the network config */
6276                    if (config != null) {
6277                        result = mWifiConfigStore.saveNetwork(config, message.sendingUid);
6278                        netId = result.getNetworkId();
6279                    }
6280                    config = mWifiConfigStore.getWifiConfiguration(netId);
6281
6282                    if (config == null) {
6283                        loge("CONNECT_NETWORK id=" + Integer.toString(netId) + " "
6284                                + mSupplicantStateTracker.getSupplicantStateName() + " my state "
6285                                + getCurrentState().getName());
6286                    } else {
6287                        String wasSkipped = config.autoJoinBailedDueToLowRssi ? " skipped" : "";
6288                        loge("CONNECT_NETWORK id=" + Integer.toString(netId)
6289                                + " config=" + config.SSID
6290                                + " cnid=" + config.networkId
6291                                + " supstate=" + mSupplicantStateTracker.getSupplicantStateName()
6292                                + " my state " + getCurrentState().getName()
6293                                + " uid = " + message.sendingUid
6294                                + wasSkipped);
6295                    }
6296
6297                    autoRoamSetBSSID(netId, "any");
6298
6299                    if (message.sendingUid == Process.WIFI_UID
6300                        || message.sendingUid == Process.SYSTEM_UID) {
6301                        // As a sanity measure, clear the BSSID in the supplicant network block.
6302                        // If system or Wifi Settings want to connect, they will not
6303                        // specify the BSSID.
6304                        // If an app however had added a BSSID to this configuration, and the BSSID
6305                        // was wrong, Then we would forever fail to connect until that BSSID
6306                        // is cleaned up.
6307                        clearConfigBSSID(config, "CONNECT_NETWORK");
6308                    }
6309
6310                    mAutoRoaming = WifiAutoJoinController.AUTO_JOIN_IDLE;
6311
6312                    /* Tell autojoin the user did try to connect to that network */
6313                    mWifiAutoJoinController.updateConfigurationHistory(netId, true, true);
6314
6315                    mWifiConfigStore.setLastSelectedConfiguration(netId);
6316
6317                    didDisconnect = false;
6318                    if (mLastNetworkId != WifiConfiguration.INVALID_NETWORK_ID
6319                            && mLastNetworkId != netId) {
6320                        /** Supplicant will ignore the reconnect if we are currently associated,
6321                         * hence trigger a disconnect
6322                         */
6323                        didDisconnect = true;
6324                        mWifiNative.disconnect();
6325                    }
6326
6327                    // Make sure the network is enabled, since supplicant will not reenable it
6328                    mWifiConfigStore.enableNetworkWithoutBroadcast(netId, false);
6329
6330                    if (mWifiConfigStore.selectNetwork(netId) &&
6331                            mWifiNative.reconnect()) {
6332                        lastConnectAttempt = System.currentTimeMillis();
6333                        targetWificonfiguration = mWifiConfigStore.getWifiConfiguration(netId);
6334
6335                        /* The state tracker handles enabling networks upon completion/failure */
6336                        mSupplicantStateTracker.sendMessage(WifiManager.CONNECT_NETWORK);
6337                        replyToMessage(message, WifiManager.CONNECT_NETWORK_SUCCEEDED);
6338                        if (didDisconnect) {
6339                            /* Expect a disconnection from the old connection */
6340                            transitionTo(mDisconnectingState);
6341                        } else {
6342                            /**
6343                             *  Directly go to disconnected state where we
6344                             * process the connection events from supplicant
6345                             **/
6346                            transitionTo(mDisconnectedState);
6347                        }
6348                    } else {
6349                        loge("Failed to connect config: " + config + " netId: " + netId);
6350                        replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
6351                                WifiManager.ERROR);
6352                        break;
6353                    }
6354                    break;
6355                case WifiManager.SAVE_NETWORK:
6356                    mWifiConnectionStatistics.numWifiManagerJoinAttempt++;
6357                    // Fall thru
6358                case WifiStateMachine.CMD_AUTO_SAVE_NETWORK:
6359                    lastSavedConfigurationAttempt = null; // Used for debug
6360                    config = (WifiConfiguration) message.obj;
6361                    if (config == null) {
6362                        loge("ERROR: SAVE_NETWORK with null configuration"
6363                                + mSupplicantStateTracker.getSupplicantStateName()
6364                                + " my state " + getCurrentState().getName());
6365                        messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
6366                        replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED,
6367                                WifiManager.ERROR);
6368                        break;
6369                    }
6370                    lastSavedConfigurationAttempt = new WifiConfiguration(config);
6371                    int nid = config.networkId;
6372                    loge("SAVE_NETWORK id=" + Integer.toString(nid)
6373                                + " config=" + config.SSID
6374                                + " nid=" + config.networkId
6375                                + " supstate=" + mSupplicantStateTracker.getSupplicantStateName()
6376                                + " my state " + getCurrentState().getName());
6377
6378                    result = mWifiConfigStore.saveNetwork(config, -1);
6379                    if (result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID) {
6380                        if (mWifiInfo.getNetworkId() == result.getNetworkId()) {
6381                            if (result.hasIpChanged()) {
6382                                // The currently connection configuration was changed
6383                                // We switched from DHCP to static or from static to DHCP, or the
6384                                // static IP address has changed.
6385                                log("Reconfiguring IP on connection");
6386                                // TODO: clear addresses and disable IPv6
6387                                // to simplify obtainingIpState.
6388                                transitionTo(mObtainingIpState);
6389                            }
6390                            if (result.hasProxyChanged()) {
6391                                log("Reconfiguring proxy on connection");
6392                                updateLinkProperties(CMD_UPDATE_LINKPROPERTIES);
6393                            }
6394                        }
6395                        replyToMessage(message, WifiManager.SAVE_NETWORK_SUCCEEDED);
6396                        if (VDBG) {
6397                           loge("Success save network nid="
6398                                        + Integer.toString(result.getNetworkId()));
6399                        }
6400
6401                        synchronized(mScanResultCache) {
6402                            /**
6403                             * If the command comes from WifiManager, then
6404                             * tell autojoin the user did try to modify and save that network,
6405                             * and interpret the SAVE_NETWORK as a request to connect
6406                             */
6407                            boolean user = message.what == WifiManager.SAVE_NETWORK;
6408                            mWifiAutoJoinController.updateConfigurationHistory(result.getNetworkId()
6409                                    , user, true);
6410                            mWifiAutoJoinController.attemptAutoJoin();
6411                        }
6412                    } else {
6413                        loge("Failed to save network");
6414                        messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
6415                        replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED,
6416                                WifiManager.ERROR);
6417                    }
6418                    break;
6419                case WifiManager.FORGET_NETWORK:
6420                    // Debug only, remember last configuration that was forgotten
6421                    WifiConfiguration toRemove
6422                            = mWifiConfigStore.getWifiConfiguration(message.arg1);
6423                    if (toRemove == null) {
6424                        lastForgetConfigurationAttempt = null;
6425                    } else {
6426                        lastForgetConfigurationAttempt = new WifiConfiguration(toRemove);
6427                    }
6428                    if (mWifiConfigStore.forgetNetwork(message.arg1)) {
6429                        replyToMessage(message, WifiManager.FORGET_NETWORK_SUCCEEDED);
6430                    } else {
6431                        loge("Failed to forget network");
6432                        replyToMessage(message, WifiManager.FORGET_NETWORK_FAILED,
6433                                WifiManager.ERROR);
6434                    }
6435                    break;
6436                case WifiManager.START_WPS:
6437                    WpsInfo wpsInfo = (WpsInfo) message.obj;
6438                    WpsResult wpsResult;
6439                    switch (wpsInfo.setup) {
6440                        case WpsInfo.PBC:
6441                            wpsResult = mWifiConfigStore.startWpsPbc(wpsInfo);
6442                            break;
6443                        case WpsInfo.KEYPAD:
6444                            wpsResult = mWifiConfigStore.startWpsWithPinFromAccessPoint(wpsInfo);
6445                            break;
6446                        case WpsInfo.DISPLAY:
6447                            wpsResult = mWifiConfigStore.startWpsWithPinFromDevice(wpsInfo);
6448                            break;
6449                        default:
6450                            wpsResult = new WpsResult(Status.FAILURE);
6451                            loge("Invalid setup for WPS");
6452                            break;
6453                    }
6454                    mWifiConfigStore.setLastSelectedConfiguration
6455                            (WifiConfiguration.INVALID_NETWORK_ID);
6456                    if (wpsResult.status == Status.SUCCESS) {
6457                        replyToMessage(message, WifiManager.START_WPS_SUCCEEDED, wpsResult);
6458                        transitionTo(mWpsRunningState);
6459                    } else {
6460                        loge("Failed to start WPS with config " + wpsInfo.toString());
6461                        replyToMessage(message, WifiManager.WPS_FAILED, WifiManager.ERROR);
6462                    }
6463                    break;
6464                case WifiMonitor.NETWORK_CONNECTION_EVENT:
6465                    if (DBG) log("Network connection established");
6466                    mLastNetworkId = message.arg1;
6467                    mLastBssid = (String) message.obj;
6468
6469                    mWifiInfo.setBSSID(mLastBssid);
6470                    mWifiInfo.setNetworkId(mLastNetworkId);
6471                    // Send event to CM & network change broadcast
6472                    setNetworkDetailedState(DetailedState.OBTAINING_IPADDR);
6473                    sendNetworkStateChangeBroadcast(mLastBssid);
6474                    transitionTo(mObtainingIpState);
6475                    break;
6476                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
6477                    // Calling handleNetworkDisconnect here is redundant because we might already
6478                    // have called it when leaving L2ConnectedState to go to disconnecting state
6479                    // or thru other path
6480                    // We should normally check the mWifiInfo or mLastNetworkId so as to check
6481                    // if they are valid, and only in this case call handleNEtworkDisconnect,
6482                    // TODO: this should be fixed for a L MR release
6483                    // The side effect of calling handleNetworkDisconnect twice is that a bunch of
6484                    // idempotent commands are executed twice (stopping Dhcp, enabling the SPS mode
6485                    // at the chip etc...
6486                    if (DBG) log("ConnectModeState: Network connection lost ");
6487                    handleNetworkDisconnect();
6488                    transitionTo(mDisconnectedState);
6489                    break;
6490                default:
6491                    return NOT_HANDLED;
6492            }
6493            return HANDLED;
6494        }
6495    }
6496
6497    private class WifiNetworkAgent extends NetworkAgent {
6498        public WifiNetworkAgent(Looper l, Context c, String TAG, NetworkInfo ni,
6499                NetworkCapabilities nc, LinkProperties lp, int score) {
6500            super(l, c, TAG, ni, nc, lp, score);
6501        }
6502        protected void unwanted() {
6503            // Ignore if we're not the current networkAgent.
6504            if (this != mNetworkAgent) return;
6505            if (DBG) log("WifiNetworkAgent -> Wifi unwanted score "
6506                    + Integer.toString(mWifiInfo.score));
6507            unwantedNetwork(network_status_unwanted_disconnect);
6508        }
6509
6510        protected void networkStatus(int status) {
6511            if (status == NetworkAgent.INVALID_NETWORK) {
6512                if (DBG) log("WifiNetworkAgent -> Wifi networkStatus invalid score "
6513                        + Integer.toString(mWifiInfo.score));
6514                unwantedNetwork(network_status_unwanted_disable_autojoin);
6515            }
6516        }
6517    }
6518
6519    void unwantedNetwork(int reason) {
6520        sendMessage(CMD_UNWANTED_NETWORK, reason);
6521    }
6522
6523
6524    boolean startScanForConfiguration(WifiConfiguration config, boolean restrictChannelList) {
6525        if (config == null)
6526            return false;
6527        HashSet<Integer> channels
6528                = mWifiConfigStore.makeChannelList(config,
6529                ONE_HOUR_MILLI, restrictChannelList);
6530        if (channels != null && channels.size() != 0) {
6531            StringBuilder freqs = new StringBuilder();
6532            boolean first = true;
6533            for (Integer channel : channels) {
6534                if (!first)
6535                    freqs.append(",");
6536                freqs.append(channel.toString());
6537                first = false;
6538            }
6539            if (DBG) {
6540                loge("WifiStateMachine starting scan with " + freqs);
6541            }
6542            // Call wifi native to start the scan
6543            if (startScanNative(
6544                    WifiNative.SCAN_WITHOUT_CONNECTION_SETUP,
6545                    freqs.toString())) {
6546                // Only count battery consumption if scan request is accepted
6547                noteScanStart(SCAN_ALARM_SOURCE, null);
6548                // Return true
6549            }
6550            messageHandlingStatus = MESSAGE_HANDLING_STATUS_OK;
6551            return true;
6552        } else {
6553            if (DBG) loge("WifiStateMachine no channels for " + config.configKey());
6554            return false;
6555        }
6556    }
6557
6558    void clearCurrentConfigBSSID(String dbg) {
6559        // Clear the bssid in the current config's network block
6560        WifiConfiguration config = getCurrentWifiConfiguration();
6561        if (config == null)
6562            return;
6563        clearConfigBSSID(config, dbg);
6564    }
6565    void clearConfigBSSID(WifiConfiguration config, String dbg) {
6566        if (config == null)
6567            return;
6568        if (DBG) {
6569            loge(dbg + " " + mTargetRoamBSSID + " config " + config.configKey()
6570                    + " config.bssid " + config.BSSID);
6571        }
6572        config.autoJoinBSSID = "any";
6573        config.BSSID = "any";
6574        if (DBG) {
6575           loge(dbg + " " + config.SSID
6576                    + " nid=" + Integer.toString(config.networkId));
6577        }
6578        mWifiConfigStore.saveWifiConfigBSSID(config);
6579    }
6580
6581    class L2ConnectedState extends State {
6582        @Override
6583        public void enter() {
6584            mRssiPollToken++;
6585            if (mEnableRssiPolling) {
6586                sendMessage(CMD_RSSI_POLL, mRssiPollToken, 0);
6587            }
6588            if (mNetworkAgent != null) {
6589                loge("Have NetworkAgent when entering L2Connected");
6590                setNetworkDetailedState(DetailedState.DISCONNECTED);
6591            }
6592            setNetworkDetailedState(DetailedState.CONNECTING);
6593
6594            if (TextUtils.isEmpty(mTcpBufferSizes) == false) {
6595                mLinkProperties.setTcpBufferSizes(mTcpBufferSizes);
6596            }
6597            mNetworkAgent = new WifiNetworkAgent(getHandler().getLooper(), mContext,
6598                    "WifiNetworkAgent", mNetworkInfo, mNetworkCapabilitiesFilter,
6599                    mLinkProperties, 60);
6600
6601            // We must clear the config BSSID, as the wifi chipset may decide to roam
6602            // from this point on and having the BSSID specified in the network block would
6603            // cause the roam to faile and the device to disconnect
6604            clearCurrentConfigBSSID("L2ConnectedState");
6605        }
6606
6607        @Override
6608        public void exit() {
6609            // This is handled by receiving a NETWORK_DISCONNECTION_EVENT in ConnectModeState
6610            // Bug: 15347363
6611            // For paranoia's sake, call handleNetworkDisconnect
6612            // only if BSSID is null or last networkId
6613            // is not invalid.
6614            if (DBG) {
6615                StringBuilder sb = new StringBuilder();
6616                sb.append("leaving L2ConnectedState state nid=" + Integer.toString(mLastNetworkId));
6617                if (mLastBssid !=null) {
6618                    sb.append(" ").append(mLastBssid);
6619                }
6620            }
6621            if (mLastBssid != null || mLastNetworkId != WifiConfiguration.INVALID_NETWORK_ID) {
6622                handleNetworkDisconnect();
6623            }
6624        }
6625
6626        @Override
6627        public boolean processMessage(Message message) {
6628            logStateAndMessage(message, getClass().getSimpleName());
6629
6630            switch (message.what) {
6631              case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
6632                  handlePreDhcpSetup();
6633                  break;
6634              case DhcpStateMachine.CMD_POST_DHCP_ACTION:
6635                  handlePostDhcpSetup();
6636                  if (message.arg1 == DhcpStateMachine.DHCP_SUCCESS) {
6637                      if (DBG) log("WifiStateMachine DHCP successful");
6638                      handleIPv4Success((DhcpResults) message.obj, DhcpStateMachine.DHCP_SUCCESS);
6639                      // We advance to mVerifyingLinkState because handleIPv4Success will call
6640                      // updateLinkProperties, which then sends CMD_IP_CONFIGURATION_SUCCESSFUL.
6641                  } else if (message.arg1 == DhcpStateMachine.DHCP_FAILURE) {
6642                      if (DBG) {
6643                          int count = -1;
6644                          WifiConfiguration config = getCurrentWifiConfiguration();
6645                          if (config != null) {
6646                              count = config.numConnectionFailures;
6647                          }
6648                          log("WifiStateMachine DHCP failure count=" + count);
6649                      }
6650                      handleIPv4Failure(DhcpStateMachine.DHCP_FAILURE);
6651                      // As above, we transition to mDisconnectingState via updateLinkProperties.
6652                  }
6653                  break;
6654                case CMD_IP_CONFIGURATION_SUCCESSFUL:
6655                    handleSuccessfulIpConfiguration();
6656                    sendConnectedState();
6657                    transitionTo(mConnectedState);
6658                    break;
6659                case CMD_IP_CONFIGURATION_LOST:
6660                    handleIpConfigurationLost();
6661                    transitionTo(mDisconnectingState);
6662                    break;
6663                case CMD_DISCONNECT:
6664                    mWifiNative.disconnect();
6665                    transitionTo(mDisconnectingState);
6666                    break;
6667                case WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST:
6668                    if (message.arg1 == 1) {
6669                        mWifiNative.disconnect();
6670                        mTemporarilyDisconnectWifi = true;
6671                        transitionTo(mDisconnectingState);
6672                    }
6673                    break;
6674                case CMD_SET_OPERATIONAL_MODE:
6675                    if (message.arg1 != CONNECT_MODE) {
6676                        sendMessage(CMD_DISCONNECT);
6677                        deferMessage(message);
6678                        if (message.arg1 == SCAN_ONLY_WITH_WIFI_OFF_MODE) {
6679                            noteWifiDisabledWhileAssociated();
6680                        }
6681                    }
6682                    break;
6683                case CMD_SET_COUNTRY_CODE:
6684                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_DEFERRED;
6685                    deferMessage(message);
6686                    break;
6687                case CMD_START_SCAN:
6688                    if (DBG) {
6689                        loge("WifiStateMachine CMD_START_SCAN source " + message.arg1
6690                              + " txSuccessRate="+String.format( "%.2f", mWifiInfo.txSuccessRate)
6691                              + " rxSuccessRate="+String.format( "%.2f", mWifiInfo.rxSuccessRate)
6692                              + " targetRoamBSSID=" + mTargetRoamBSSID
6693                              + " RSSI=" + mWifiInfo.getRssi());
6694                    }
6695                    if (message.arg1 == SCAN_ALARM_SOURCE) {
6696                        boolean tryFullBandScan = false;
6697                        boolean restrictChannelList = false;
6698                        long now_ms = System.currentTimeMillis();
6699                        if (DBG) {
6700                            loge("WifiStateMachine CMD_START_SCAN with age="
6701                                    + Long.toString(now_ms - lastFullBandConnectedTimeMilli)
6702                                    + " interval=" + fullBandConnectedTimeIntervalMilli
6703                                    + " maxinterval=" + maxFullBandConnectedTimeIntervalMilli);
6704                        }
6705                        if (mWifiInfo != null) {
6706                            if (mWifiConfigStore.enableFullBandScanWhenAssociated &&
6707                                    (now_ms - lastFullBandConnectedTimeMilli)
6708                                    > fullBandConnectedTimeIntervalMilli) {
6709                                if (DBG) {
6710                                    loge("WifiStateMachine CMD_START_SCAN try full band scan age="
6711                                         + Long.toString(now_ms - lastFullBandConnectedTimeMilli)
6712                                         + " interval=" + fullBandConnectedTimeIntervalMilli
6713                                         + " maxinterval=" + maxFullBandConnectedTimeIntervalMilli);
6714                                }
6715                                tryFullBandScan = true;
6716                            }
6717
6718                            if (mWifiInfo.txSuccessRate >
6719                                    mWifiConfigStore.maxTxPacketForPartialScans
6720                                    || mWifiInfo.rxSuccessRate >
6721                                    mWifiConfigStore.maxRxPacketForPartialScans) {
6722                                // Too much traffic at the interface, hence no full band scan
6723                                if (DBG) {
6724                                    loge("WifiStateMachine CMD_START_SCAN " +
6725                                            "prevent full band scan due to pkt rate");
6726                                }
6727                                tryFullBandScan = false;
6728                            }
6729
6730                            if (mWifiInfo.txSuccessRate >
6731                                    mWifiConfigStore.maxTxPacketForPartialScans
6732                                    || mWifiInfo.rxSuccessRate >
6733                                    mWifiConfigStore.maxRxPacketForPartialScans) {
6734                                // Don't scan if lots of packets are being sent
6735                                restrictChannelList = true;
6736                                if (mWifiConfigStore.alwaysEnableScansWhileAssociated == 0) {
6737                                    if (DBG) {
6738                                     loge("WifiStateMachine CMD_START_SCAN source " + message.arg1
6739                                        + " ...and ignore scans"
6740                                        + " tx=" + String.format("%.2f", mWifiInfo.txSuccessRate)
6741                                        + " rx=" + String.format("%.2f", mWifiInfo.rxSuccessRate));
6742                                    }
6743                                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_REFUSED;
6744                                    return HANDLED;
6745                                }
6746                            }
6747                        }
6748
6749                        WifiConfiguration currentConfiguration = getCurrentWifiConfiguration();
6750                        if (DBG) {
6751                            loge("WifiStateMachine CMD_START_SCAN full=" +
6752                                    tryFullBandScan);
6753                        }
6754                        if (currentConfiguration != null) {
6755                            if (fullBandConnectedTimeIntervalMilli
6756                                    < mWifiConfigStore.associatedPartialScanPeriodMilli) {
6757                                // Sanity
6758                                fullBandConnectedTimeIntervalMilli
6759                                        = mWifiConfigStore.associatedPartialScanPeriodMilli;
6760                            }
6761                            if (tryFullBandScan) {
6762                                lastFullBandConnectedTimeMilli = now_ms;
6763                                if (fullBandConnectedTimeIntervalMilli
6764                                        < mWifiConfigStore.associatedFullScanMaxIntervalMilli) {
6765                                    // Increase the interval
6766                                    fullBandConnectedTimeIntervalMilli
6767                                            = fullBandConnectedTimeIntervalMilli
6768                                            * mWifiConfigStore.associatedFullScanBackoff / 8;
6769
6770                                    if (DBG) {
6771                                        loge("WifiStateMachine CMD_START_SCAN bump interval ="
6772                                        + fullBandConnectedTimeIntervalMilli);
6773                                    }
6774                                }
6775                                handleScanRequest(
6776                                        WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, message);
6777                            } else {
6778                                if (!startScanForConfiguration(
6779                                        currentConfiguration, restrictChannelList)) {
6780                                    if (DBG) {
6781                                        loge("WifiStateMachine starting scan, " +
6782                                                " did not find channels -> full");
6783                                    }
6784                                    lastFullBandConnectedTimeMilli = now_ms;
6785                                    if (fullBandConnectedTimeIntervalMilli
6786                                            < mWifiConfigStore.associatedFullScanMaxIntervalMilli) {
6787                                        // Increase the interval
6788                                        fullBandConnectedTimeIntervalMilli
6789                                                = fullBandConnectedTimeIntervalMilli
6790                                                * mWifiConfigStore.associatedFullScanBackoff / 8;
6791
6792                                        if (DBG) {
6793                                            loge("WifiStateMachine CMD_START_SCAN bump interval ="
6794                                                    + fullBandConnectedTimeIntervalMilli);
6795                                        }
6796                                    }
6797                                    handleScanRequest(
6798                                                WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, message);
6799                                }
6800                            }
6801                        } else {
6802                            loge("CMD_START_SCAN : connected mode and no configuration");
6803                            messageHandlingStatus = MESSAGE_HANDLING_STATUS_HANDLING_ERROR;
6804                        }
6805                    } else {
6806                        // Not scan alarm source
6807                        handleScanRequest(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, message);
6808                    }
6809                    break;
6810                    /* Ignore connection to same network */
6811                case WifiManager.CONNECT_NETWORK:
6812                    int netId = message.arg1;
6813                    if (mWifiInfo.getNetworkId() == netId) {
6814                        break;
6815                    }
6816                    return NOT_HANDLED;
6817                    /* Ignore */
6818                case WifiMonitor.NETWORK_CONNECTION_EVENT:
6819                    break;
6820                case CMD_RSSI_POLL:
6821                    if (message.arg1 == mRssiPollToken) {
6822                        if (mWifiConfigStore.enableChipWakeUpWhenAssociated) {
6823                            if (VVDBG) log(" get link layer stats " + mWifiLinkLayerStatsSupported);
6824                            WifiLinkLayerStats stats = getWifiLinkLayerStats(VDBG);
6825                            if (stats != null) {
6826                                // Sanity check the results provided by driver
6827                                if (mWifiInfo.getRssi() != WifiInfo.INVALID_RSSI
6828                                        && (stats.rssi_mgmt == 0
6829                                        || stats.beacon_rx == 0)) {
6830                                    stats = null;
6831                                }
6832                            }
6833                            // Get Info and continue polling
6834                            fetchRssiLinkSpeedAndFrequencyNative();
6835                            calculateWifiScore(stats);
6836                        }
6837                        sendMessageDelayed(obtainMessage(CMD_RSSI_POLL,
6838                                mRssiPollToken, 0), POLL_RSSI_INTERVAL_MSECS);
6839
6840                        if (DBG) sendRssiChangeBroadcast(mWifiInfo.getRssi());
6841                    } else {
6842                        // Polling has completed
6843                    }
6844                    break;
6845                case CMD_ENABLE_RSSI_POLL:
6846                    if (mWifiConfigStore.enableRssiPollWhenAssociated) {
6847                        mEnableRssiPolling = (message.arg1 == 1);
6848                    } else {
6849                        mEnableRssiPolling = false;
6850                    }
6851                    mRssiPollToken++;
6852                    if (mEnableRssiPolling) {
6853                        // First poll
6854                        fetchRssiLinkSpeedAndFrequencyNative();
6855                        sendMessageDelayed(obtainMessage(CMD_RSSI_POLL,
6856                                mRssiPollToken, 0), POLL_RSSI_INTERVAL_MSECS);
6857                    } else {
6858                        cleanWifiScore();
6859                    }
6860                    break;
6861                case WifiManager.RSSI_PKTCNT_FETCH:
6862                    RssiPacketCountInfo info = new RssiPacketCountInfo();
6863                    fetchRssiLinkSpeedAndFrequencyNative();
6864                    info.rssi = mWifiInfo.getRssi();
6865                    fetchPktcntNative(info);
6866                    replyToMessage(message, WifiManager.RSSI_PKTCNT_FETCH_SUCCEEDED, info);
6867                    break;
6868                case CMD_DELAYED_NETWORK_DISCONNECT:
6869                    if (!linkDebouncing && mWifiConfigStore.enableLinkDebouncing) {
6870
6871                        // Ignore if we are not debouncing
6872                        loge("CMD_DELAYED_NETWORK_DISCONNECT and not debouncing - ignore "
6873                                + message.arg1);
6874                        return HANDLED;
6875                    } else {
6876                        loge("CMD_DELAYED_NETWORK_DISCONNECT and debouncing - disconnect "
6877                                + message.arg1);
6878
6879                        linkDebouncing = false;
6880                        // If we are still debouncing while this message comes,
6881                        // it means we were not able to reconnect within the alloted time
6882                        // = LINK_FLAPPING_DEBOUNCE_MSEC
6883                        // and thus, trigger a real disconnect
6884                        handleNetworkDisconnect();
6885                        transitionTo(mDisconnectedState);
6886                    }
6887                    break;
6888                case CMD_ASSOCIATED_BSSID:
6889                    if ((String) message.obj == null) {
6890                        loge("Associated command w/o BSSID");
6891                        break;
6892                    }
6893                    mLastBssid = (String) message.obj;
6894                    mWifiInfo.setBSSID((String) message.obj);
6895                    break;
6896                default:
6897                    return NOT_HANDLED;
6898            }
6899
6900            return HANDLED;
6901        }
6902    }
6903
6904    class ObtainingIpState extends State {
6905        @Override
6906        public void enter() {
6907            if (DBG) {
6908                String key = "";
6909                if (getCurrentWifiConfiguration() != null) {
6910                    key = getCurrentWifiConfiguration().configKey();
6911                }
6912                log("enter ObtainingIpState netId=" + Integer.toString(mLastNetworkId)
6913                        + " " + key + " "
6914                        + " roam=" + mAutoRoaming
6915                        + " static=" + mWifiConfigStore.isUsingStaticIp(mLastNetworkId)
6916                        + " watchdog= " + obtainingIpWatchdogCount);
6917            }
6918
6919            // Reset link Debouncing, indicating we have successfully re-connected to the AP
6920            // We might still be roaming
6921            linkDebouncing = false;
6922
6923            // We must clear the config BSSID, as the wifi chipset may decide to roam
6924            // from this point on and having the BSSID specified in the network block would
6925            // cause the roam to faile and the device to disconnect
6926            clearCurrentConfigBSSID("ObtainingIpAddress");
6927
6928            try {
6929                mNwService.enableIpv6(mInterfaceName);
6930            } catch (RemoteException re) {
6931                loge("Failed to enable IPv6: " + re);
6932            } catch (IllegalStateException e) {
6933                loge("Failed to enable IPv6: " + e);
6934            }
6935
6936            if (!mWifiConfigStore.isUsingStaticIp(mLastNetworkId)) {
6937                if (isRoaming()) {
6938                    renewDhcp();
6939                } else {
6940                    // Remove any IP address on the interface in case we're switching from static
6941                    // IP configuration to DHCP. This is safe because if we get here when not
6942                    // roaming, we don't have a usable address.
6943                    clearIPv4Address(mInterfaceName);
6944                    startDhcp();
6945                }
6946                obtainingIpWatchdogCount++;
6947                loge("Start Dhcp Watchdog " + obtainingIpWatchdogCount);
6948                sendMessageDelayed(obtainMessage(CMD_OBTAINING_IP_ADDRESS_WATCHDOG_TIMER,
6949                        obtainingIpWatchdogCount, 0), OBTAINING_IP_ADDRESS_GUARD_TIMER_MSEC);
6950            } else {
6951                // stop any running dhcp before assigning static IP
6952                stopDhcp();
6953                StaticIpConfiguration config = mWifiConfigStore.getStaticIpConfiguration(
6954                        mLastNetworkId);
6955                if (config.ipAddress == null) {
6956                    loge("Static IP lacks address");
6957                    sendMessage(CMD_STATIC_IP_FAILURE);
6958                } else {
6959                    InterfaceConfiguration ifcg = new InterfaceConfiguration();
6960                    ifcg.setLinkAddress(config.ipAddress);
6961                    ifcg.setInterfaceUp();
6962                    try {
6963                        mNwService.setInterfaceConfig(mInterfaceName, ifcg);
6964                        if (DBG) log("Static IP configuration succeeded");
6965                        DhcpResults dhcpResults = new DhcpResults(config);
6966                        sendMessage(CMD_STATIC_IP_SUCCESS, dhcpResults);
6967                    } catch (RemoteException re) {
6968                        loge("Static IP configuration failed: " + re);
6969                        sendMessage(CMD_STATIC_IP_FAILURE);
6970                    } catch (IllegalStateException e) {
6971                        loge("Static IP configuration failed: " + e);
6972                        sendMessage(CMD_STATIC_IP_FAILURE);
6973                    }
6974                }
6975            }
6976        }
6977      @Override
6978      public boolean processMessage(Message message) {
6979          logStateAndMessage(message, getClass().getSimpleName());
6980
6981          switch(message.what) {
6982              case CMD_STATIC_IP_SUCCESS:
6983                  handleIPv4Success((DhcpResults) message.obj, CMD_STATIC_IP_SUCCESS);
6984                  break;
6985              case CMD_STATIC_IP_FAILURE:
6986                  handleIPv4Failure(CMD_STATIC_IP_FAILURE);
6987                  break;
6988              case CMD_AUTO_CONNECT:
6989              case CMD_AUTO_ROAM:
6990                  messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
6991                  break;
6992              case WifiManager.SAVE_NETWORK:
6993              case WifiStateMachine.CMD_AUTO_SAVE_NETWORK:
6994                  messageHandlingStatus = MESSAGE_HANDLING_STATUS_DEFERRED;
6995                  deferMessage(message);
6996                  break;
6997                  /* Defer any power mode changes since we must keep active power mode at DHCP */
6998              case CMD_SET_HIGH_PERF_MODE:
6999                  messageHandlingStatus = MESSAGE_HANDLING_STATUS_DEFERRED;
7000                  deferMessage(message);
7001                  break;
7002                  /* Defer scan request since we should not switch to other channels at DHCP */
7003              case CMD_START_SCAN:
7004                  messageHandlingStatus = MESSAGE_HANDLING_STATUS_DEFERRED;
7005                  deferMessage(message);
7006                  break;
7007              case CMD_OBTAINING_IP_ADDRESS_WATCHDOG_TIMER:
7008                  if (message.arg1 == obtainingIpWatchdogCount) {
7009                      loge("ObtainingIpAddress: Watchdog Triggered, count="
7010                              + obtainingIpWatchdogCount);
7011                      handleIpConfigurationLost();
7012                      transitionTo(mDisconnectingState);
7013                      break;
7014                  }
7015                  messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
7016                  break;
7017              default:
7018                  return NOT_HANDLED;
7019          }
7020          return HANDLED;
7021      }
7022    }
7023
7024    class VerifyingLinkState extends State {
7025        @Override
7026        public void enter() {
7027            log(getName() + " enter");
7028            setNetworkDetailedState(DetailedState.VERIFYING_POOR_LINK);
7029            mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.VERIFYING_POOR_LINK);
7030            sendNetworkStateChangeBroadcast(mLastBssid);
7031            // End roaming
7032            mAutoRoaming = WifiAutoJoinController.AUTO_JOIN_IDLE;
7033        }
7034        @Override
7035        public boolean processMessage(Message message) {
7036            logStateAndMessage(message, getClass().getSimpleName());
7037
7038            switch (message.what) {
7039                case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
7040                    // Stay here
7041                    log(getName() + " POOR_LINK_DETECTED: no transition");
7042                    break;
7043                case WifiWatchdogStateMachine.GOOD_LINK_DETECTED:
7044                    log(getName() + " GOOD_LINK_DETECTED: transition to captive portal check");
7045
7046                    log(getName() + " GOOD_LINK_DETECTED: transition to CONNECTED");
7047                    sendConnectedState();
7048                    transitionTo(mConnectedState);
7049                    break;
7050                default:
7051                    if (DBG) log(getName() + " what=" + message.what + " NOT_HANDLED");
7052                    return NOT_HANDLED;
7053            }
7054            return HANDLED;
7055        }
7056    }
7057
7058    private void sendConnectedState() {
7059        // Send out a broadcast with the CAPTIVE_PORTAL_CHECK to preserve
7060        // existing behaviour. The captive portal check really happens after we
7061        // transition into DetailedState.CONNECTED.
7062        setNetworkDetailedState(DetailedState.CAPTIVE_PORTAL_CHECK);
7063        mWifiConfigStore.updateStatus(mLastNetworkId,
7064        DetailedState.CAPTIVE_PORTAL_CHECK);
7065        sendNetworkStateChangeBroadcast(mLastBssid);
7066
7067        if (mWifiConfigStore.getLastSelectedConfiguration() != null) {
7068            if (mNetworkAgent != null) mNetworkAgent.explicitlySelected();
7069        }
7070
7071        setNetworkDetailedState(DetailedState.CONNECTED);
7072        mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.CONNECTED);
7073        sendNetworkStateChangeBroadcast(mLastBssid);
7074    }
7075
7076    class RoamingState extends State {
7077        boolean mAssociated;
7078        @Override
7079        public void enter() {
7080            if (DBG) {
7081                log("RoamingState Enter"
7082                        + " mScreenOn=" + mScreenOn );
7083            }
7084            setScanAlarm(false, 0);
7085
7086            // Make sure we disconnect if roaming fails
7087            roamWatchdogCount++;
7088            loge("Start Roam Watchdog " + roamWatchdogCount);
7089            sendMessageDelayed(obtainMessage(CMD_ROAM_WATCHDOG_TIMER,
7090                    roamWatchdogCount, 0), ROAM_GUARD_TIMER_MSEC);
7091            mAssociated = false;
7092        }
7093        @Override
7094        public boolean processMessage(Message message) {
7095            logStateAndMessage(message, getClass().getSimpleName());
7096
7097            switch (message.what) {
7098               case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
7099                    if (DBG) log("Roaming and Watchdog reports poor link -> ignore");
7100                    return HANDLED;
7101               case CMD_UNWANTED_NETWORK:
7102                    if (DBG) log("Roaming and CS doesnt want the network -> ignore");
7103                    return HANDLED;
7104               case CMD_SET_OPERATIONAL_MODE:
7105                    if (message.arg1 != CONNECT_MODE) {
7106                        deferMessage(message);
7107                    }
7108                    break;
7109               case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
7110                    /**
7111                     * If we get a SUPPLICANT_STATE_CHANGE_EVENT indicating a DISCONNECT
7112                     * before NETWORK_DISCONNECTION_EVENT
7113                     * And there is an associated BSSID corresponding to our target BSSID, then
7114                     * we have missed the network disconnection, transition to mDisconnectedState
7115                     * and handle the rest of the events there.
7116                     */
7117                    StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
7118                    if (stateChangeResult.state == SupplicantState.DISCONNECTED
7119                            || stateChangeResult.state == SupplicantState.INACTIVE
7120                            || stateChangeResult.state == SupplicantState.INTERFACE_DISABLED) {
7121                        if (DBG) {
7122                            log("STATE_CHANGE_EVENT in roaming state "
7123                                    + stateChangeResult.toString() );
7124                        }
7125                        if (stateChangeResult.BSSID != null
7126                                && stateChangeResult.BSSID.equals(mTargetRoamBSSID)) {
7127                            handleNetworkDisconnect();
7128                            transitionTo(mDisconnectedState);
7129                        }
7130                    }
7131                    if (stateChangeResult.state == SupplicantState.ASSOCIATED) {
7132                        // We completed the layer2 roaming part
7133                        mAssociated = true;
7134                        if (stateChangeResult.BSSID != null) {
7135                            mTargetRoamBSSID = (String) stateChangeResult.BSSID;
7136                        }
7137                    }
7138                    break;
7139                case CMD_ROAM_WATCHDOG_TIMER:
7140                    if (roamWatchdogCount == message.arg1) {
7141                        if (DBG) log("roaming watchdog! -> disconnect");
7142                        mRoamFailCount++;
7143                        handleNetworkDisconnect();
7144                        mWifiNative.disconnect();
7145                        transitionTo(mDisconnectedState);
7146                    }
7147                    break;
7148               case WifiMonitor.NETWORK_CONNECTION_EVENT:
7149                   if (mAssociated) {
7150                       if (DBG) log("roaming and Network connection established");
7151                       mLastNetworkId = message.arg1;
7152                       mLastBssid = (String) message.obj;
7153                       mWifiInfo.setBSSID(mLastBssid);
7154                       mWifiInfo.setNetworkId(mLastNetworkId);
7155                       mWifiConfigStore.handleBSSIDBlackList(mLastNetworkId, mLastBssid, true);
7156                       transitionTo(mObtainingIpState);
7157                   } else {
7158                       messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
7159                   }
7160                   break;
7161               case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
7162                   // Throw away but only if it corresponds to the network we're roaming to
7163                   String bssid = (String)message.obj;
7164                   if (true) {
7165                       String target = "";
7166                       if (mTargetRoamBSSID != null) target = mTargetRoamBSSID;
7167                       log("NETWORK_DISCONNECTION_EVENT in roaming state"
7168                               + " BSSID=" + bssid
7169                               + " target=" + target);
7170                   }
7171                   if (bssid != null && bssid.equals(mTargetRoamBSSID)) {
7172                       handleNetworkDisconnect();
7173                       transitionTo(mDisconnectedState);
7174                   }
7175                   break;
7176                case WifiMonitor.SSID_TEMP_DISABLED:
7177                    // Auth error while roaming
7178                    loge("SSID_TEMP_DISABLED nid=" + Integer.toString(mLastNetworkId)
7179                            + " id=" + Integer.toString(message.arg1)
7180                            + " isRoaming=" + isRoaming()
7181                            + " roam=" + Integer.toString(mAutoRoaming));
7182                    if (message.arg1 == mLastNetworkId) {
7183                        handleNetworkDisconnect();
7184                        transitionTo(mDisconnectingState);
7185                    }
7186                    return NOT_HANDLED;
7187                case CMD_START_SCAN:
7188                    deferMessage(message);
7189                    break;
7190                default:
7191                    return NOT_HANDLED;
7192            }
7193            return HANDLED;
7194        }
7195
7196        @Override
7197        public void exit() {
7198            loge("WifiStateMachine: Leaving Roaming state");
7199        }
7200    }
7201
7202    class ConnectedState extends State {
7203        @Override
7204        public void enter() {
7205            String address;
7206            updateDefaultRouteMacAddress(1000);
7207            if (DBG) {
7208                log("ConnectedState Enter "
7209                        + " mScreenOn=" + mScreenOn
7210                        + " scanperiod="
7211                        + Integer.toString(mWifiConfigStore.associatedPartialScanPeriodMilli) );
7212            }
7213            if (mScreenOn
7214                    && mWifiConfigStore.enableAutoJoinScanWhenAssociated) {
7215                mCurrentScanAlarmMs = mWifiConfigStore.associatedPartialScanPeriodMilli;
7216                // Scan after 200ms
7217                setScanAlarm(true, 200);
7218            } else {
7219                mCurrentScanAlarmMs = 0;
7220            }
7221            registerConnected();
7222            lastConnectAttempt = 0;
7223            targetWificonfiguration = null;
7224            // Paranoia
7225            linkDebouncing = false;
7226
7227            // Not roaming anymore
7228            mAutoRoaming = WifiAutoJoinController.AUTO_JOIN_IDLE;
7229
7230            if (testNetworkDisconnect) {
7231                testNetworkDisconnectCounter++;
7232                loge("ConnectedState Enter start disconnect test " +
7233                        testNetworkDisconnectCounter);
7234                sendMessageDelayed(obtainMessage(CMD_TEST_NETWORK_DISCONNECT,
7235                        testNetworkDisconnectCounter, 0), 15000);
7236            }
7237
7238            // Reenable all networks, allow for hidden networks to be scanned
7239            mWifiConfigStore.enableAllNetworks();
7240
7241            mLastDriverRoamAttempt = 0;
7242        }
7243        @Override
7244        public boolean processMessage(Message message) {
7245            WifiConfiguration config = null;
7246            logStateAndMessage(message, getClass().getSimpleName());
7247
7248            switch (message.what) {
7249                case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
7250                    if (DBG) log("Watchdog reports poor link");
7251                    transitionTo(mVerifyingLinkState);
7252                    break;
7253                case CMD_UNWANTED_NETWORK:
7254                    if (message.arg1 == network_status_unwanted_disconnect) {
7255                        mWifiConfigStore.handleBadNetworkDisconnectReport(mLastNetworkId, mWifiInfo);
7256                        mWifiNative.disconnect();
7257                        transitionTo(mDisconnectingState);
7258                    } else if (message.arg1 == network_status_unwanted_disconnect) {
7259                        config = getCurrentWifiConfiguration();
7260                        if (config != null) {
7261                            // Disable autojoin
7262                            config.noInternetAccess = true;
7263                        }
7264                    }
7265                    return HANDLED;
7266                case CMD_TEST_NETWORK_DISCONNECT:
7267                    // Force a disconnect
7268                    if (message.arg1 == testNetworkDisconnectCounter) {
7269                        mWifiNative.disconnect();
7270                    }
7271                    break;
7272                case CMD_ASSOCIATED_BSSID:
7273                    // ASSOCIATING to a new BSSID while already connected, indicates
7274                    // that driver is roaming
7275                    mLastDriverRoamAttempt = System.currentTimeMillis();
7276                    return NOT_HANDLED;
7277                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
7278                    long lastRoam = 0;
7279                    if (mLastDriverRoamAttempt != 0) {
7280                        // Calculate time since last driver roam attempt
7281                        lastRoam = System.currentTimeMillis() - mLastDriverRoamAttempt;
7282                        mLastDriverRoamAttempt = 0;
7283                    }
7284                    config = getCurrentWifiConfiguration();
7285                    if (mScreenOn
7286                            && !linkDebouncing
7287                            && config != null
7288                            && config.autoJoinStatus == WifiConfiguration.AUTO_JOIN_ENABLED
7289                            && !mWifiConfigStore.isLastSelectedConfiguration(config)
7290                            && (message.arg2 != 3 /* reason cannot be 3, i.e. locally generated */
7291                                || (lastRoam > 0 && lastRoam < 2000) /* unless driver is roaming */)
7292                            && ((ScanResult.is24GHz(mWifiInfo.getFrequency())
7293                                    && mWifiInfo.getRssi() >
7294                                    WifiConfiguration.BAD_RSSI_24)
7295                                    || (ScanResult.is5GHz(mWifiInfo.getFrequency())
7296                                    && mWifiInfo.getRssi() >
7297                                    WifiConfiguration.BAD_RSSI_5))) {
7298                        // Start de-bouncing the L2 disconnection:
7299                        // this L2 disconnection might be spurious.
7300                        // Hence we allow 7 seconds for the state machine to try
7301                        // to reconnect, go thru the
7302                        // roaming cycle and enter Obtaining IP address
7303                        // before signalling the disconnect to ConnectivityService and L3
7304                        startScanForConfiguration(getCurrentWifiConfiguration(), false);
7305                        linkDebouncing = true;
7306
7307                        sendMessageDelayed(obtainMessage(CMD_DELAYED_NETWORK_DISCONNECT,
7308                                0, mLastNetworkId), LINK_FLAPPING_DEBOUNCE_MSEC);
7309                        if (DBG) {
7310                            log("NETWORK_DISCONNECTION_EVENT in connected state"
7311                                    + " BSSID=" + mWifiInfo.getBSSID()
7312                                    + " RSSI=" + mWifiInfo.getRssi()
7313                                    + " freq=" + mWifiInfo.getFrequency()
7314                                    + " reason=" + message.arg2
7315                                    + " -> debounce");
7316                        }
7317                        return HANDLED;
7318                    } else {
7319                        if (DBG) {
7320                            int ajst = -1;
7321                            if (config != null) ajst = config.autoJoinStatus;
7322                            log("NETWORK_DISCONNECTION_EVENT in connected state"
7323                                    + " BSSID=" + mWifiInfo.getBSSID()
7324                                    + " RSSI=" + mWifiInfo.getRssi()
7325                                    + " freq=" + mWifiInfo.getFrequency()
7326                                    + " was debouncing=" + linkDebouncing
7327                                    + " reason=" + message.arg2
7328                                    + " ajst=" + ajst);
7329                        }
7330                    }
7331                    break;
7332                case CMD_AUTO_ROAM:
7333                    // Clear the driver roam indication since we are attempting a framerwork roam
7334                    mLastDriverRoamAttempt = 0;
7335
7336                    /* Connect command coming from auto-join */
7337                    ScanResult candidate = (ScanResult)message.obj;
7338                    String bssid = "any";
7339                    if (candidate != null && candidate.is5GHz()) {
7340                        // Only lock BSSID for 5GHz networks
7341                        bssid = candidate.BSSID;
7342                    }
7343                    int netId = mLastNetworkId;
7344                    config = getCurrentWifiConfiguration();
7345
7346
7347                    if (config == null) {
7348                        loge("AUTO_ROAM and no config, bail out...");
7349                        break;
7350                    }
7351
7352                    loge("CMD_AUTO_ROAM sup state "
7353                            + mSupplicantStateTracker.getSupplicantStateName()
7354                            + " my state " + getCurrentState().getName()
7355                            + " nid=" + Integer.toString(netId)
7356                            + " config " + config.configKey()
7357                            + " roam=" + Integer.toString(message.arg2)
7358                            + " to " + bssid
7359                            + " targetRoamBSSID " + mTargetRoamBSSID);
7360
7361                    /* Save the BSSID so as to lock it @ firmware */
7362                    if (!autoRoamSetBSSID(config, bssid) && !linkDebouncing) {
7363                        loge("AUTO_ROAM nothing to do");
7364                        // Same BSSID, nothing to do
7365                        messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
7366                        break;
7367                    };
7368
7369                    // Make sure the network is enabled, since supplicant will not reenable it
7370                    mWifiConfigStore.enableNetworkWithoutBroadcast(netId, false);
7371
7372                    boolean ret = false;
7373                    if (netId != mLastNetworkId) {
7374                        if (mWifiConfigStore.selectNetwork(netId) &&
7375                                mWifiNative.reconnect()) {
7376                            ret = true;
7377                        }
7378                    } else {
7379                        ret = mWifiNative.reassociate();
7380                    }
7381                    if (ret) {
7382                        lastConnectAttempt = System.currentTimeMillis();
7383                        targetWificonfiguration = mWifiConfigStore.getWifiConfiguration(netId);
7384
7385                        // replyToMessage(message, WifiManager.CONNECT_NETWORK_SUCCEEDED);
7386                        mAutoRoaming = message.arg2;
7387                        transitionTo(mRoamingState);
7388
7389                    } else {
7390                        loge("Failed to connect config: " + config + " netId: " + netId);
7391                        replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
7392                                WifiManager.ERROR);
7393                        messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
7394                        break;
7395                    }
7396                    break;
7397                default:
7398                    return NOT_HANDLED;
7399            }
7400            return HANDLED;
7401        }
7402
7403        @Override
7404        public void exit() {
7405            loge("WifiStateMachine: Leaving Connected state");
7406            setScanAlarm(false, 0);
7407            mLastDriverRoamAttempt = 0;
7408        }
7409    }
7410
7411    class DisconnectingState extends State {
7412
7413        @Override
7414        public void enter() {
7415            mCurrentScanAlarmMs = mDisconnectedScanPeriodMs;
7416
7417            if (PDBG) {
7418                loge(" Enter DisconnectingState State scan interval " + mFrameworkScanIntervalMs
7419                        + " mEnableBackgroundScan= " + mEnableBackgroundScan
7420                        + " screenOn=" + mScreenOn);
7421            }
7422
7423            // Make sure we disconnect: we enter this state prior connecting to a new
7424            // network, waiting for either a DISCONECT event or a SUPPLICANT_STATE_CHANGE
7425            // event which in this case will be indicating that supplicant started to associate.
7426            // In some cases supplicant doesn't ignore the connect requests (it might not
7427            // find the target SSID in its cache),
7428            // Therefore we end up stuck that state, hence the need for the watchdog.
7429            disconnectingWatchdogCount++;
7430            loge("Start Disconnecting Watchdog " + disconnectingWatchdogCount);
7431            sendMessageDelayed(obtainMessage(CMD_DISCONNECTING_WATCHDOG_TIMER,
7432                    disconnectingWatchdogCount, 0), DISCONNECTING_GUARD_TIMER_MSEC);
7433        }
7434
7435        @Override
7436        public boolean processMessage(Message message) {
7437            logStateAndMessage(message, getClass().getSimpleName());
7438            switch (message.what) {
7439                case CMD_SET_OPERATIONAL_MODE:
7440                    if (message.arg1 != CONNECT_MODE) {
7441                        deferMessage(message);
7442                    }
7443                    break;
7444                case CMD_START_SCAN:
7445                    // Ignore scans while disconnecting
7446                    return HANDLED;
7447                case CMD_DISCONNECTING_WATCHDOG_TIMER:
7448                    if (disconnectingWatchdogCount == message.arg1) {
7449                        if (DBG) log("disconnecting watchdog! -> disconnect");
7450                        handleNetworkDisconnect();
7451                        transitionTo(mDisconnectedState);
7452                    }
7453                    break;
7454                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
7455                    /**
7456                     * If we get a SUPPLICANT_STATE_CHANGE_EVENT before NETWORK_DISCONNECTION_EVENT
7457                     * we have missed the network disconnection, transition to mDisconnectedState
7458                     * and handle the rest of the events there
7459                     */
7460                    deferMessage(message);
7461                    handleNetworkDisconnect();
7462                    transitionTo(mDisconnectedState);
7463                    break;
7464                default:
7465                    return NOT_HANDLED;
7466            }
7467            return HANDLED;
7468        }
7469
7470        @Override
7471        public void exit() {
7472            mCurrentScanAlarmMs = 0;
7473        }
7474    }
7475
7476    class DisconnectedState extends State {
7477        @Override
7478        public void enter() {
7479            // We dont scan frequently if this is a temporary disconnect
7480            // due to p2p
7481            if (mTemporarilyDisconnectWifi) {
7482                mWifiP2pChannel.sendMessage(WifiP2pServiceImpl.DISCONNECT_WIFI_RESPONSE);
7483                return;
7484            }
7485
7486            // Loose the last selection choice
7487            // mWifiAutoJoinController.setLastSelectedConfiguration
7488            // (WifiConfiguration.INVALID_NETWORK_ID);
7489
7490            mFrameworkScanIntervalMs = Settings.Global.getLong(mContext.getContentResolver(),
7491                    Settings.Global.WIFI_FRAMEWORK_SCAN_INTERVAL_MS,
7492                    mDefaultFrameworkScanIntervalMs);
7493
7494            if (mScreenOn)
7495                mCurrentScanAlarmMs = mDisconnectedScanPeriodMs;
7496
7497            if (PDBG) {
7498                loge(" Enter disconnected State scan interval " + mFrameworkScanIntervalMs
7499                        + " mEnableBackgroundScan= " + mEnableBackgroundScan
7500                        + " screenOn=" + mScreenOn);
7501            }
7502
7503            /** clear the roaming state, if we were roaming, we failed */
7504            mAutoRoaming = WifiAutoJoinController.AUTO_JOIN_IDLE;
7505
7506            // Reenable all networks, allow for hidden networks to be scanned
7507            mWifiConfigStore.enableAllNetworks();
7508
7509            /**
7510             * - screen dark and PNO supported => scan alarm disabled
7511             * - everything else => scan alarm enabled with mDefaultFrameworkScanIntervalMs period
7512             */
7513            if ((mScreenOn == false) && mEnableBackgroundScan) { //mEnableBackgroundScan) {
7514                /* If a regular scan result is pending, do not initiate background
7515                 * scan until the scan results are returned. This is needed because
7516                 * initiating a background scan will cancel the regular scan and
7517                 * scan results will not be returned until background scanning is
7518                 * cleared
7519                 */
7520                if (!mIsScanOngoing) {
7521                    mWifiNative.enableBackgroundScan(true);
7522                }
7523            } else {
7524                setScanAlarm(true, 200);
7525            }
7526
7527            /**
7528             * If we have no networks saved, the supplicant stops doing the periodic scan.
7529             * The scans are useful to notify the user of the presence of an open network.
7530             * Note that these are not wake up scans.
7531             */
7532            if (!mP2pConnected.get() && mWifiConfigStore.getConfiguredNetworks().size() == 0) {
7533                sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,
7534                        ++mPeriodicScanToken, 0), mSupplicantScanIntervalMs);
7535            }
7536
7537            mDisconnectedTimeStamp = System.currentTimeMillis();
7538
7539        }
7540        @Override
7541        public boolean processMessage(Message message) {
7542            boolean ret = HANDLED;
7543
7544            logStateAndMessage(message, getClass().getSimpleName());
7545
7546            switch (message.what) {
7547                case CMD_NO_NETWORKS_PERIODIC_SCAN:
7548                    if (mP2pConnected.get()) break;
7549                    if (message.arg1 == mPeriodicScanToken &&
7550                            mWifiConfigStore.getConfiguredNetworks().size() == 0) {
7551                        startScan(UNKNOWN_SCAN_SOURCE, -1, null, null);
7552                        sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,
7553                                    ++mPeriodicScanToken, 0), mSupplicantScanIntervalMs);
7554                    }
7555                    break;
7556                case WifiManager.FORGET_NETWORK:
7557                case CMD_REMOVE_NETWORK:
7558                    // Set up a delayed message here. After the forget/remove is handled
7559                    // the handled delayed message will determine if there is a need to
7560                    // scan and continue
7561                    sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,
7562                                ++mPeriodicScanToken, 0), mSupplicantScanIntervalMs);
7563                    ret = NOT_HANDLED;
7564                    break;
7565                case CMD_SET_OPERATIONAL_MODE:
7566                    if (message.arg1 != CONNECT_MODE) {
7567                        mOperationalMode = message.arg1;
7568
7569                        mWifiConfigStore.disableAllNetworks();
7570                        if (mOperationalMode == SCAN_ONLY_WITH_WIFI_OFF_MODE) {
7571                            mWifiP2pChannel.sendMessage(CMD_DISABLE_P2P_REQ);
7572                            setWifiState(WIFI_STATE_DISABLED);
7573                        }
7574
7575                        transitionTo(mScanModeState);
7576                    }
7577                    break;
7578                    /* Ignore network disconnect */
7579                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
7580                    break;
7581                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
7582                    StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
7583                    if (DBG) {
7584                        loge("SUPPLICANT_STATE_CHANGE_EVENT state=" + stateChangeResult.state +
7585                                " -> state= " + WifiInfo.getDetailedStateOf(stateChangeResult.state)
7586                                + " debouncing=" + linkDebouncing);
7587                    }
7588                    setNetworkDetailedState(WifiInfo.getDetailedStateOf(stateChangeResult.state));
7589                    /* ConnectModeState does the rest of the handling */
7590                    ret = NOT_HANDLED;
7591                    break;
7592                case CMD_START_SCAN:
7593                    if (!isScanAllowed()) {
7594                        // Ignore the scan request
7595                        return HANDLED;
7596                    }
7597                    /* Disable background scan temporarily during a regular scan */
7598                    if (mEnableBackgroundScan) {
7599                        mWifiNative.enableBackgroundScan(false);
7600                    }
7601                    /* Handled in parent state */
7602                    ret = NOT_HANDLED;
7603                    break;
7604                case WifiMonitor.SCAN_RESULTS_EVENT:
7605                    /* Re-enable background scan when a pending scan result is received */
7606                    if (mEnableBackgroundScan && mIsScanOngoing) {
7607                        mWifiNative.enableBackgroundScan(true);
7608                    }
7609                    /* Handled in parent state */
7610                    ret = NOT_HANDLED;
7611                    break;
7612                case WifiP2pServiceImpl.P2P_CONNECTION_CHANGED:
7613                    NetworkInfo info = (NetworkInfo) message.obj;
7614                    mP2pConnected.set(info.isConnected());
7615                    if (mP2pConnected.get()) {
7616                        int defaultInterval = mContext.getResources().getInteger(
7617                                R.integer.config_wifi_scan_interval_p2p_connected);
7618                        long scanIntervalMs = Settings.Global.getLong(mContext.getContentResolver(),
7619                                Settings.Global.WIFI_SCAN_INTERVAL_WHEN_P2P_CONNECTED_MS,
7620                                defaultInterval);
7621                        mWifiNative.setScanInterval((int) scanIntervalMs/1000);
7622                    } else if (mWifiConfigStore.getConfiguredNetworks().size() == 0) {
7623                        if (DBG) log("Turn on scanning after p2p disconnected");
7624                        sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,
7625                                    ++mPeriodicScanToken, 0), mSupplicantScanIntervalMs);
7626                    }
7627                case CMD_RECONNECT:
7628                case CMD_REASSOCIATE:
7629                    if (mTemporarilyDisconnectWifi) {
7630                        // Drop a third party reconnect/reassociate if STA is
7631                        // temporarily disconnected for p2p
7632                        break;
7633                    } else {
7634                        // ConnectModeState handles it
7635                        ret = NOT_HANDLED;
7636                    }
7637                    break;
7638                case CMD_SCREEN_STATE_CHANGED:
7639                    handleScreenStateChanged(message.arg1 != 0,
7640                            /* startBackgroundScanIfNeeded = */ true);
7641                    break;
7642                default:
7643                    ret = NOT_HANDLED;
7644            }
7645            return ret;
7646        }
7647
7648        @Override
7649        public void exit() {
7650            /* No need for a background scan upon exit from a disconnected state */
7651            if (mEnableBackgroundScan) {
7652                mWifiNative.enableBackgroundScan(false);
7653            }
7654            mCurrentScanAlarmMs = 0;
7655            setScanAlarm(false, 0);
7656        }
7657    }
7658
7659    class WpsRunningState extends State {
7660        // Tracks the source to provide a reply
7661        private Message mSourceMessage;
7662        @Override
7663        public void enter() {
7664            mSourceMessage = Message.obtain(getCurrentMessage());
7665        }
7666        @Override
7667        public boolean processMessage(Message message) {
7668            logStateAndMessage(message, getClass().getSimpleName());
7669
7670            switch (message.what) {
7671                case WifiMonitor.WPS_SUCCESS_EVENT:
7672                    // Ignore intermediate success, wait for full connection
7673                    break;
7674                case WifiMonitor.NETWORK_CONNECTION_EVENT:
7675                    replyToMessage(mSourceMessage, WifiManager.WPS_COMPLETED);
7676                    mSourceMessage.recycle();
7677                    mSourceMessage = null;
7678                    deferMessage(message);
7679                    transitionTo(mDisconnectedState);
7680                    break;
7681                case WifiMonitor.WPS_OVERLAP_EVENT:
7682                    replyToMessage(mSourceMessage, WifiManager.WPS_FAILED,
7683                            WifiManager.WPS_OVERLAP_ERROR);
7684                    mSourceMessage.recycle();
7685                    mSourceMessage = null;
7686                    transitionTo(mDisconnectedState);
7687                    break;
7688                case WifiMonitor.WPS_FAIL_EVENT:
7689                    // Arg1 has the reason for the failure
7690                    if ((message.arg1 != WifiManager.ERROR) || (message.arg2 != 0)) {
7691                        replyToMessage(mSourceMessage, WifiManager.WPS_FAILED, message.arg1);
7692                        mSourceMessage.recycle();
7693                        mSourceMessage = null;
7694                        transitionTo(mDisconnectedState);
7695                    } else {
7696                        if (DBG) log("Ignore unspecified fail event during WPS connection");
7697                    }
7698                    break;
7699                case WifiMonitor.WPS_TIMEOUT_EVENT:
7700                    replyToMessage(mSourceMessage, WifiManager.WPS_FAILED,
7701                            WifiManager.WPS_TIMED_OUT);
7702                    mSourceMessage.recycle();
7703                    mSourceMessage = null;
7704                    transitionTo(mDisconnectedState);
7705                    break;
7706                case WifiManager.START_WPS:
7707                    replyToMessage(message, WifiManager.WPS_FAILED, WifiManager.IN_PROGRESS);
7708                    break;
7709                case WifiManager.CANCEL_WPS:
7710                    if (mWifiNative.cancelWps()) {
7711                        replyToMessage(message, WifiManager.CANCEL_WPS_SUCCEDED);
7712                    } else {
7713                        replyToMessage(message, WifiManager.CANCEL_WPS_FAILED, WifiManager.ERROR);
7714                    }
7715                    transitionTo(mDisconnectedState);
7716                    break;
7717                /**
7718                 * Defer all commands that can cause connections to a different network
7719                 * or put the state machine out of connect mode
7720                 */
7721                case CMD_STOP_DRIVER:
7722                case CMD_SET_OPERATIONAL_MODE:
7723                case WifiManager.CONNECT_NETWORK:
7724                case CMD_ENABLE_NETWORK:
7725                case CMD_RECONNECT:
7726                case CMD_REASSOCIATE:
7727                    deferMessage(message);
7728                    break;
7729                case CMD_AUTO_CONNECT:
7730                case CMD_AUTO_ROAM:
7731                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
7732                    return HANDLED;
7733                case CMD_START_SCAN:
7734                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
7735                    return HANDLED;
7736                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
7737                    if (DBG) log("Network connection lost");
7738                    handleNetworkDisconnect();
7739                    break;
7740                case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
7741                    if (DBG) log("Ignore Assoc reject event during WPS Connection");
7742                    break;
7743                case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
7744                    // Disregard auth failure events during WPS connection. The
7745                    // EAP sequence is retried several times, and there might be
7746                    // failures (especially for wps pin). We will get a WPS_XXX
7747                    // event at the end of the sequence anyway.
7748                    if (DBG) log("Ignore auth failure during WPS connection");
7749                    break;
7750                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
7751                    // Throw away supplicant state changes when WPS is running.
7752                    // We will start getting supplicant state changes once we get
7753                    // a WPS success or failure
7754                    break;
7755                default:
7756                    return NOT_HANDLED;
7757            }
7758            return HANDLED;
7759        }
7760
7761        @Override
7762        public void exit() {
7763            mWifiConfigStore.enableAllNetworks();
7764            mWifiConfigStore.loadConfiguredNetworks();
7765        }
7766    }
7767
7768    class SoftApStartingState extends State {
7769        @Override
7770        public void enter() {
7771            final Message message = getCurrentMessage();
7772            if (message.what == CMD_START_AP) {
7773                final WifiConfiguration config = (WifiConfiguration) message.obj;
7774
7775                if (config == null) {
7776                    mWifiApConfigChannel.sendMessage(CMD_REQUEST_AP_CONFIG);
7777                } else {
7778                    mWifiApConfigChannel.sendMessage(CMD_SET_AP_CONFIG, config);
7779                    startSoftApWithConfig(config);
7780                }
7781            } else {
7782                throw new RuntimeException("Illegal transition to SoftApStartingState: " + message);
7783            }
7784        }
7785        @Override
7786        public boolean processMessage(Message message) {
7787            logStateAndMessage(message, getClass().getSimpleName());
7788
7789            switch(message.what) {
7790                case CMD_START_SUPPLICANT:
7791                case CMD_STOP_SUPPLICANT:
7792                case CMD_START_AP:
7793                case CMD_STOP_AP:
7794                case CMD_START_DRIVER:
7795                case CMD_STOP_DRIVER:
7796                case CMD_SET_OPERATIONAL_MODE:
7797                case CMD_SET_COUNTRY_CODE:
7798                case CMD_SET_FREQUENCY_BAND:
7799                case CMD_START_PACKET_FILTERING:
7800                case CMD_STOP_PACKET_FILTERING:
7801                case CMD_TETHER_STATE_CHANGE:
7802                    deferMessage(message);
7803                    break;
7804                case WifiStateMachine.CMD_RESPONSE_AP_CONFIG:
7805                    WifiConfiguration config = (WifiConfiguration) message.obj;
7806                    if (config != null) {
7807                        startSoftApWithConfig(config);
7808                    } else {
7809                        loge("Softap config is null!");
7810                        sendMessage(CMD_START_AP_FAILURE);
7811                    }
7812                    break;
7813                case CMD_START_AP_SUCCESS:
7814                    setWifiApState(WIFI_AP_STATE_ENABLED);
7815                    transitionTo(mSoftApStartedState);
7816                    break;
7817                case CMD_START_AP_FAILURE:
7818                    setWifiApState(WIFI_AP_STATE_FAILED);
7819                    transitionTo(mInitialState);
7820                    break;
7821                default:
7822                    return NOT_HANDLED;
7823            }
7824            return HANDLED;
7825        }
7826    }
7827
7828    class SoftApStartedState extends State {
7829        @Override
7830        public boolean processMessage(Message message) {
7831            logStateAndMessage(message, getClass().getSimpleName());
7832
7833            switch(message.what) {
7834                case CMD_STOP_AP:
7835                    if (DBG) log("Stopping Soft AP");
7836                    /* We have not tethered at this point, so we just shutdown soft Ap */
7837                    try {
7838                        mNwService.stopAccessPoint(mInterfaceName);
7839                    } catch(Exception e) {
7840                        loge("Exception in stopAccessPoint()");
7841                    }
7842                    setWifiApState(WIFI_AP_STATE_DISABLED);
7843                    transitionTo(mInitialState);
7844                    break;
7845                case CMD_START_AP:
7846                    // Ignore a start on a running access point
7847                    break;
7848                    // Fail client mode operation when soft AP is enabled
7849                case CMD_START_SUPPLICANT:
7850                    loge("Cannot start supplicant with a running soft AP");
7851                    setWifiState(WIFI_STATE_UNKNOWN);
7852                    break;
7853                case CMD_TETHER_STATE_CHANGE:
7854                    TetherStateChange stateChange = (TetherStateChange) message.obj;
7855                    if (startTethering(stateChange.available)) {
7856                        transitionTo(mTetheringState);
7857                    }
7858                    break;
7859                default:
7860                    return NOT_HANDLED;
7861            }
7862            return HANDLED;
7863        }
7864    }
7865
7866    class TetheringState extends State {
7867        @Override
7868        public void enter() {
7869            /* Send ourselves a delayed message to shut down if tethering fails to notify */
7870            sendMessageDelayed(obtainMessage(CMD_TETHER_NOTIFICATION_TIMED_OUT,
7871                    ++mTetherToken, 0), TETHER_NOTIFICATION_TIME_OUT_MSECS);
7872        }
7873        @Override
7874        public boolean processMessage(Message message) {
7875            logStateAndMessage(message, getClass().getSimpleName());
7876
7877            switch(message.what) {
7878                case CMD_TETHER_STATE_CHANGE:
7879                    TetherStateChange stateChange = (TetherStateChange) message.obj;
7880                    if (isWifiTethered(stateChange.active)) {
7881                        transitionTo(mTetheredState);
7882                    }
7883                    return HANDLED;
7884                case CMD_TETHER_NOTIFICATION_TIMED_OUT:
7885                    if (message.arg1 == mTetherToken) {
7886                        loge("Failed to get tether update, shutdown soft access point");
7887                        transitionTo(mSoftApStartedState);
7888                        // Needs to be first thing handled
7889                        sendMessageAtFrontOfQueue(CMD_STOP_AP);
7890                    }
7891                    break;
7892                case CMD_START_SUPPLICANT:
7893                case CMD_STOP_SUPPLICANT:
7894                case CMD_START_AP:
7895                case CMD_STOP_AP:
7896                case CMD_START_DRIVER:
7897                case CMD_STOP_DRIVER:
7898                case CMD_SET_OPERATIONAL_MODE:
7899                case CMD_SET_COUNTRY_CODE:
7900                case CMD_SET_FREQUENCY_BAND:
7901                case CMD_START_PACKET_FILTERING:
7902                case CMD_STOP_PACKET_FILTERING:
7903                    deferMessage(message);
7904                    break;
7905                default:
7906                    return NOT_HANDLED;
7907            }
7908            return HANDLED;
7909        }
7910    }
7911
7912    class TetheredState extends State {
7913        @Override
7914        public boolean processMessage(Message message) {
7915            logStateAndMessage(message, getClass().getSimpleName());
7916
7917            switch(message.what) {
7918                case CMD_TETHER_STATE_CHANGE:
7919                    TetherStateChange stateChange = (TetherStateChange) message.obj;
7920                    if (!isWifiTethered(stateChange.active)) {
7921                        loge("Tethering reports wifi as untethered!, shut down soft Ap");
7922                        setHostApRunning(null, false);
7923                        setHostApRunning(null, true);
7924                    }
7925                    return HANDLED;
7926                case CMD_STOP_AP:
7927                    if (DBG) log("Untethering before stopping AP");
7928                    setWifiApState(WIFI_AP_STATE_DISABLING);
7929                    stopTethering();
7930                    transitionTo(mUntetheringState);
7931                    // More work to do after untethering
7932                    deferMessage(message);
7933                    break;
7934                default:
7935                    return NOT_HANDLED;
7936            }
7937            return HANDLED;
7938        }
7939    }
7940
7941    class UntetheringState extends State {
7942        @Override
7943        public void enter() {
7944            /* Send ourselves a delayed message to shut down if tethering fails to notify */
7945            sendMessageDelayed(obtainMessage(CMD_TETHER_NOTIFICATION_TIMED_OUT,
7946                    ++mTetherToken, 0), TETHER_NOTIFICATION_TIME_OUT_MSECS);
7947
7948        }
7949        @Override
7950        public boolean processMessage(Message message) {
7951            logStateAndMessage(message, getClass().getSimpleName());
7952
7953            switch(message.what) {
7954                case CMD_TETHER_STATE_CHANGE:
7955                    TetherStateChange stateChange = (TetherStateChange) message.obj;
7956
7957                    /* Wait till wifi is untethered */
7958                    if (isWifiTethered(stateChange.active)) break;
7959
7960                    transitionTo(mSoftApStartedState);
7961                    break;
7962                case CMD_TETHER_NOTIFICATION_TIMED_OUT:
7963                    if (message.arg1 == mTetherToken) {
7964                        loge("Failed to get tether update, force stop access point");
7965                        transitionTo(mSoftApStartedState);
7966                    }
7967                    break;
7968                case CMD_START_SUPPLICANT:
7969                case CMD_STOP_SUPPLICANT:
7970                case CMD_START_AP:
7971                case CMD_STOP_AP:
7972                case CMD_START_DRIVER:
7973                case CMD_STOP_DRIVER:
7974                case CMD_SET_OPERATIONAL_MODE:
7975                case CMD_SET_COUNTRY_CODE:
7976                case CMD_SET_FREQUENCY_BAND:
7977                case CMD_START_PACKET_FILTERING:
7978                case CMD_STOP_PACKET_FILTERING:
7979                    deferMessage(message);
7980                    break;
7981                default:
7982                    return NOT_HANDLED;
7983            }
7984            return HANDLED;
7985        }
7986    }
7987
7988    //State machine initiated requests can have replyTo set to null indicating
7989    //there are no recepients, we ignore those reply actions
7990    private void replyToMessage(Message msg, int what) {
7991        if (msg.replyTo == null) return;
7992        Message dstMsg = obtainMessageWithArg2(msg);
7993        dstMsg.what = what;
7994        mReplyChannel.replyToMessage(msg, dstMsg);
7995    }
7996
7997    private void replyToMessage(Message msg, int what, int arg1) {
7998        if (msg.replyTo == null) return;
7999        Message dstMsg = obtainMessageWithArg2(msg);
8000        dstMsg.what = what;
8001        dstMsg.arg1 = arg1;
8002        mReplyChannel.replyToMessage(msg, dstMsg);
8003    }
8004
8005    private void replyToMessage(Message msg, int what, Object obj) {
8006        if (msg.replyTo == null) return;
8007        Message dstMsg = obtainMessageWithArg2(msg);
8008        dstMsg.what = what;
8009        dstMsg.obj = obj;
8010        mReplyChannel.replyToMessage(msg, dstMsg);
8011    }
8012
8013    /**
8014     * arg2 on the source message has a unique id that needs to be retained in replies
8015     * to match the request
8016
8017     * see WifiManager for details
8018     */
8019    private Message obtainMessageWithArg2(Message srcMsg) {
8020        Message msg = Message.obtain();
8021        msg.arg2 = srcMsg.arg2;
8022        return msg;
8023    }
8024
8025    private static int parseHex(char ch) {
8026        if ('0' <= ch && ch <= '9') {
8027            return ch - '0';
8028        } else if ('a' <= ch && ch <= 'f') {
8029            return ch - 'a' + 10;
8030        } else if ('A' <= ch && ch <= 'F') {
8031            return ch - 'A' + 10;
8032        } else {
8033            throw new NumberFormatException("" + ch + " is not a valid hex digit");
8034        }
8035    }
8036
8037    private byte[] parseHex(String hex) {
8038        /* This only works for good input; don't throw bad data at it */
8039        if (hex == null) {
8040            return new byte[0];
8041        }
8042
8043        if (hex.length() % 2 != 0) {
8044            throw new NumberFormatException(hex + " is not a valid hex string");
8045        }
8046
8047        byte[] result = new byte[(hex.length())/2 + 1];
8048        result[0] = (byte) ((hex.length())/2);
8049        for (int i = 0, j = 1; i < hex.length(); i += 2, j++) {
8050            int val = parseHex(hex.charAt(i)) * 16 + parseHex(hex.charAt(i+1));
8051            byte b = (byte) (val & 0xFF);
8052            result[j] = b;
8053        }
8054
8055        return result;
8056    }
8057
8058    private static String makeHex(byte[] bytes) {
8059        StringBuilder sb = new StringBuilder();
8060        for (byte b : bytes) {
8061            sb.append(String.format("%02x", b));
8062        }
8063        return sb.toString();
8064    }
8065
8066    private static String makeHex(byte[] bytes, int from, int len) {
8067        StringBuilder sb = new StringBuilder();
8068        for (int i = 0; i < len; i++) {
8069            sb.append(String.format("%02x", bytes[from+i]));
8070        }
8071        return sb.toString();
8072    }
8073
8074
8075    private static byte[] concat(byte[] array1, byte[] array2, byte[] array3) {
8076
8077        int len = array1.length + array2.length + array3.length;
8078
8079        if (array1.length != 0) {
8080            len++;                      /* add another byte for size */
8081        }
8082
8083        if (array2.length != 0) {
8084            len++;                      /* add another byte for size */
8085        }
8086
8087        if (array3.length != 0) {
8088            len++;                      /* add another byte for size */
8089        }
8090
8091        byte[] result = new byte[len];
8092
8093        int index = 0;
8094        if (array1.length != 0) {
8095            result[index] = (byte) (array1.length & 0xFF);
8096            index++;
8097            for (byte b : array1) {
8098                result[index] = b;
8099                index++;
8100            }
8101        }
8102
8103        if (array2.length != 0) {
8104            result[index] = (byte) (array2.length & 0xFF);
8105            index++;
8106            for (byte b : array2) {
8107                result[index] = b;
8108                index++;
8109            }
8110        }
8111
8112        if (array3.length != 0) {
8113            result[index] = (byte) (array3.length & 0xFF);
8114            index++;
8115            for (byte b : array3) {
8116                result[index] = b;
8117                index++;
8118            }
8119        }
8120        return result;
8121    }
8122
8123    void handleGsmAuthRequest(SimAuthRequestData requestData) {
8124        if (targetWificonfiguration == null
8125                || targetWificonfiguration.networkId == requestData.networkId) {
8126            logd("id matches targetWifiConfiguration");
8127        } else {
8128            logd("id does not match targetWifiConfiguration");
8129            return;
8130        }
8131
8132        TelephonyManager tm = (TelephonyManager)
8133                mContext.getSystemService(Context.TELEPHONY_SERVICE);
8134
8135        if (tm != null) {
8136            StringBuilder sb = new StringBuilder();
8137            for (String challenge : requestData.challenges) {
8138
8139                logd("RAND = " + challenge);
8140
8141                byte[] rand = null;
8142                try {
8143                    rand = parseHex(challenge);
8144                } catch (NumberFormatException e) {
8145                    loge("malformed challenge");
8146                    continue;
8147                }
8148
8149                String base64Challenge = android.util.Base64.encodeToString(
8150                        rand, android.util.Base64.NO_WRAP);
8151                /*
8152                 * appType = 1 => SIM, 2 => USIM according to
8153                 * com.android.internal.telephony.PhoneConstants#APPTYPE_xxx
8154                 */
8155                int appType = 2;
8156                String tmResponse = tm.getIccSimChallengeResponse(appType, base64Challenge);
8157                logv("Raw Response - " + tmResponse);
8158
8159                if (tmResponse != null && tmResponse.length() > 4) {
8160                    byte[] result = android.util.Base64.decode(tmResponse,
8161                            android.util.Base64.DEFAULT);
8162                    logv("Hex Response -" + makeHex(result));
8163                    int sres_len = result[0];
8164                    String sres = makeHex(result, 1, sres_len);
8165                    int kc_offset = 1+sres_len;
8166                    int kc_len = result[kc_offset];
8167                    String kc = makeHex(result, 1+kc_offset, kc_len);
8168                    sb.append(":" + kc + ":" + sres);
8169                    logv("kc:" + kc + " sres:" + sres);
8170                } else {
8171                    loge("bad response - " + tmResponse);
8172                }
8173            }
8174
8175            String response = sb.toString();
8176            logv("Supplicant Response -" + response);
8177            mWifiNative.simAuthResponse(requestData.networkId, response);
8178        } else {
8179            loge("could not get telephony manager");
8180        }
8181    }
8182
8183    void handle3GAuthRequest(SimAuthRequestData requestData) {
8184
8185    }
8186}
8187