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