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