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