WifiStateMachine.java revision 3aed1e5a7a7f2ec730c393e2c7d129e3e551a711
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            updateCapabilities(c);
4348        }
4349        if (c != null) {
4350            ScanResult result = getCurrentScanResult();
4351            if (result == null) {
4352                loge("WifiStateMachine: handleSuccessfulIpConfiguration and no scan results" +
4353                        c.configKey());
4354            } else {
4355                // Clear the per BSSID failure count
4356                result.numIpConfigFailures = 0;
4357                // Clear the WHOLE BSSID blacklist, which means supplicant is free to retry
4358                // any BSSID, even though it may already have a non zero ip failure count,
4359                // this will typically happen if the user walks away and come back to his arrea
4360                // TODO: implement blacklisting based on a timer, i.e. keep BSSID blacklisted
4361                // in supplicant for a couple of hours or a day
4362                mWifiNative.clearBlacklist();
4363            }
4364        }
4365    }
4366
4367    private void handleIPv4Failure(int reason) {
4368        synchronized(mDhcpResultsLock) {
4369             if (mDhcpResults != null) {
4370                 mDhcpResults.clear();
4371             }
4372        }
4373        if (PDBG) {
4374            loge("wifistatemachine handleIPv4Failure");
4375        }
4376        updateLinkProperties(reason);
4377    }
4378
4379    private void handleIpConfigurationLost() {
4380        mWifiInfo.setInetAddress(null);
4381        mWifiInfo.setMeteredHint(false);
4382
4383        mWifiConfigStore.handleSSIDStateChange(mLastNetworkId, false,
4384                "DHCP FAILURE", mWifiInfo.getBSSID());
4385
4386        /* DHCP times out after about 30 seconds, we do a
4387         * disconnect thru supplicant, we will let autojoin retry connecting to the network
4388         */
4389        mWifiNative.disconnect();
4390    }
4391
4392    /* Current design is to not set the config on a running hostapd but instead
4393     * stop and start tethering when user changes config on a running access point
4394     *
4395     * TODO: Add control channel setup through hostapd that allows changing config
4396     * on a running daemon
4397     */
4398    private void startSoftApWithConfig(final WifiConfiguration config) {
4399        // Start hostapd on a separate thread
4400        new Thread(new Runnable() {
4401            public void run() {
4402                try {
4403                    mNwService.startAccessPoint(config, mInterfaceName);
4404                } catch (Exception e) {
4405                    loge("Exception in softap start " + e);
4406                    try {
4407                        mNwService.stopAccessPoint(mInterfaceName);
4408                        mNwService.startAccessPoint(config, mInterfaceName);
4409                    } catch (Exception e1) {
4410                        loge("Exception in softap re-start " + e1);
4411                        sendMessage(CMD_START_AP_FAILURE);
4412                        return;
4413                    }
4414                }
4415                if (DBG) log("Soft AP start successful");
4416                sendMessage(CMD_START_AP_SUCCESS);
4417            }
4418        }).start();
4419    }
4420
4421    /*
4422     * Read a MAC address in /proc/arp/table, used by WifistateMachine
4423     * so as to record MAC address of default gateway.
4424     **/
4425    private String macAddressFromRoute(String ipAddress) {
4426        String macAddress = null;
4427        BufferedReader reader = null;
4428        try {
4429            reader = new BufferedReader(new FileReader("/proc/net/arp"));
4430
4431            // Skip over the line bearing colum titles
4432            String line = reader.readLine();
4433
4434            while ((line = reader.readLine()) != null) {
4435                String[] tokens = line.split("[ ]+");
4436                if (tokens.length < 6) {
4437                    continue;
4438                }
4439
4440                // ARP column format is
4441                // Address HWType HWAddress Flags Mask IFace
4442                String ip = tokens[0];
4443                String mac = tokens[3];
4444
4445                if (ipAddress.equals(ip)) {
4446                    macAddress = mac;
4447                    break;
4448                }
4449            }
4450
4451            if (macAddress == null) {
4452                loge("Did not find remoteAddress {" + ipAddress + "} in " +
4453                        "/proc/net/arp");
4454            }
4455
4456        } catch (FileNotFoundException e) {
4457            loge("Could not open /proc/net/arp to lookup mac address");
4458        } catch (IOException e) {
4459            loge("Could not read /proc/net/arp to lookup mac address");
4460        } finally {
4461            try {
4462                if (reader != null) {
4463                    reader.close();
4464                }
4465            } catch (IOException e) {
4466                // Do nothing
4467            }
4468        }
4469        return macAddress;
4470
4471    }
4472
4473    private class WifiNetworkFactory extends NetworkFactory {
4474        /** Number of outstanding NetworkRequests for untrusted networks. */
4475        private int mUntrustedReqCount = 0;
4476
4477        public WifiNetworkFactory(Looper l, Context c, String TAG, NetworkCapabilities f) {
4478            super(l, c, TAG, f);
4479        }
4480
4481        @Override
4482        protected void needNetworkFor(NetworkRequest networkRequest, int score) {
4483            ++mConnectionRequests;
4484            if (!networkRequest.networkCapabilities.hasCapability(
4485                    NetworkCapabilities.NET_CAPABILITY_TRUSTED)) {
4486                if (++mUntrustedReqCount == 1) {
4487                    mWifiAutoJoinController.setAllowUntrustedConnections(true);
4488                }
4489            }
4490        }
4491
4492        @Override
4493        protected void releaseNetworkFor(NetworkRequest networkRequest) {
4494            --mConnectionRequests;
4495            if (!networkRequest.networkCapabilities.hasCapability(
4496                    NetworkCapabilities.NET_CAPABILITY_TRUSTED)) {
4497                if (--mUntrustedReqCount == 0) {
4498                    mWifiAutoJoinController.setAllowUntrustedConnections(false);
4499                }
4500            }
4501        }
4502
4503        public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
4504            pw.println("mConnectionRequests " + mConnectionRequests);
4505            pw.println("mUntrustedReqCount " + mUntrustedReqCount);
4506        }
4507
4508    }
4509    /********************************************************
4510     * HSM states
4511     *******************************************************/
4512
4513    class DefaultState extends State {
4514        @Override
4515        public boolean processMessage(Message message) {
4516            logStateAndMessage(message, getClass().getSimpleName());
4517
4518            switch (message.what) {
4519                case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: {
4520                    AsyncChannel ac = (AsyncChannel) message.obj;
4521                    if (ac == mWifiP2pChannel) {
4522                        if (message.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
4523                            mWifiP2pChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
4524                        } else {
4525                            loge("WifiP2pService connection failure, error=" + message.arg1);
4526                        }
4527                    } else {
4528                        loge("got HALF_CONNECTED for unknown channel");
4529                    }
4530                    break;
4531                }
4532                case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
4533                    AsyncChannel ac = (AsyncChannel) message.obj;
4534                    if (ac == mWifiP2pChannel) {
4535                        loge("WifiP2pService channel lost, message.arg1 =" + message.arg1);
4536                        //TODO: Re-establish connection to state machine after a delay
4537                        // mWifiP2pChannel.connect(mContext, getHandler(),
4538                        // mWifiP2pManager.getMessenger());
4539                    }
4540                    break;
4541                }
4542                case CMD_BLUETOOTH_ADAPTER_STATE_CHANGE:
4543                    mBluetoothConnectionActive = (message.arg1 !=
4544                            BluetoothAdapter.STATE_DISCONNECTED);
4545                    break;
4546                    /* Synchronous call returns */
4547                case CMD_PING_SUPPLICANT:
4548                case CMD_ENABLE_NETWORK:
4549                case CMD_ADD_OR_UPDATE_NETWORK:
4550                case CMD_REMOVE_NETWORK:
4551                case CMD_SAVE_CONFIG:
4552                    replyToMessage(message, message.what, FAILURE);
4553                    break;
4554                case CMD_GET_CAPABILITY_FREQ:
4555                    replyToMessage(message, message.what, null);
4556                    break;
4557                case CMD_GET_CONFIGURED_NETWORKS:
4558                    replyToMessage(message, message.what, (List<WifiConfiguration>) null);
4559                    break;
4560                case CMD_GET_PRIVILEGED_CONFIGURED_NETWORKS:
4561                    replyToMessage(message, message.what, (List<WifiConfiguration>) null);
4562                    break;
4563                case CMD_ENABLE_RSSI_POLL:
4564                    mEnableRssiPolling = (message.arg1 == 1);
4565                    break;
4566                case CMD_SET_HIGH_PERF_MODE:
4567                    if (message.arg1 == 1) {
4568                        setSuspendOptimizations(SUSPEND_DUE_TO_HIGH_PERF, false);
4569                    } else {
4570                        setSuspendOptimizations(SUSPEND_DUE_TO_HIGH_PERF, true);
4571                    }
4572                    break;
4573                case CMD_BOOT_COMPLETED:
4574                    String countryCode = mPersistedCountryCode;
4575                    if (TextUtils.isEmpty(countryCode) == false) {
4576                        Settings.Global.putString(mContext.getContentResolver(),
4577                                Settings.Global.WIFI_COUNTRY_CODE,
4578                                countryCode);
4579                        // It may be that the state transition that should send this info
4580                        // to the driver happened between mPersistedCountryCode getting set
4581                        // and now, so simply persisting it here would mean we have sent
4582                        // nothing to the driver.  Send the cmd so it might be set now.
4583                        int sequenceNum = mCountryCodeSequence.incrementAndGet();
4584                        sendMessageAtFrontOfQueue(CMD_SET_COUNTRY_CODE,
4585                                sequenceNum, 0, countryCode);
4586                    }
4587
4588                    checkAndSetConnectivityInstance();
4589                    mNetworkFactory = new WifiNetworkFactory(getHandler().getLooper(), mContext,
4590                            NETWORKTYPE, mNetworkCapabilitiesFilter);
4591                    mNetworkFactory.setScoreFilter(60);
4592                    mCm.registerNetworkFactory(new Messenger(mNetworkFactory), NETWORKTYPE);
4593                    break;
4594                case CMD_SET_BATCHED_SCAN:
4595                    recordBatchedScanSettings(message.arg1, message.arg2, (Bundle)message.obj);
4596                    break;
4597                case CMD_POLL_BATCHED_SCAN:
4598                    handleBatchedScanPollRequest();
4599                    break;
4600                case CMD_START_NEXT_BATCHED_SCAN:
4601                    startNextBatchedScan();
4602                    break;
4603                case CMD_SCREEN_STATE_CHANGED:
4604                    handleScreenStateChanged(message.arg1 != 0,
4605                            /* startBackgroundScanIfNeeded = */ false);
4606                    break;
4607                    /* Discard */
4608                case CMD_START_SCAN:
4609                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
4610                    break;
4611                case CMD_START_SUPPLICANT:
4612                case CMD_STOP_SUPPLICANT:
4613                case CMD_STOP_SUPPLICANT_FAILED:
4614                case CMD_START_DRIVER:
4615                case CMD_STOP_DRIVER:
4616                case CMD_DELAYED_STOP_DRIVER:
4617                case CMD_DRIVER_START_TIMED_OUT:
4618                case CMD_START_AP:
4619                case CMD_START_AP_SUCCESS:
4620                case CMD_START_AP_FAILURE:
4621                case CMD_STOP_AP:
4622                case CMD_TETHER_STATE_CHANGE:
4623                case CMD_TETHER_NOTIFICATION_TIMED_OUT:
4624                case CMD_DISCONNECT:
4625                case CMD_RECONNECT:
4626                case CMD_REASSOCIATE:
4627                case CMD_RELOAD_TLS_AND_RECONNECT:
4628                case WifiMonitor.SUP_CONNECTION_EVENT:
4629                case WifiMonitor.SUP_DISCONNECTION_EVENT:
4630                case WifiMonitor.NETWORK_CONNECTION_EVENT:
4631                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
4632                case WifiMonitor.SCAN_RESULTS_EVENT:
4633                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
4634                case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
4635                case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
4636                case WifiMonitor.WPS_OVERLAP_EVENT:
4637                case CMD_BLACKLIST_NETWORK:
4638                case CMD_CLEAR_BLACKLIST:
4639                case CMD_SET_OPERATIONAL_MODE:
4640                case CMD_SET_COUNTRY_CODE:
4641                case CMD_SET_FREQUENCY_BAND:
4642                case CMD_RSSI_POLL:
4643                case CMD_ENABLE_ALL_NETWORKS:
4644                case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
4645                case DhcpStateMachine.CMD_POST_DHCP_ACTION:
4646                /* Handled by WifiApConfigStore */
4647                case CMD_SET_AP_CONFIG:
4648                case CMD_SET_AP_CONFIG_COMPLETED:
4649                case CMD_REQUEST_AP_CONFIG:
4650                case CMD_RESPONSE_AP_CONFIG:
4651                case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
4652                case WifiWatchdogStateMachine.GOOD_LINK_DETECTED:
4653                case CMD_NO_NETWORKS_PERIODIC_SCAN:
4654                case CMD_DISABLE_P2P_RSP:
4655                case WifiMonitor.SUP_REQUEST_IDENTITY:
4656                case CMD_TEST_NETWORK_DISCONNECT:
4657                case CMD_OBTAINING_IP_ADDRESS_WATCHDOG_TIMER:
4658                case WifiMonitor.SUP_REQUEST_SIM_AUTH:
4659                case CMD_TARGET_BSSID:
4660                case CMD_AUTO_CONNECT:
4661                case CMD_AUTO_ROAM:
4662                case CMD_AUTO_SAVE_NETWORK:
4663                case CMD_ASSOCIATED_BSSID:
4664                case CMD_UNWANTED_NETWORK:
4665                case CMD_DISCONNECTING_WATCHDOG_TIMER:
4666                case CMD_ROAM_WATCHDOG_TIMER:
4667                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
4668                    break;
4669                case DhcpStateMachine.CMD_ON_QUIT:
4670                    mDhcpStateMachine = null;
4671                    break;
4672                case CMD_SET_SUSPEND_OPT_ENABLED:
4673                    if (message.arg1 == 1) {
4674                        mSuspendWakeLock.release();
4675                        setSuspendOptimizations(SUSPEND_DUE_TO_SCREEN, true);
4676                    } else {
4677                        setSuspendOptimizations(SUSPEND_DUE_TO_SCREEN, false);
4678                    }
4679                    break;
4680                case WifiMonitor.DRIVER_HUNG_EVENT:
4681                    setSupplicantRunning(false);
4682                    setSupplicantRunning(true);
4683                    break;
4684                case WifiManager.CONNECT_NETWORK:
4685                    replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
4686                            WifiManager.BUSY);
4687                    break;
4688                case WifiManager.FORGET_NETWORK:
4689                    replyToMessage(message, WifiManager.FORGET_NETWORK_FAILED,
4690                            WifiManager.BUSY);
4691                    break;
4692                case WifiManager.SAVE_NETWORK:
4693                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
4694                    replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED,
4695                            WifiManager.BUSY);
4696                    break;
4697                case WifiManager.START_WPS:
4698                    replyToMessage(message, WifiManager.WPS_FAILED,
4699                            WifiManager.BUSY);
4700                    break;
4701                case WifiManager.CANCEL_WPS:
4702                    replyToMessage(message, WifiManager.CANCEL_WPS_FAILED,
4703                            WifiManager.BUSY);
4704                    break;
4705                case WifiManager.DISABLE_NETWORK:
4706                    replyToMessage(message, WifiManager.DISABLE_NETWORK_FAILED,
4707                            WifiManager.BUSY);
4708                    break;
4709                case WifiManager.RSSI_PKTCNT_FETCH:
4710                    replyToMessage(message, WifiManager.RSSI_PKTCNT_FETCH_FAILED,
4711                            WifiManager.BUSY);
4712                    break;
4713                case CMD_GET_SUPPORTED_FEATURES:
4714                    if (WifiNative.startHal()) {
4715                        int featureSet = WifiNative.getSupportedFeatureSet();
4716                        replyToMessage(message, message.what, featureSet);
4717                    } else {
4718                        replyToMessage(message, message.what, 0);
4719                    }
4720                    break;
4721                case CMD_GET_LINK_LAYER_STATS:
4722                    // Not supported hence reply with error message
4723                    replyToMessage(message, message.what, null);
4724                    break;
4725                case WifiP2pServiceImpl.P2P_CONNECTION_CHANGED:
4726                    NetworkInfo info = (NetworkInfo) message.obj;
4727                    mP2pConnected.set(info.isConnected());
4728                    break;
4729                case WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST:
4730                    mTemporarilyDisconnectWifi = (message.arg1 == 1);
4731                    replyToMessage(message, WifiP2pServiceImpl.DISCONNECT_WIFI_RESPONSE);
4732                    break;
4733                /* Link configuration (IP address, DNS, ...) changes notified via netlink */
4734                case CMD_UPDATE_LINKPROPERTIES:
4735                    updateLinkProperties(CMD_UPDATE_LINKPROPERTIES);
4736                    break;
4737                case CMD_IP_CONFIGURATION_SUCCESSFUL:
4738                case CMD_IP_CONFIGURATION_LOST:
4739                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
4740                    break;
4741                case CMD_GET_CONNECTION_STATISTICS:
4742                    replyToMessage(message, message.what, mWifiConnectionStatistics);
4743                    break;
4744                default:
4745                    loge("Error! unhandled message" + message);
4746                    break;
4747            }
4748            return HANDLED;
4749        }
4750    }
4751
4752    class InitialState extends State {
4753        @Override
4754        public void enter() {
4755            mWifiNative.unloadDriver();
4756
4757            if (mWifiP2pChannel == null) {
4758                mWifiP2pChannel = new AsyncChannel();
4759                mWifiP2pChannel.connect(mContext, getHandler(),
4760                    mWifiP2pServiceImpl.getP2pStateMachineMessenger());
4761            }
4762
4763            if (mWifiApConfigChannel == null) {
4764                mWifiApConfigChannel = new AsyncChannel();
4765                WifiApConfigStore wifiApConfigStore = WifiApConfigStore.makeWifiApConfigStore(
4766                        mContext, getHandler());
4767                wifiApConfigStore.loadApConfiguration();
4768                mWifiApConfigChannel.connectSync(mContext, getHandler(),
4769                        wifiApConfigStore.getMessenger());
4770            }
4771        }
4772        @Override
4773        public boolean processMessage(Message message) {
4774            logStateAndMessage(message, getClass().getSimpleName());
4775            switch (message.what) {
4776                case CMD_START_SUPPLICANT:
4777                    if (mWifiNative.loadDriver()) {
4778                        try {
4779                            mNwService.wifiFirmwareReload(mInterfaceName, "STA");
4780                        } catch (Exception e) {
4781                            loge("Failed to reload STA firmware " + e);
4782                            // Continue
4783                        }
4784
4785                        try {
4786                            // A runtime crash can leave the interface up and
4787                            // IP addresses configured, and this affects
4788                            // connectivity when supplicant starts up.
4789                            // Ensure interface is down and we have no IP
4790                            // addresses before a supplicant start.
4791                            mNwService.setInterfaceDown(mInterfaceName);
4792                            mNwService.clearInterfaceAddresses(mInterfaceName);
4793
4794                            // Set privacy extensions
4795                            mNwService.setInterfaceIpv6PrivacyExtensions(mInterfaceName, true);
4796
4797                           // IPv6 is enabled only as long as access point is connected since:
4798                           // - IPv6 addresses and routes stick around after disconnection
4799                           // - kernel is unaware when connected and fails to start IPv6 negotiation
4800                           // - kernel can start autoconfiguration when 802.1x is not complete
4801                            mNwService.disableIpv6(mInterfaceName);
4802                        } catch (RemoteException re) {
4803                            loge("Unable to change interface settings: " + re);
4804                        } catch (IllegalStateException ie) {
4805                            loge("Unable to change interface settings: " + ie);
4806                        }
4807
4808                       /* Stop a running supplicant after a runtime restart
4809                        * Avoids issues with drivers that do not handle interface down
4810                        * on a running supplicant properly.
4811                        */
4812                        mWifiMonitor.killSupplicant(mP2pSupported);
4813                        if(mWifiNative.startSupplicant(mP2pSupported)) {
4814                            setWifiState(WIFI_STATE_ENABLING);
4815                            if (DBG) log("Supplicant start successful");
4816                            mWifiMonitor.startMonitoring();
4817                            transitionTo(mSupplicantStartingState);
4818                        } else {
4819                            loge("Failed to start supplicant!");
4820                        }
4821                    } else {
4822                        loge("Failed to load driver");
4823                    }
4824                    break;
4825                case CMD_START_AP:
4826                    if (mWifiNative.loadDriver()) {
4827                        setWifiApState(WIFI_AP_STATE_ENABLING);
4828                        transitionTo(mSoftApStartingState);
4829                    } else {
4830                        loge("Failed to load driver for softap");
4831                    }
4832                default:
4833                    return NOT_HANDLED;
4834            }
4835            return HANDLED;
4836        }
4837    }
4838
4839    class SupplicantStartingState extends State {
4840        private void initializeWpsDetails() {
4841            String detail;
4842            detail = SystemProperties.get("ro.product.name", "");
4843            if (!mWifiNative.setDeviceName(detail)) {
4844                loge("Failed to set device name " +  detail);
4845            }
4846            detail = SystemProperties.get("ro.product.manufacturer", "");
4847            if (!mWifiNative.setManufacturer(detail)) {
4848                loge("Failed to set manufacturer " + detail);
4849            }
4850            detail = SystemProperties.get("ro.product.model", "");
4851            if (!mWifiNative.setModelName(detail)) {
4852                loge("Failed to set model name " + detail);
4853            }
4854            detail = SystemProperties.get("ro.product.model", "");
4855            if (!mWifiNative.setModelNumber(detail)) {
4856                loge("Failed to set model number " + detail);
4857            }
4858            detail = SystemProperties.get("ro.serialno", "");
4859            if (!mWifiNative.setSerialNumber(detail)) {
4860                loge("Failed to set serial number " + detail);
4861            }
4862            if (!mWifiNative.setConfigMethods("physical_display virtual_push_button")) {
4863                loge("Failed to set WPS config methods");
4864            }
4865            if (!mWifiNative.setDeviceType(mPrimaryDeviceType)) {
4866                loge("Failed to set primary device type " + mPrimaryDeviceType);
4867            }
4868        }
4869
4870        @Override
4871        public boolean processMessage(Message message) {
4872            logStateAndMessage(message, getClass().getSimpleName());
4873
4874            switch(message.what) {
4875                case WifiMonitor.SUP_CONNECTION_EVENT:
4876                    if (DBG) log("Supplicant connection established");
4877                    setWifiState(WIFI_STATE_ENABLED);
4878                    mSupplicantRestartCount = 0;
4879                    /* Reset the supplicant state to indicate the supplicant
4880                     * state is not known at this time */
4881                    mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
4882                    /* Initialize data structures */
4883                    mLastBssid = null;
4884                    mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
4885                    mLastSignalLevel = -1;
4886
4887                    mWifiInfo.setMacAddress(mWifiNative.getMacAddress());
4888                    mWifiNative.enableSaveConfig();
4889                    mWifiConfigStore.loadAndEnableAllNetworks();
4890                    if (mWifiConfigStore.enableVerboseLogging > 0) {
4891                        enableVerboseLogging(mWifiConfigStore.enableVerboseLogging);
4892                    }
4893                    if (mWifiConfigStore.associatedPartialScanPeriodMilli < 0) {
4894                        mWifiConfigStore.associatedPartialScanPeriodMilli = 0;
4895                    }
4896                    initializeWpsDetails();
4897
4898                    sendSupplicantConnectionChangedBroadcast(true);
4899                    transitionTo(mDriverStartedState);
4900                    break;
4901                case WifiMonitor.SUP_DISCONNECTION_EVENT:
4902                    if (++mSupplicantRestartCount <= SUPPLICANT_RESTART_TRIES) {
4903                        loge("Failed to setup control channel, restart supplicant");
4904                        mWifiMonitor.killSupplicant(mP2pSupported);
4905                        transitionTo(mInitialState);
4906                        sendMessageDelayed(CMD_START_SUPPLICANT, SUPPLICANT_RESTART_INTERVAL_MSECS);
4907                    } else {
4908                        loge("Failed " + mSupplicantRestartCount +
4909                                " times to start supplicant, unload driver");
4910                        mSupplicantRestartCount = 0;
4911                        setWifiState(WIFI_STATE_UNKNOWN);
4912                        transitionTo(mInitialState);
4913                    }
4914                    break;
4915                case CMD_START_SUPPLICANT:
4916                case CMD_STOP_SUPPLICANT:
4917                case CMD_START_AP:
4918                case CMD_STOP_AP:
4919                case CMD_START_DRIVER:
4920                case CMD_STOP_DRIVER:
4921                case CMD_SET_OPERATIONAL_MODE:
4922                case CMD_SET_COUNTRY_CODE:
4923                case CMD_SET_FREQUENCY_BAND:
4924                case CMD_START_PACKET_FILTERING:
4925                case CMD_STOP_PACKET_FILTERING:
4926                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_DEFERRED;
4927                    deferMessage(message);
4928                    break;
4929                default:
4930                    return NOT_HANDLED;
4931            }
4932            return HANDLED;
4933        }
4934    }
4935
4936    class SupplicantStartedState extends State {
4937        @Override
4938        public void enter() {
4939            /* Wifi is available as long as we have a connection to supplicant */
4940            mNetworkInfo.setIsAvailable(true);
4941            if (mNetworkAgent != null) mNetworkAgent.sendNetworkInfo(mNetworkInfo);
4942
4943            int defaultInterval = mContext.getResources().getInteger(
4944                    R.integer.config_wifi_supplicant_scan_interval);
4945
4946            mSupplicantScanIntervalMs = Settings.Global.getLong(mContext.getContentResolver(),
4947                    Settings.Global.WIFI_SUPPLICANT_SCAN_INTERVAL_MS,
4948                    defaultInterval);
4949
4950            mWifiNative.setScanInterval((int)mSupplicantScanIntervalMs / 1000);
4951            mWifiNative.setExternalSim(true);
4952
4953            setRandomMacOui();
4954            mWifiNative.enableAutoConnect(false);
4955        }
4956
4957        @Override
4958        public boolean processMessage(Message message) {
4959            logStateAndMessage(message, getClass().getSimpleName());
4960
4961            switch(message.what) {
4962                case CMD_STOP_SUPPLICANT:   /* Supplicant stopped by user */
4963                    if (mP2pSupported) {
4964                        transitionTo(mWaitForP2pDisableState);
4965                    } else {
4966                        transitionTo(mSupplicantStoppingState);
4967                    }
4968                    break;
4969                case WifiMonitor.SUP_DISCONNECTION_EVENT:  /* Supplicant connection lost */
4970                    loge("Connection lost, restart supplicant");
4971                    handleSupplicantConnectionLoss();
4972                    handleNetworkDisconnect();
4973                    mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
4974                    if (mP2pSupported) {
4975                        transitionTo(mWaitForP2pDisableState);
4976                    } else {
4977                        transitionTo(mInitialState);
4978                    }
4979                    sendMessageDelayed(CMD_START_SUPPLICANT, SUPPLICANT_RESTART_INTERVAL_MSECS);
4980                    break;
4981                case WifiMonitor.SCAN_RESULTS_EVENT:
4982                    closeRadioScanStats();
4983                    noteScanEnd();
4984                    setScanResults();
4985                    if (mIsFullScanOngoing) {
4986                        /* Just updated results from full scan, let apps know about this */
4987                        sendScanResultsAvailableBroadcast();
4988                    }
4989                    mIsScanOngoing = false;
4990                    mIsFullScanOngoing = false;
4991                    if (mBufferedScanMsg.size() > 0)
4992                        sendMessage(mBufferedScanMsg.remove());
4993                    break;
4994                case CMD_PING_SUPPLICANT:
4995                    boolean ok = mWifiNative.ping();
4996                    replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
4997                    break;
4998                case CMD_GET_CAPABILITY_FREQ:
4999                    String freqs = mWifiNative.getFreqCapability();
5000                    replyToMessage(message, message.what, freqs);
5001                    break;
5002                case CMD_START_AP:
5003                    /* Cannot start soft AP while in client mode */
5004                    loge("Failed to start soft AP with a running supplicant");
5005                    setWifiApState(WIFI_AP_STATE_FAILED);
5006                    break;
5007                case CMD_SET_OPERATIONAL_MODE:
5008                    mOperationalMode = message.arg1;
5009                    break;
5010                case CMD_TARGET_BSSID:
5011                    // Trying to associate to this BSSID
5012                    if (message.obj != null) {
5013                        mTargetRoamBSSID = (String) message.obj;
5014                    }
5015                    break;
5016                case CMD_GET_LINK_LAYER_STATS:
5017                    WifiLinkLayerStats stats = getWifiLinkLayerStats(DBG);
5018                    if (stats == null) {
5019                        // When firmware doesnt support link layer stats, return an empty object
5020                        stats = new WifiLinkLayerStats();
5021                    }
5022                    replyToMessage(message, message.what, stats);
5023                    break;
5024                default:
5025                    return NOT_HANDLED;
5026            }
5027            return HANDLED;
5028        }
5029
5030        @Override
5031        public void exit() {
5032            mNetworkInfo.setIsAvailable(false);
5033            if (mNetworkAgent != null) mNetworkAgent.sendNetworkInfo(mNetworkInfo);
5034        }
5035    }
5036
5037    class SupplicantStoppingState extends State {
5038        @Override
5039        public void enter() {
5040            /* Send any reset commands to supplicant before shutting it down */
5041            handleNetworkDisconnect();
5042            if (mDhcpStateMachine != null) {
5043                mDhcpStateMachine.doQuit();
5044            }
5045
5046            if (DBG) log("stopping supplicant");
5047            mWifiMonitor.stopSupplicant();
5048
5049            /* Send ourselves a delayed message to indicate failure after a wait time */
5050            sendMessageDelayed(obtainMessage(CMD_STOP_SUPPLICANT_FAILED,
5051                    ++mSupplicantStopFailureToken, 0), SUPPLICANT_RESTART_INTERVAL_MSECS);
5052            setWifiState(WIFI_STATE_DISABLING);
5053            mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
5054        }
5055        @Override
5056        public boolean processMessage(Message message) {
5057            logStateAndMessage(message, getClass().getSimpleName());
5058
5059            switch(message.what) {
5060                case WifiMonitor.SUP_CONNECTION_EVENT:
5061                    loge("Supplicant connection received while stopping");
5062                    break;
5063                case WifiMonitor.SUP_DISCONNECTION_EVENT:
5064                    if (DBG) log("Supplicant connection lost");
5065                    handleSupplicantConnectionLoss();
5066                    transitionTo(mInitialState);
5067                    break;
5068                case CMD_STOP_SUPPLICANT_FAILED:
5069                    if (message.arg1 == mSupplicantStopFailureToken) {
5070                        loge("Timed out on a supplicant stop, kill and proceed");
5071                        handleSupplicantConnectionLoss();
5072                        transitionTo(mInitialState);
5073                    }
5074                    break;
5075                case CMD_START_SUPPLICANT:
5076                case CMD_STOP_SUPPLICANT:
5077                case CMD_START_AP:
5078                case CMD_STOP_AP:
5079                case CMD_START_DRIVER:
5080                case CMD_STOP_DRIVER:
5081                case CMD_SET_OPERATIONAL_MODE:
5082                case CMD_SET_COUNTRY_CODE:
5083                case CMD_SET_FREQUENCY_BAND:
5084                case CMD_START_PACKET_FILTERING:
5085                case CMD_STOP_PACKET_FILTERING:
5086                    deferMessage(message);
5087                    break;
5088                default:
5089                    return NOT_HANDLED;
5090            }
5091            return HANDLED;
5092        }
5093    }
5094
5095    class DriverStartingState extends State {
5096        private int mTries;
5097        @Override
5098        public void enter() {
5099            mTries = 1;
5100            /* Send ourselves a delayed message to start driver a second time */
5101            sendMessageDelayed(obtainMessage(CMD_DRIVER_START_TIMED_OUT,
5102                        ++mDriverStartToken, 0), DRIVER_START_TIME_OUT_MSECS);
5103        }
5104        @Override
5105        public boolean processMessage(Message message) {
5106            logStateAndMessage(message, getClass().getSimpleName());
5107
5108            switch(message.what) {
5109               case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
5110                    SupplicantState state = handleSupplicantStateChange(message);
5111                    /* If suplicant is exiting out of INTERFACE_DISABLED state into
5112                     * a state that indicates driver has started, it is ready to
5113                     * receive driver commands
5114                     */
5115                    if (SupplicantState.isDriverActive(state)) {
5116                        transitionTo(mDriverStartedState);
5117                    }
5118                    break;
5119                case CMD_DRIVER_START_TIMED_OUT:
5120                    if (message.arg1 == mDriverStartToken) {
5121                        if (mTries >= 2) {
5122                            loge("Failed to start driver after " + mTries);
5123                            transitionTo(mDriverStoppedState);
5124                        } else {
5125                            loge("Driver start failed, retrying");
5126                            mWakeLock.acquire();
5127                            mWifiNative.startDriver();
5128                            mWakeLock.release();
5129
5130                            ++mTries;
5131                            /* Send ourselves a delayed message to start driver again */
5132                            sendMessageDelayed(obtainMessage(CMD_DRIVER_START_TIMED_OUT,
5133                                        ++mDriverStartToken, 0), DRIVER_START_TIME_OUT_MSECS);
5134                        }
5135                    }
5136                    break;
5137                    /* Queue driver commands & connection events */
5138                case CMD_START_DRIVER:
5139                case CMD_STOP_DRIVER:
5140                case WifiMonitor.NETWORK_CONNECTION_EVENT:
5141                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
5142                case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
5143                case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
5144                case WifiMonitor.WPS_OVERLAP_EVENT:
5145                case CMD_SET_COUNTRY_CODE:
5146                case CMD_SET_FREQUENCY_BAND:
5147                case CMD_START_PACKET_FILTERING:
5148                case CMD_STOP_PACKET_FILTERING:
5149                case CMD_START_SCAN:
5150                case CMD_DISCONNECT:
5151                case CMD_REASSOCIATE:
5152                case CMD_RECONNECT:
5153                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_DEFERRED;
5154                    deferMessage(message);
5155                    break;
5156                case WifiMonitor.SCAN_RESULTS_EVENT:
5157                    // Loose scan results obtained in Driver Starting state, they can only confuse
5158                    // the state machine
5159                    break;
5160                default:
5161                    return NOT_HANDLED;
5162            }
5163            return HANDLED;
5164        }
5165    }
5166
5167    class DriverStartedState extends State {
5168        @Override
5169        public void enter() {
5170
5171            if (PDBG) {
5172                loge("Driverstarted State enter");
5173            }
5174            mIsRunning = true;
5175            mInDelayedStop = false;
5176            mDelayedStopCounter++;
5177            updateBatteryWorkSource(null);
5178            /**
5179             * Enable bluetooth coexistence scan mode when bluetooth connection is active.
5180             * When this mode is on, some of the low-level scan parameters used by the
5181             * driver are changed to reduce interference with bluetooth
5182             */
5183            mWifiNative.setBluetoothCoexistenceScanMode(mBluetoothConnectionActive);
5184            /* set country code */
5185            setCountryCode();
5186            /* set frequency band of operation */
5187            setFrequencyBand();
5188            /* initialize network state */
5189            setNetworkDetailedState(DetailedState.DISCONNECTED);
5190
5191            /* Remove any filtering on Multicast v6 at start */
5192            mWifiNative.stopFilteringMulticastV6Packets();
5193
5194            /* Reset Multicast v4 filtering state */
5195            if (mFilteringMulticastV4Packets.get()) {
5196                mWifiNative.startFilteringMulticastV4Packets();
5197            } else {
5198                mWifiNative.stopFilteringMulticastV4Packets();
5199            }
5200
5201            mDhcpActive = false;
5202
5203            startBatchedScan();
5204
5205            if (mOperationalMode != CONNECT_MODE) {
5206                mWifiNative.disconnect();
5207                mWifiConfigStore.disableAllNetworks();
5208                if (mOperationalMode == SCAN_ONLY_WITH_WIFI_OFF_MODE) {
5209                    setWifiState(WIFI_STATE_DISABLED);
5210                }
5211                transitionTo(mScanModeState);
5212            } else {
5213
5214                // Status pulls in the current supplicant state and network connection state
5215                // events over the monitor connection. This helps framework sync up with
5216                // current supplicant state
5217                // TODO: actually check th supplicant status string and make sure the supplicant
5218                // is in disconnecte4d state.
5219                mWifiNative.status();
5220                // Transitioning to Disconnected state will trigger a scan and subsequently AutoJoin
5221                transitionTo(mDisconnectedState);
5222            }
5223
5224            // We may have missed screen update at boot
5225            if (mScreenBroadcastReceived.get() == false) {
5226                PowerManager powerManager = (PowerManager)mContext.getSystemService(
5227                        Context.POWER_SERVICE);
5228                handleScreenStateChanged(powerManager.isScreenOn(),
5229                        /* startBackgroundScanIfNeeded = */ false);
5230            } else {
5231                // Set the right suspend mode settings
5232                mWifiNative.setSuspendOptimizations(mSuspendOptNeedsDisabled == 0
5233                        && mUserWantsSuspendOpt.get());
5234            }
5235            mWifiNative.setPowerSave(true);
5236
5237            if (mP2pSupported) {
5238                if (mOperationalMode == CONNECT_MODE) {
5239                    mWifiP2pChannel.sendMessage(WifiStateMachine.CMD_ENABLE_P2P);
5240                } else {
5241                    // P2P statemachine starts in disabled state, and is not enabled until
5242                    // CMD_ENABLE_P2P is sent from here; so, nothing needs to be done to
5243                    // keep it disabled.
5244                }
5245            }
5246
5247            final Intent intent = new Intent(WifiManager.WIFI_SCAN_AVAILABLE);
5248            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
5249            intent.putExtra(WifiManager.EXTRA_SCAN_AVAILABLE, WIFI_STATE_ENABLED);
5250            mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
5251
5252            if (PDBG) {
5253                loge("Driverstarted State enter done");
5254            }
5255        }
5256
5257        @Override
5258        public boolean processMessage(Message message) {
5259            logStateAndMessage(message, getClass().getSimpleName());
5260
5261            switch(message.what) {
5262                case CMD_START_SCAN:
5263                    handleScanRequest(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, message);
5264                    break;
5265                case CMD_SET_BATCHED_SCAN:
5266                    if (recordBatchedScanSettings(message.arg1, message.arg2,
5267                            (Bundle)message.obj)) {
5268                        if (mBatchedScanSettings != null) {
5269                            startBatchedScan();
5270                        } else {
5271                            stopBatchedScan();
5272                        }
5273                    }
5274                    break;
5275                case CMD_SET_COUNTRY_CODE:
5276                    String country = (String) message.obj;
5277                    final boolean persist = (message.arg2 == 1);
5278                    final int sequence = message.arg1;
5279                    if (sequence != mCountryCodeSequence.get()) {
5280                        if (DBG) log("set country code ignored due to sequnce num");
5281                        break;
5282                    }
5283                    if (DBG) log("set country code " + country);
5284                    if (persist) {
5285                        mPersistedCountryCode = country;
5286                        Settings.Global.putString(mContext.getContentResolver(),
5287                                Settings.Global.WIFI_COUNTRY_CODE,
5288                                country);
5289                    }
5290                    country = country.toUpperCase(Locale.ROOT);
5291                    if (mLastSetCountryCode == null
5292                            || country.equals(mLastSetCountryCode) == false) {
5293                        if (mWifiNative.setCountryCode(country)) {
5294                            mLastSetCountryCode = country;
5295                        } else {
5296                            loge("Failed to set country code " + country);
5297                        }
5298                    }
5299                    mWifiP2pChannel.sendMessage(WifiP2pServiceImpl.SET_COUNTRY_CODE, country);
5300                    break;
5301                case CMD_SET_FREQUENCY_BAND:
5302                    int band =  message.arg1;
5303                    if (DBG) log("set frequency band " + band);
5304                    if (mWifiNative.setBand(band)) {
5305
5306                        if (PDBG)  loge("did set frequency band " + band);
5307
5308                        mFrequencyBand.set(band);
5309                        // Flush old data - like scan results
5310                        mWifiNative.bssFlush();
5311                        // Fetch the latest scan results when frequency band is set
5312                        startScanNative(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, null);
5313
5314                        if (PDBG)  loge("done set frequency band " + band);
5315
5316                    } else {
5317                        loge("Failed to set frequency band " + band);
5318                    }
5319                    break;
5320                case CMD_BLUETOOTH_ADAPTER_STATE_CHANGE:
5321                    mBluetoothConnectionActive = (message.arg1 !=
5322                            BluetoothAdapter.STATE_DISCONNECTED);
5323                    mWifiNative.setBluetoothCoexistenceScanMode(mBluetoothConnectionActive);
5324                    break;
5325                case CMD_STOP_DRIVER:
5326                    int mode = message.arg1;
5327
5328                    /* Already doing a delayed stop */
5329                    if (mInDelayedStop) {
5330                        if (DBG) log("Already in delayed stop");
5331                        break;
5332                    }
5333                    /* disconnect right now, but leave the driver running for a bit */
5334                    mWifiConfigStore.disableAllNetworks();
5335
5336                    mInDelayedStop = true;
5337                    mDelayedStopCounter++;
5338                    if (DBG) log("Delayed stop message " + mDelayedStopCounter);
5339
5340                    /* send regular delayed shut down */
5341                    Intent driverStopIntent = new Intent(ACTION_DELAYED_DRIVER_STOP, null);
5342                    driverStopIntent.putExtra(DELAYED_STOP_COUNTER, mDelayedStopCounter);
5343                    mDriverStopIntent = PendingIntent.getBroadcast(mContext,
5344                            DRIVER_STOP_REQUEST, driverStopIntent,
5345                            PendingIntent.FLAG_UPDATE_CURRENT);
5346
5347                    mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()
5348                            + mDriverStopDelayMs, mDriverStopIntent);
5349                    break;
5350                case CMD_START_DRIVER:
5351                    if (mInDelayedStop) {
5352                        mInDelayedStop = false;
5353                        mDelayedStopCounter++;
5354                        mAlarmManager.cancel(mDriverStopIntent);
5355                        if (DBG) log("Delayed stop ignored due to start");
5356                        if (mOperationalMode == CONNECT_MODE) {
5357                            mWifiConfigStore.enableAllNetworks();
5358                        }
5359                    }
5360                    break;
5361                case CMD_DELAYED_STOP_DRIVER:
5362                    if (DBG) log("delayed stop " + message.arg1 + " " + mDelayedStopCounter);
5363                    if (message.arg1 != mDelayedStopCounter) break;
5364                    if (getCurrentState() != mDisconnectedState) {
5365                        mWifiNative.disconnect();
5366                        handleNetworkDisconnect();
5367                    }
5368                    mWakeLock.acquire();
5369                    mWifiNative.stopDriver();
5370                    mWakeLock.release();
5371                    if (mP2pSupported) {
5372                        transitionTo(mWaitForP2pDisableState);
5373                    } else {
5374                        transitionTo(mDriverStoppingState);
5375                    }
5376                    break;
5377                case CMD_START_PACKET_FILTERING:
5378                    if (message.arg1 == MULTICAST_V6) {
5379                        mWifiNative.startFilteringMulticastV6Packets();
5380                    } else if (message.arg1 == MULTICAST_V4) {
5381                        mWifiNative.startFilteringMulticastV4Packets();
5382                    } else {
5383                        loge("Illegal arugments to CMD_START_PACKET_FILTERING");
5384                    }
5385                    break;
5386                case CMD_STOP_PACKET_FILTERING:
5387                    if (message.arg1 == MULTICAST_V6) {
5388                        mWifiNative.stopFilteringMulticastV6Packets();
5389                    } else if (message.arg1 == MULTICAST_V4) {
5390                        mWifiNative.stopFilteringMulticastV4Packets();
5391                    } else {
5392                        loge("Illegal arugments to CMD_STOP_PACKET_FILTERING");
5393                    }
5394                    break;
5395                case CMD_SET_SUSPEND_OPT_ENABLED:
5396                    if (message.arg1 == 1) {
5397                        setSuspendOptimizationsNative(SUSPEND_DUE_TO_SCREEN, true);
5398                        mSuspendWakeLock.release();
5399                    } else {
5400                        setSuspendOptimizationsNative(SUSPEND_DUE_TO_SCREEN, false);
5401                    }
5402                    break;
5403                case CMD_SET_HIGH_PERF_MODE:
5404                    if (message.arg1 == 1) {
5405                        setSuspendOptimizationsNative(SUSPEND_DUE_TO_HIGH_PERF, false);
5406                    } else {
5407                        setSuspendOptimizationsNative(SUSPEND_DUE_TO_HIGH_PERF, true);
5408                    }
5409                    break;
5410                case CMD_ENABLE_TDLS:
5411                    if (message.obj != null) {
5412                        String remoteAddress = (String) message.obj;
5413                        boolean enable = (message.arg1 == 1);
5414                        mWifiNative.startTdls(remoteAddress, enable);
5415                    }
5416                    break;
5417                default:
5418                    return NOT_HANDLED;
5419            }
5420            return HANDLED;
5421        }
5422        @Override
5423        public void exit() {
5424            mIsRunning = false;
5425            updateBatteryWorkSource(null);
5426            mScanResults = new ArrayList<ScanResult>();
5427
5428            stopBatchedScan();
5429
5430            final Intent intent = new Intent(WifiManager.WIFI_SCAN_AVAILABLE);
5431            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
5432            intent.putExtra(WifiManager.EXTRA_SCAN_AVAILABLE, WIFI_STATE_DISABLED);
5433            mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
5434            noteScanEnd(); // wrap up any pending request.
5435            mBufferedScanMsg.clear();
5436
5437            mLastSetCountryCode = null;
5438        }
5439    }
5440
5441    class WaitForP2pDisableState extends State {
5442        private State mTransitionToState;
5443        @Override
5444        public void enter() {
5445            switch (getCurrentMessage().what) {
5446                case WifiMonitor.SUP_DISCONNECTION_EVENT:
5447                    mTransitionToState = mInitialState;
5448                    break;
5449                case CMD_DELAYED_STOP_DRIVER:
5450                    mTransitionToState = mDriverStoppingState;
5451                    break;
5452                case CMD_STOP_SUPPLICANT:
5453                    mTransitionToState = mSupplicantStoppingState;
5454                    break;
5455                default:
5456                    mTransitionToState = mDriverStoppingState;
5457                    break;
5458            }
5459            mWifiP2pChannel.sendMessage(WifiStateMachine.CMD_DISABLE_P2P_REQ);
5460        }
5461        @Override
5462        public boolean processMessage(Message message) {
5463            logStateAndMessage(message, getClass().getSimpleName());
5464
5465            switch(message.what) {
5466                case WifiStateMachine.CMD_DISABLE_P2P_RSP:
5467                    transitionTo(mTransitionToState);
5468                    break;
5469                /* Defer wifi start/shut and driver commands */
5470                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
5471                case CMD_START_SUPPLICANT:
5472                case CMD_STOP_SUPPLICANT:
5473                case CMD_START_AP:
5474                case CMD_STOP_AP:
5475                case CMD_START_DRIVER:
5476                case CMD_STOP_DRIVER:
5477                case CMD_SET_OPERATIONAL_MODE:
5478                case CMD_SET_COUNTRY_CODE:
5479                case CMD_SET_FREQUENCY_BAND:
5480                case CMD_START_PACKET_FILTERING:
5481                case CMD_STOP_PACKET_FILTERING:
5482                case CMD_START_SCAN:
5483                case CMD_DISCONNECT:
5484                case CMD_REASSOCIATE:
5485                case CMD_RECONNECT:
5486                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_DEFERRED;
5487                    deferMessage(message);
5488                    break;
5489                default:
5490                    return NOT_HANDLED;
5491            }
5492            return HANDLED;
5493        }
5494    }
5495
5496    class DriverStoppingState extends State {
5497        @Override
5498        public boolean processMessage(Message message) {
5499            logStateAndMessage(message, getClass().getSimpleName());
5500
5501            switch(message.what) {
5502                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
5503                    SupplicantState state = handleSupplicantStateChange(message);
5504                    if (state == SupplicantState.INTERFACE_DISABLED) {
5505                        transitionTo(mDriverStoppedState);
5506                    }
5507                    break;
5508                    /* Queue driver commands */
5509                case CMD_START_DRIVER:
5510                case CMD_STOP_DRIVER:
5511                case CMD_SET_COUNTRY_CODE:
5512                case CMD_SET_FREQUENCY_BAND:
5513                case CMD_START_PACKET_FILTERING:
5514                case CMD_STOP_PACKET_FILTERING:
5515                case CMD_START_SCAN:
5516                case CMD_DISCONNECT:
5517                case CMD_REASSOCIATE:
5518                case CMD_RECONNECT:
5519                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_DEFERRED;
5520                    deferMessage(message);
5521                    break;
5522                default:
5523                    return NOT_HANDLED;
5524            }
5525            return HANDLED;
5526        }
5527    }
5528
5529    class DriverStoppedState extends State {
5530        @Override
5531        public boolean processMessage(Message message) {
5532            logStateAndMessage(message, getClass().getSimpleName());
5533            switch (message.what) {
5534                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
5535                    StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
5536                    SupplicantState state = stateChangeResult.state;
5537                    // A WEXT bug means that we can be back to driver started state
5538                    // unexpectedly
5539                    if (SupplicantState.isDriverActive(state)) {
5540                        transitionTo(mDriverStartedState);
5541                    }
5542                    break;
5543                case CMD_START_DRIVER:
5544                    mWakeLock.acquire();
5545                    mWifiNative.startDriver();
5546                    mWakeLock.release();
5547                    transitionTo(mDriverStartingState);
5548                    break;
5549                default:
5550                    return NOT_HANDLED;
5551            }
5552            return HANDLED;
5553        }
5554    }
5555
5556    class ScanModeState extends State {
5557        private int mLastOperationMode;
5558        @Override
5559        public void enter() {
5560            mLastOperationMode = mOperationalMode;
5561        }
5562        @Override
5563        public boolean processMessage(Message message) {
5564            logStateAndMessage(message, getClass().getSimpleName());
5565
5566            switch(message.what) {
5567                case CMD_SET_OPERATIONAL_MODE:
5568                    if (message.arg1 == CONNECT_MODE) {
5569
5570                        if (mLastOperationMode == SCAN_ONLY_WITH_WIFI_OFF_MODE) {
5571                            setWifiState(WIFI_STATE_ENABLED);
5572                            // Load and re-enable networks when going back to enabled state
5573                            // This is essential for networks to show up after restore
5574                            mWifiConfigStore.loadAndEnableAllNetworks();
5575                            mWifiP2pChannel.sendMessage(CMD_ENABLE_P2P);
5576                        } else {
5577                            mWifiConfigStore.enableAllNetworks();
5578                        }
5579
5580                        mWifiNative.reconnect();
5581
5582                        mOperationalMode = CONNECT_MODE;
5583                        transitionTo(mDisconnectedState);
5584                    } else {
5585                        // Nothing to do
5586                        return HANDLED;
5587                    }
5588                    break;
5589                // Handle scan. All the connection related commands are
5590                // handled only in ConnectModeState
5591                case CMD_START_SCAN:
5592                    handleScanRequest(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, message);
5593                    break;
5594                default:
5595                    return NOT_HANDLED;
5596            }
5597            return HANDLED;
5598        }
5599    }
5600
5601
5602    String smToString(Message message) {
5603        return smToString(message.what);
5604    }
5605
5606    String smToString(int what) {
5607        String s = "unknown";
5608        switch (what) {
5609            case WifiMonitor.DRIVER_HUNG_EVENT:
5610                s = "DRIVER_HUNG_EVENT";
5611                break;
5612            case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
5613                s = "AsyncChannel.CMD_CHANNEL_HALF_CONNECTED";
5614                break;
5615            case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
5616                s = "AsyncChannel.CMD_CHANNEL_DISCONNECTED";
5617                break;
5618            case CMD_SET_FREQUENCY_BAND:
5619                s = "CMD_SET_FREQUENCY_BAND";
5620                break;
5621            case CMD_DELAYED_NETWORK_DISCONNECT:
5622                s = "CMD_DELAYED_NETWORK_DISCONNECT";
5623                break;
5624            case CMD_TEST_NETWORK_DISCONNECT:
5625                s = "CMD_TEST_NETWORK_DISCONNECT";
5626                break;
5627            case CMD_OBTAINING_IP_ADDRESS_WATCHDOG_TIMER:
5628                s = "CMD_OBTAINING_IP_ADDRESS_WATCHDOG_TIMER";
5629                break;
5630            case CMD_START_DRIVER:
5631                s = "CMD_START_DRIVER";
5632                break;
5633            case CMD_STOP_DRIVER:
5634                s = "CMD_STOP_DRIVER";
5635                break;
5636            case CMD_STOP_SUPPLICANT:
5637                s = "CMD_STOP_SUPPLICANT";
5638                break;
5639            case CMD_STOP_SUPPLICANT_FAILED:
5640                s = "CMD_STOP_SUPPLICANT_FAILED";
5641                break;
5642            case CMD_START_SUPPLICANT:
5643                s = "CMD_START_SUPPLICANT";
5644                break;
5645            case CMD_REQUEST_AP_CONFIG:
5646                s = "CMD_REQUEST_AP_CONFIG";
5647                break;
5648            case CMD_RESPONSE_AP_CONFIG:
5649                s = "CMD_RESPONSE_AP_CONFIG";
5650                break;
5651            case CMD_TETHER_STATE_CHANGE:
5652                s = "CMD_TETHER_STATE_CHANGE";
5653                break;
5654            case CMD_TETHER_NOTIFICATION_TIMED_OUT:
5655                s = "CMD_TETHER_NOTIFICATION_TIMED_OUT";
5656                break;
5657            case CMD_BLUETOOTH_ADAPTER_STATE_CHANGE:
5658                s = "CMD_BLUETOOTH_ADAPTER_STATE_CHANGE";
5659                break;
5660            case CMD_ADD_OR_UPDATE_NETWORK:
5661                s = "CMD_ADD_OR_UPDATE_NETWORK";
5662                break;
5663            case CMD_REMOVE_NETWORK:
5664                s = "CMD_REMOVE_NETWORK";
5665                break;
5666            case CMD_ENABLE_NETWORK:
5667                s = "CMD_ENABLE_NETWORK";
5668                break;
5669            case CMD_ENABLE_ALL_NETWORKS:
5670                s = "CMD_ENABLE_ALL_NETWORKS";
5671                break;
5672            case CMD_AUTO_CONNECT:
5673                s = "CMD_AUTO_CONNECT";
5674                break;
5675            case CMD_AUTO_ROAM:
5676                s = "CMD_AUTO_ROAM";
5677                break;
5678            case CMD_AUTO_SAVE_NETWORK:
5679                s = "CMD_AUTO_SAVE_NETWORK";
5680                break;
5681            case CMD_BOOT_COMPLETED:
5682                s = "CMD_BOOT_COMPLETED";
5683                break;
5684            case DhcpStateMachine.CMD_START_DHCP:
5685                s = "CMD_START_DHCP";
5686                break;
5687            case DhcpStateMachine.CMD_STOP_DHCP:
5688                s = "CMD_STOP_DHCP";
5689                break;
5690            case DhcpStateMachine.CMD_RENEW_DHCP:
5691                s = "CMD_RENEW_DHCP";
5692                break;
5693            case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
5694                s = "CMD_PRE_DHCP_ACTION";
5695                break;
5696            case DhcpStateMachine.CMD_POST_DHCP_ACTION:
5697                s = "CMD_POST_DHCP_ACTION";
5698                break;
5699            case DhcpStateMachine.CMD_PRE_DHCP_ACTION_COMPLETE:
5700                s = "CMD_PRE_DHCP_ACTION_COMPLETE";
5701                break;
5702            case DhcpStateMachine.CMD_ON_QUIT:
5703                s = "CMD_ON_QUIT";
5704                break;
5705            case WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST:
5706                s = "WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST";
5707                break;
5708            case WifiManager.DISABLE_NETWORK:
5709                s = "WifiManager.DISABLE_NETWORK";
5710                break;
5711            case CMD_BLACKLIST_NETWORK:
5712                s = "CMD_BLACKLIST_NETWORK";
5713                break;
5714            case CMD_CLEAR_BLACKLIST:
5715                s = "CMD_CLEAR_BLACKLIST";
5716                break;
5717            case CMD_SAVE_CONFIG:
5718                s = "CMD_SAVE_CONFIG";
5719                break;
5720            case CMD_GET_CONFIGURED_NETWORKS:
5721                s = "CMD_GET_CONFIGURED_NETWORKS";
5722                break;
5723            case CMD_GET_SUPPORTED_FEATURES:
5724                s = "CMD_GET_ADAPTORS";
5725                break;
5726            case CMD_UNWANTED_NETWORK:
5727                s = "CMD_UNWANTED_NETWORK";
5728                break;
5729            case CMD_GET_LINK_LAYER_STATS:
5730                s = "CMD_GET_LINK_LAYER_STATS";
5731                break;
5732            case CMD_GET_PRIVILEGED_CONFIGURED_NETWORKS:
5733                s = "CMD_GET_PRIVILEGED_CONFIGURED_NETWORKS";
5734                break;
5735            case CMD_DISCONNECT:
5736                s = "CMD_DISCONNECT";
5737                break;
5738            case CMD_RECONNECT:
5739                s = "CMD_RECONNECT";
5740                break;
5741            case CMD_REASSOCIATE:
5742                s = "CMD_REASSOCIATE";
5743                break;
5744            case CMD_GET_CONNECTION_STATISTICS:
5745                s = "CMD_GET_CONNECTION_STATISTICS";
5746                break;
5747            case CMD_SET_HIGH_PERF_MODE:
5748                s = "CMD_SET_HIGH_PERF_MODE";
5749                break;
5750            case CMD_SET_COUNTRY_CODE:
5751                s = "CMD_SET_COUNTRY_CODE";
5752                break;
5753            case CMD_ENABLE_RSSI_POLL:
5754                s = "CMD_ENABLE_RSSI_POLL";
5755                break;
5756            case CMD_RSSI_POLL:
5757                s = "CMD_RSSI_POLL";
5758                break;
5759            case CMD_START_PACKET_FILTERING:
5760                s = "CMD_START_PACKET_FILTERING";
5761                break;
5762            case CMD_STOP_PACKET_FILTERING:
5763                s = "CMD_STOP_PACKET_FILTERING";
5764                break;
5765            case CMD_SET_SUSPEND_OPT_ENABLED:
5766                s = "CMD_SET_SUSPEND_OPT_ENABLED";
5767                break;
5768            case CMD_NO_NETWORKS_PERIODIC_SCAN:
5769                s = "CMD_NO_NETWORKS_PERIODIC_SCAN";
5770                break;
5771            case CMD_SET_BATCHED_SCAN:
5772                s = "CMD_SET_BATCHED_SCAN";
5773                break;
5774            case CMD_START_NEXT_BATCHED_SCAN:
5775                s = "CMD_START_NEXT_BATCHED_SCAN";
5776                break;
5777            case CMD_POLL_BATCHED_SCAN:
5778                s = "CMD_POLL_BATCHED_SCAN";
5779                break;
5780            case CMD_UPDATE_LINKPROPERTIES:
5781                s = "CMD_UPDATE_LINKPROPERTIES";
5782                break;
5783            case CMD_RELOAD_TLS_AND_RECONNECT:
5784                s = "CMD_RELOAD_TLS_AND_RECONNECT";
5785                break;
5786            case WifiManager.CONNECT_NETWORK:
5787                s = "CONNECT_NETWORK";
5788                break;
5789            case WifiManager.SAVE_NETWORK:
5790                s = "SAVE_NETWORK";
5791                break;
5792            case WifiManager.FORGET_NETWORK:
5793                s = "FORGET_NETWORK";
5794                break;
5795            case WifiMonitor.SUP_CONNECTION_EVENT:
5796                s = "SUP_CONNECTION_EVENT";
5797                break;
5798            case WifiMonitor.SUP_DISCONNECTION_EVENT:
5799                s = "SUP_DISCONNECTION_EVENT";
5800                break;
5801            case WifiMonitor.SCAN_RESULTS_EVENT:
5802                s = "SCAN_RESULTS_EVENT";
5803                break;
5804            case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
5805                s = "SUPPLICANT_STATE_CHANGE_EVENT";
5806                break;
5807            case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
5808                s = "AUTHENTICATION_FAILURE_EVENT";
5809                break;
5810            case WifiMonitor.SSID_TEMP_DISABLED:
5811                s = "SSID_TEMP_DISABLED";
5812                break;
5813            case WifiMonitor.SSID_REENABLED:
5814                s = "SSID_REENABLED";
5815                break;
5816            case WifiMonitor.WPS_SUCCESS_EVENT:
5817                s = "WPS_SUCCESS_EVENT";
5818                break;
5819            case WifiMonitor.WPS_FAIL_EVENT:
5820                s = "WPS_FAIL_EVENT";
5821                break;
5822            case WifiMonitor.SUP_REQUEST_IDENTITY:
5823                s = "SUP_REQUEST_IDENTITY";
5824                break;
5825            case WifiMonitor.NETWORK_CONNECTION_EVENT:
5826                s = "NETWORK_CONNECTION_EVENT";
5827                break;
5828            case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
5829                s = "NETWORK_DISCONNECTION_EVENT";
5830                break;
5831            case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
5832                s = "ASSOCIATION_REJECTION_EVENT";
5833                break;
5834            case CMD_SET_OPERATIONAL_MODE:
5835                s = "CMD_SET_OPERATIONAL_MODE";
5836                break;
5837            case CMD_START_SCAN:
5838                s = "CMD_START_SCAN";
5839                break;
5840            case CMD_DISABLE_P2P_RSP:
5841                s = "CMD_DISABLE_P2P_RSP";
5842                break;
5843            case CMD_DISABLE_P2P_REQ:
5844                s = "CMD_DISABLE_P2P_REQ";
5845                break;
5846            case WifiWatchdogStateMachine.GOOD_LINK_DETECTED:
5847                s = "GOOD_LINK_DETECTED";
5848                break;
5849            case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
5850                s = "POOR_LINK_DETECTED";
5851                break;
5852            case WifiP2pServiceImpl.GROUP_CREATING_TIMED_OUT:
5853                s = "GROUP_CREATING_TIMED_OUT";
5854                break;
5855            case WifiP2pServiceImpl.P2P_CONNECTION_CHANGED:
5856                s = "P2P_CONNECTION_CHANGED";
5857                break;
5858            case WifiP2pServiceImpl.DISCONNECT_WIFI_RESPONSE:
5859                s = "P2P.DISCONNECT_WIFI_RESPONSE";
5860                break;
5861            case WifiP2pServiceImpl.SET_MIRACAST_MODE:
5862                s = "P2P.SET_MIRACAST_MODE";
5863                break;
5864            case WifiP2pServiceImpl.BLOCK_DISCOVERY:
5865                s = "P2P.BLOCK_DISCOVERY";
5866                break;
5867            case WifiP2pServiceImpl.SET_COUNTRY_CODE:
5868                s = "P2P.SET_COUNTRY_CODE";
5869                break;
5870            case WifiManager.CANCEL_WPS:
5871                s = "CANCEL_WPS";
5872                break;
5873            case WifiManager.CANCEL_WPS_FAILED:
5874                s = "CANCEL_WPS_FAILED";
5875                break;
5876            case WifiManager.CANCEL_WPS_SUCCEDED:
5877                s = "CANCEL_WPS_SUCCEDED";
5878                break;
5879            case WifiManager.START_WPS:
5880                s = "START_WPS";
5881                break;
5882            case WifiManager.START_WPS_SUCCEEDED:
5883                s = "START_WPS_SUCCEEDED";
5884                break;
5885            case WifiManager.WPS_FAILED:
5886                s = "WPS_FAILED";
5887                break;
5888            case WifiManager.WPS_COMPLETED:
5889                s = "WPS_COMPLETED";
5890                break;
5891            case WifiManager.RSSI_PKTCNT_FETCH:
5892                s = "RSSI_PKTCNT_FETCH";
5893                break;
5894            case CMD_IP_CONFIGURATION_LOST:
5895                s = "CMD_IP_CONFIGURATION_LOST";
5896                break;
5897            case CMD_IP_CONFIGURATION_SUCCESSFUL:
5898                s = "CMD_IP_CONFIGURATION_SUCCESSFUL";
5899                break;
5900            case CMD_STATIC_IP_SUCCESS:
5901                s = "CMD_STATIC_IP_SUCCESSFUL";
5902                break;
5903            case CMD_STATIC_IP_FAILURE:
5904                s = "CMD_STATIC_IP_FAILURE";
5905                break;
5906            case DhcpStateMachine.DHCP_SUCCESS:
5907                s = "DHCP_SUCCESS";
5908                break;
5909            case DhcpStateMachine.DHCP_FAILURE:
5910                s = "DHCP_FAILURE";
5911                break;
5912            case CMD_TARGET_BSSID:
5913                s = "CMD_TARGET_BSSID";
5914                break;
5915            case CMD_ASSOCIATED_BSSID:
5916                s = "CMD_ASSOCIATED_BSSID";
5917                break;
5918            case CMD_ROAM_WATCHDOG_TIMER:
5919                s = "CMD_ROAM_WATCHDOG_TIMER";
5920                break;
5921            case CMD_SCREEN_STATE_CHANGED:
5922                s = "CMD_SCREEN_STATE_CHANGED";
5923                break;
5924            case CMD_DISCONNECTING_WATCHDOG_TIMER:
5925                s = "CMD_DISCONNECTING_WATCHDOG_TIMER";
5926                break;
5927            default:
5928                s = "what:" + Integer.toString(what);
5929                break;
5930        }
5931        return s;
5932    }
5933
5934    void registerConnected() {
5935       if (mLastNetworkId != WifiConfiguration.INVALID_NETWORK_ID) {
5936           long now_ms = System.currentTimeMillis();
5937           // We are switching away from this configuration,
5938           // hence record the time we were connected last
5939           WifiConfiguration config = mWifiConfigStore.getWifiConfiguration(mLastNetworkId);
5940           if (config != null) {
5941               config.lastConnected = System.currentTimeMillis();
5942               config.autoJoinBailedDueToLowRssi = false;
5943               config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED);
5944               config.numConnectionFailures = 0;
5945               config.numIpConfigFailures = 0;
5946               config.numAuthFailures = 0;
5947               config.numAssociation++;
5948           }
5949           mBadLinkspeedcount = 0;
5950       }
5951    }
5952
5953    void registerDisconnected() {
5954        if (mLastNetworkId != WifiConfiguration.INVALID_NETWORK_ID) {
5955            long now_ms = System.currentTimeMillis();
5956            // We are switching away from this configuration,
5957            // hence record the time we were connected last
5958            WifiConfiguration config = mWifiConfigStore.getWifiConfiguration(mLastNetworkId);
5959            if (config != null) {
5960                config.lastDisconnected = System.currentTimeMillis();
5961            }
5962        }
5963    }
5964
5965    void noteWifiDisabledWhileAssociated() {
5966        // We got disabled by user while we were associated, make note of it
5967        int rssi = mWifiInfo.getRssi();
5968        WifiConfiguration config = getCurrentWifiConfiguration();
5969        if (getCurrentState() == mConnectedState
5970                && rssi != WifiInfo.INVALID_RSSI
5971                && config != null) {
5972            boolean is24GHz = mWifiInfo.is24GHz();
5973            boolean isBadRSSI = (is24GHz && rssi < mWifiConfigStore.thresholdBadRssi24)
5974                    || (!is24GHz && rssi < mWifiConfigStore.thresholdBadRssi5);
5975            boolean isLowRSSI = (is24GHz && rssi < mWifiConfigStore.thresholdLowRssi24)
5976                    || (!is24GHz && mWifiInfo.getRssi() < mWifiConfigStore.thresholdLowRssi5);
5977            boolean isHighRSSI = (is24GHz && rssi >= mWifiConfigStore.thresholdGoodRssi24)
5978                    || (!is24GHz && mWifiInfo.getRssi() >= mWifiConfigStore.thresholdGoodRssi5);
5979            if (isBadRSSI) {
5980                // Take note that we got disabled while RSSI was Bad
5981                config.numUserTriggeredWifiDisableLowRSSI++;
5982            } else if (isLowRSSI) {
5983                // Take note that we got disabled while RSSI was Low
5984                config.numUserTriggeredWifiDisableBadRSSI++;
5985            } else if (!isHighRSSI) {
5986                // Take note that we got disabled while RSSI was Not high
5987                config.numUserTriggeredWifiDisableNotHighRSSI++;
5988            }
5989        }
5990    }
5991
5992    void setInternetAccessState(boolean enabled) {
5993        WifiConfiguration config = getCurrentWifiConfiguration();
5994        if (config != null) {
5995            config.noInternetAccess = enabled;
5996        }
5997    }
5998
5999    WifiConfiguration getCurrentWifiConfiguration() {
6000        if (mLastNetworkId == WifiConfiguration.INVALID_NETWORK_ID) {
6001            return null;
6002        }
6003        return mWifiConfigStore.getWifiConfiguration(mLastNetworkId);
6004    }
6005
6006    ScanResult getCurrentScanResult() {
6007        WifiConfiguration config = getCurrentWifiConfiguration();
6008        if (config == null) {
6009            return null;
6010        }
6011        String BSSID = mWifiInfo.getBSSID();
6012        if (BSSID == null) {
6013            BSSID = mTargetRoamBSSID;
6014        }
6015        if (config.scanResultCache == null) {
6016            return null;
6017        }
6018        return config.scanResultCache.get(BSSID);
6019    }
6020
6021    String getCurrentBSSID() {
6022        if (linkDebouncing) {
6023            return null;
6024        }
6025        return mLastBssid;
6026    }
6027
6028    class ConnectModeState extends State {
6029        @Override
6030        public boolean processMessage(Message message) {
6031            WifiConfiguration config;
6032            int netId;
6033            boolean ok;
6034            boolean didDisconnect;
6035            String bssid;
6036            String ssid;
6037            NetworkUpdateResult result;
6038            logStateAndMessage(message, getClass().getSimpleName());
6039
6040            switch (message.what) {
6041                case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
6042                    didBlackListBSSID = false;
6043                    bssid = (String) message.obj;
6044                    if (bssid == null || TextUtils.isEmpty(bssid)) {
6045                        // If BSSID is null, use the target roam BSSID
6046                        bssid = mTargetRoamBSSID;
6047                    }
6048                    if (bssid != null) {
6049                        // If we have a BSSID, tell configStore to black list it
6050                        synchronized(mScanResultCache) {
6051                            didBlackListBSSID = mWifiConfigStore.handleBSSIDBlackList
6052                                    (mLastNetworkId, bssid, false);
6053                        }
6054                    }
6055                    mSupplicantStateTracker.sendMessage(WifiMonitor.ASSOCIATION_REJECTION_EVENT);
6056                    break;
6057                case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
6058                    mSupplicantStateTracker.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT);
6059                    break;
6060                case WifiMonitor.SSID_TEMP_DISABLED:
6061                case WifiMonitor.SSID_REENABLED:
6062                    String substr = (String) message.obj;
6063                    String en = message.what == WifiMonitor.SSID_TEMP_DISABLED ?
6064                            "temp-disabled" : "re-enabled";
6065                    loge("ConnectModeState SSID state=" + en + " nid="
6066                            + Integer.toString(message.arg1) + " [" + substr + "]");
6067                    synchronized(mScanResultCache) {
6068                        mWifiConfigStore.handleSSIDStateChange(message.arg1, message.what ==
6069                                WifiMonitor.SSID_REENABLED, substr, mWifiInfo.getBSSID());
6070                    }
6071                    break;
6072                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
6073                    SupplicantState state = handleSupplicantStateChange(message);
6074                    // A driver/firmware hang can now put the interface in a down state.
6075                    // We detect the interface going down and recover from it
6076                    if (!SupplicantState.isDriverActive(state)) {
6077                        if (mNetworkInfo.getState() != NetworkInfo.State.DISCONNECTED) {
6078                            handleNetworkDisconnect();
6079                        }
6080                        log("Detected an interface down, restart driver");
6081                        transitionTo(mDriverStoppedState);
6082                        sendMessage(CMD_START_DRIVER);
6083                        break;
6084                    }
6085
6086                    // Supplicant can fail to report a NETWORK_DISCONNECTION_EVENT
6087                    // when authentication times out after a successful connection,
6088                    // we can figure this from the supplicant state. If supplicant
6089                    // state is DISCONNECTED, but the mNetworkInfo says we are not
6090                    // disconnected, we need to handle a disconnection
6091                    if (!linkDebouncing && state == SupplicantState.DISCONNECTED &&
6092                            mNetworkInfo.getState() != NetworkInfo.State.DISCONNECTED) {
6093                        if (DBG) log("Missed CTRL-EVENT-DISCONNECTED, disconnect");
6094                        handleNetworkDisconnect();
6095                        transitionTo(mDisconnectedState);
6096                    }
6097                    break;
6098                case WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST:
6099                    if (message.arg1 == 1) {
6100                        mWifiNative.disconnect();
6101                        mTemporarilyDisconnectWifi = true;
6102                    } else {
6103                        mWifiNative.reconnect();
6104                        mTemporarilyDisconnectWifi = false;
6105                    }
6106                    break;
6107                case CMD_ADD_OR_UPDATE_NETWORK:
6108                    config = (WifiConfiguration) message.obj;
6109                    int res = mWifiConfigStore.addOrUpdateNetwork(config, message.sendingUid);
6110                    if (res < 0) {
6111                        messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
6112                    } else {
6113                        WifiConfiguration curConfig = getCurrentWifiConfiguration();
6114                        if (curConfig != null && config != null) {
6115                            if (curConfig.priority < config.priority
6116                                    && config.status == WifiConfiguration.Status.ENABLED) {
6117                                // Interpret this as a connect attempt
6118                                // Set the last selected configuration so as to allow the system to
6119                                // stick the last user choice without persisting the choice
6120                                mWifiConfigStore.setLastSelectedConfiguration(res);
6121
6122                                // Remember time of last connection attempt
6123                                lastConnectAttempt = System.currentTimeMillis();
6124
6125                                mWifiConnectionStatistics.numWifiManagerJoinAttempt++;
6126
6127                                // As a courtesy to the caller, trigger a scan now
6128                                startScan(ADD_OR_UPDATE_SOURCE, 0, null, null);
6129                            }
6130                        }
6131                    }
6132                    replyToMessage(message, CMD_ADD_OR_UPDATE_NETWORK, res);
6133                    break;
6134                case CMD_REMOVE_NETWORK:
6135                    ok = mWifiConfigStore.removeNetwork(message.arg1);
6136                    if (!ok) {
6137                        messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
6138                    }
6139                    replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
6140                    break;
6141                case CMD_ENABLE_NETWORK:
6142                    boolean others = message.arg2 == 1;
6143                    // Tell autojoin the user did try to select to that network
6144                    // However, do NOT persist the choice by bumping the priority of the network
6145                    if (others) {
6146                        mWifiAutoJoinController.
6147                                updateConfigurationHistory(message.arg1, true, false);
6148                        // Set the last selected configuration so as to allow the system to
6149                        // stick the last user choice without persisting the choice
6150                        mWifiConfigStore.setLastSelectedConfiguration(message.arg1);
6151
6152                        // Remember time of last connection attempt
6153                        lastConnectAttempt = System.currentTimeMillis();
6154
6155                        mWifiConnectionStatistics.numWifiManagerJoinAttempt++;
6156                    }
6157                    // Cancel auto roam requests
6158                    autoRoamSetBSSID(message.arg1, "any");
6159
6160                    ok = mWifiConfigStore.enableNetwork(message.arg1, message.arg2 == 1);
6161                    if (!ok) {
6162                        messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
6163                    }
6164                    replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
6165                    break;
6166                case CMD_ENABLE_ALL_NETWORKS:
6167                    long time = android.os.SystemClock.elapsedRealtime();
6168                    if (time - mLastEnableAllNetworksTime > MIN_INTERVAL_ENABLE_ALL_NETWORKS_MS) {
6169                        mWifiConfigStore.enableAllNetworks();
6170                        mLastEnableAllNetworksTime = time;
6171                    }
6172                    break;
6173                case WifiManager.DISABLE_NETWORK:
6174                    if (mWifiConfigStore.disableNetwork(message.arg1,
6175                            WifiConfiguration.DISABLED_BY_WIFI_MANAGER) == true) {
6176                        replyToMessage(message, WifiManager.DISABLE_NETWORK_SUCCEEDED);
6177                    } else {
6178                        messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
6179                        replyToMessage(message, WifiManager.DISABLE_NETWORK_FAILED,
6180                                WifiManager.ERROR);
6181                    }
6182                    break;
6183                case CMD_BLACKLIST_NETWORK:
6184                    mWifiNative.addToBlacklist((String) message.obj);
6185                    break;
6186                case CMD_CLEAR_BLACKLIST:
6187                    mWifiNative.clearBlacklist();
6188                    break;
6189                case CMD_SAVE_CONFIG:
6190                    ok = mWifiConfigStore.saveConfig();
6191
6192                    if (DBG) loge("wifistatemachine did save config " + ok);
6193                    replyToMessage(message, CMD_SAVE_CONFIG, ok ? SUCCESS : FAILURE);
6194
6195                    // Inform the backup manager about a data change
6196                    IBackupManager ibm = IBackupManager.Stub.asInterface(
6197                            ServiceManager.getService(Context.BACKUP_SERVICE));
6198                    if (ibm != null) {
6199                        try {
6200                            ibm.dataChanged("com.android.providers.settings");
6201                        } catch (Exception e) {
6202                            // Try again later
6203                        }
6204                    }
6205                    break;
6206                case CMD_GET_CONFIGURED_NETWORKS:
6207                    replyToMessage(message, message.what,
6208                            mWifiConfigStore.getConfiguredNetworks());
6209                    break;
6210                case WifiMonitor.SUP_REQUEST_IDENTITY:
6211                    // Supplicant lacks credentials to connect to that network, hence black list
6212                    ssid = (String) message.obj;
6213
6214                    if (targetWificonfiguration != null && ssid != null
6215                            && targetWificonfiguration.SSID != null
6216                            && targetWificonfiguration.SSID.equals("\"" + ssid + "\"")) {
6217                        mWifiConfigStore.handleSSIDStateChange(targetWificonfiguration.networkId,
6218                                false, "AUTH_FAILED no identity", null);
6219                    }
6220                    // Disconnect now, as we don't have any way to fullfill the  supplicant request.
6221                    mWifiConfigStore.setLastSelectedConfiguration
6222                            (WifiConfiguration.INVALID_NETWORK_ID);
6223                    mWifiNative.disconnect();
6224                    break;
6225                case WifiMonitor.SUP_REQUEST_SIM_AUTH:
6226                    logd("Received SUP_REQUEST_SIM_AUTH");
6227                    SimAuthRequestData requestData = (SimAuthRequestData) message.obj;
6228                    if (requestData != null) {
6229                        if (requestData.protocol == WifiEnterpriseConfig.Eap.SIM) {
6230                            handleGsmAuthRequest(requestData);
6231                        } else if (requestData.protocol == WifiEnterpriseConfig.Eap.AKA) {
6232                            handle3GAuthRequest(requestData);
6233                        }
6234                    } else {
6235                        loge("Invalid sim auth request");
6236                    }
6237                    break;
6238                case CMD_GET_PRIVILEGED_CONFIGURED_NETWORKS:
6239                    replyToMessage(message, message.what,
6240                            mWifiConfigStore.getPrivilegedConfiguredNetworks());
6241                    break;
6242                    /* Do a redundant disconnect without transition */
6243                case CMD_DISCONNECT:
6244                    mWifiConfigStore.setLastSelectedConfiguration
6245                            (WifiConfiguration.INVALID_NETWORK_ID);
6246                    mWifiNative.disconnect();
6247                    break;
6248                case CMD_RECONNECT:
6249                    lastConnectAttempt = System.currentTimeMillis();
6250                    mWifiNative.reconnect();
6251                    break;
6252                case CMD_REASSOCIATE:
6253                    lastConnectAttempt = System.currentTimeMillis();
6254                    mWifiNative.reassociate();
6255                    break;
6256                case CMD_RELOAD_TLS_AND_RECONNECT:
6257                    if (mWifiConfigStore.needsUnlockedKeyStore()) {
6258                        logd("Reconnecting to give a chance to un-connected TLS networks");
6259                        mWifiNative.disconnect();
6260                        lastConnectAttempt = System.currentTimeMillis();
6261                        mWifiNative.reconnect();
6262                    }
6263                    break;
6264                case CMD_AUTO_ROAM:
6265                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
6266                    return HANDLED;
6267                case CMD_AUTO_CONNECT:
6268                    /* Work Around: wpa_supplicant can get in a bad state where it returns a non
6269                     * associated status to the STATUS command but somehow-someplace still thinks
6270                     * it is associated and thus will ignore select/reconnect command with
6271                     * following message:
6272                     * "Already associated with the selected network - do nothing"
6273                     *
6274                     * Hence, sends a disconnect to supplicant first.
6275                     */
6276                    didDisconnect = false;
6277                    if (getCurrentState() != mDisconnectedState) {
6278                        /** Supplicant will ignore the reconnect if we are currently associated,
6279                         * hence trigger a disconnect
6280                         */
6281                        didDisconnect = true;
6282                        mWifiNative.disconnect();
6283                    }
6284
6285                    /* connect command coming from auto-join */
6286                    config = (WifiConfiguration) message.obj;
6287                    netId = message.arg1;
6288                    int roam = message.arg2;
6289                    loge("CMD_AUTO_CONNECT sup state "
6290                            + mSupplicantStateTracker.getSupplicantStateName()
6291                            + " my state " + getCurrentState().getName()
6292                            + " nid=" + Integer.toString(netId)
6293                            + " roam=" + Integer.toString(roam));
6294                    if (config == null) {
6295                        loge("AUTO_CONNECT and no config, bail out...");
6296                        break;
6297                    }
6298
6299                    /* Make sure we cancel any previous roam request */
6300                    autoRoamSetBSSID(netId, config.BSSID);
6301
6302                    /* Save the network config */
6303                    loge("CMD_AUTO_CONNECT will save config -> " + config.SSID
6304                            + " nid=" + Integer.toString(netId));
6305                    result = mWifiConfigStore.saveNetwork(config, -1);
6306                    netId = result.getNetworkId();
6307                    loge("CMD_AUTO_CONNECT did save config -> "
6308                            + " nid=" + Integer.toString(netId));
6309
6310                    // Make sure the network is enabled, since supplicant will not reenable it
6311                    mWifiConfigStore.enableNetworkWithoutBroadcast(netId, false);
6312
6313                    if (mWifiConfigStore.selectNetwork(netId) &&
6314                            mWifiNative.reconnect()) {
6315                        lastConnectAttempt = System.currentTimeMillis();
6316                        targetWificonfiguration = mWifiConfigStore.getWifiConfiguration(netId);
6317                        // We selected a better config,
6318                        // maybe because we could not see the last user
6319                        // selection, then forget it. We will remember the selection
6320                        // only if it was persisted.
6321                        mWifiConfigStore.
6322                                setLastSelectedConfiguration(WifiConfiguration.INVALID_NETWORK_ID);
6323
6324                        mAutoRoaming = roam;
6325                        if (isRoaming() || linkDebouncing) {
6326                            transitionTo(mRoamingState);
6327                        } else if (didDisconnect) {
6328                            transitionTo(mDisconnectingState);
6329                        } else {
6330                            transitionTo(mDisconnectedState);
6331                        }
6332                    } else {
6333                        loge("Failed to connect config: " + config + " netId: " + netId);
6334                        replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
6335                                WifiManager.ERROR);
6336                        break;
6337                    }
6338                    break;
6339                case WifiManager.CONNECT_NETWORK:
6340                    /**
6341                     *  The connect message can contain a network id passed as arg1 on message or
6342                     * or a config passed as obj on message.
6343                     * For a new network, a config is passed to create and connect.
6344                     * For an existing network, a network id is passed
6345                     */
6346                    netId = message.arg1;
6347                    config = (WifiConfiguration) message.obj;
6348                    mWifiConnectionStatistics.numWifiManagerJoinAttempt++;
6349                    boolean updatedExisting = false;
6350
6351                    /* Save the network config */
6352                    if (config != null) {
6353                        String configKey = config.configKey(true /* allowCached */);
6354                        WifiConfiguration savedConfig =
6355                                mWifiConfigStore.getWifiConfiguration(configKey);
6356                        if (savedConfig != null) {
6357                            // There is an existing config with this netId, but it wasn't exposed
6358                            // (either AUTO_JOIN_DELETED or ephemeral; see WifiConfigStore#
6359                            // getConfiguredNetworks). Remove those bits and update the config.
6360                            config = savedConfig;
6361                            loge("CONNECT_NETWORK updating existing config with id=" +
6362                                    config.networkId + " configKey=" + configKey);
6363                            config.ephemeral = false;
6364                            config.autoJoinStatus = WifiConfiguration.AUTO_JOIN_ENABLED;
6365                            updatedExisting = true;
6366                        }
6367
6368                        result = mWifiConfigStore.saveNetwork(config, message.sendingUid);
6369                        netId = result.getNetworkId();
6370                    }
6371                    config = mWifiConfigStore.getWifiConfiguration(netId);
6372
6373                    if (config == null) {
6374                        loge("CONNECT_NETWORK id=" + Integer.toString(netId) + " "
6375                                + mSupplicantStateTracker.getSupplicantStateName() + " my state "
6376                                + getCurrentState().getName());
6377                    } else {
6378                        String wasSkipped = config.autoJoinBailedDueToLowRssi ? " skipped" : "";
6379                        loge("CONNECT_NETWORK id=" + Integer.toString(netId)
6380                                + " config=" + config.SSID
6381                                + " cnid=" + config.networkId
6382                                + " supstate=" + mSupplicantStateTracker.getSupplicantStateName()
6383                                + " my state " + getCurrentState().getName()
6384                                + " uid = " + message.sendingUid
6385                                + wasSkipped);
6386                    }
6387
6388                    autoRoamSetBSSID(netId, "any");
6389
6390                    if (message.sendingUid == Process.WIFI_UID
6391                        || message.sendingUid == Process.SYSTEM_UID) {
6392                        // As a sanity measure, clear the BSSID in the supplicant network block.
6393                        // If system or Wifi Settings want to connect, they will not
6394                        // specify the BSSID.
6395                        // If an app however had added a BSSID to this configuration, and the BSSID
6396                        // was wrong, Then we would forever fail to connect until that BSSID
6397                        // is cleaned up.
6398                        clearConfigBSSID(config, "CONNECT_NETWORK");
6399                    }
6400
6401                    mAutoRoaming = WifiAutoJoinController.AUTO_JOIN_IDLE;
6402
6403                    /* Tell autojoin the user did try to connect to that network */
6404                    mWifiAutoJoinController.updateConfigurationHistory(netId, true, true);
6405
6406                    mWifiConfigStore.setLastSelectedConfiguration(netId);
6407
6408                    didDisconnect = false;
6409                    if (mLastNetworkId != WifiConfiguration.INVALID_NETWORK_ID
6410                            && mLastNetworkId != netId) {
6411                        /** Supplicant will ignore the reconnect if we are currently associated,
6412                         * hence trigger a disconnect
6413                         */
6414                        didDisconnect = true;
6415                        mWifiNative.disconnect();
6416                    }
6417
6418                    // Make sure the network is enabled, since supplicant will not reenable it
6419                    mWifiConfigStore.enableNetworkWithoutBroadcast(netId, false);
6420
6421                    if (mWifiConfigStore.selectNetwork(netId) &&
6422                            mWifiNative.reconnect()) {
6423                        lastConnectAttempt = System.currentTimeMillis();
6424                        targetWificonfiguration = mWifiConfigStore.getWifiConfiguration(netId);
6425
6426                        /* The state tracker handles enabling networks upon completion/failure */
6427                        mSupplicantStateTracker.sendMessage(WifiManager.CONNECT_NETWORK);
6428                        replyToMessage(message, WifiManager.CONNECT_NETWORK_SUCCEEDED);
6429                        if (didDisconnect) {
6430                            /* Expect a disconnection from the old connection */
6431                            transitionTo(mDisconnectingState);
6432                        } else if (updatedExisting && getCurrentState() == mConnectedState &&
6433                                getCurrentWifiConfiguration().networkId == netId) {
6434                            // Update the current set of network capabilities, but stay in the
6435                            // current state.
6436                            updateCapabilities(config);
6437                        } else {
6438                            /**
6439                             *  Directly go to disconnected state where we
6440                             * process the connection events from supplicant
6441                             **/
6442                            transitionTo(mDisconnectedState);
6443                        }
6444                    } else {
6445                        loge("Failed to connect config: " + config + " netId: " + netId);
6446                        replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
6447                                WifiManager.ERROR);
6448                        break;
6449                    }
6450                    break;
6451                case WifiManager.SAVE_NETWORK:
6452                    mWifiConnectionStatistics.numWifiManagerJoinAttempt++;
6453                    // Fall thru
6454                case WifiStateMachine.CMD_AUTO_SAVE_NETWORK:
6455                    lastSavedConfigurationAttempt = null; // Used for debug
6456                    config = (WifiConfiguration) message.obj;
6457                    if (config == null) {
6458                        loge("ERROR: SAVE_NETWORK with null configuration"
6459                                + mSupplicantStateTracker.getSupplicantStateName()
6460                                + " my state " + getCurrentState().getName());
6461                        messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
6462                        replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED,
6463                                WifiManager.ERROR);
6464                        break;
6465                    }
6466                    lastSavedConfigurationAttempt = new WifiConfiguration(config);
6467                    int nid = config.networkId;
6468                    loge("SAVE_NETWORK id=" + Integer.toString(nid)
6469                                + " config=" + config.SSID
6470                                + " nid=" + config.networkId
6471                                + " supstate=" + mSupplicantStateTracker.getSupplicantStateName()
6472                                + " my state " + getCurrentState().getName());
6473
6474                    result = mWifiConfigStore.saveNetwork(config, -1);
6475                    if (result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID) {
6476                        if (mWifiInfo.getNetworkId() == result.getNetworkId()) {
6477                            if (result.hasIpChanged()) {
6478                                // The currently connection configuration was changed
6479                                // We switched from DHCP to static or from static to DHCP, or the
6480                                // static IP address has changed.
6481                                log("Reconfiguring IP on connection");
6482                                // TODO: clear addresses and disable IPv6
6483                                // to simplify obtainingIpState.
6484                                transitionTo(mObtainingIpState);
6485                            }
6486                            if (result.hasProxyChanged()) {
6487                                log("Reconfiguring proxy on connection");
6488                                updateLinkProperties(CMD_UPDATE_LINKPROPERTIES);
6489                            }
6490                        }
6491                        replyToMessage(message, WifiManager.SAVE_NETWORK_SUCCEEDED);
6492                        if (VDBG) {
6493                           loge("Success save network nid="
6494                                        + Integer.toString(result.getNetworkId()));
6495                        }
6496
6497                        synchronized(mScanResultCache) {
6498                            /**
6499                             * If the command comes from WifiManager, then
6500                             * tell autojoin the user did try to modify and save that network,
6501                             * and interpret the SAVE_NETWORK as a request to connect
6502                             */
6503                            boolean user = message.what == WifiManager.SAVE_NETWORK;
6504                            mWifiAutoJoinController.updateConfigurationHistory(result.getNetworkId()
6505                                    , user, true);
6506                            mWifiAutoJoinController.attemptAutoJoin();
6507                        }
6508                    } else {
6509                        loge("Failed to save network");
6510                        messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
6511                        replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED,
6512                                WifiManager.ERROR);
6513                    }
6514                    break;
6515                case WifiManager.FORGET_NETWORK:
6516                    // Debug only, remember last configuration that was forgotten
6517                    WifiConfiguration toRemove
6518                            = mWifiConfigStore.getWifiConfiguration(message.arg1);
6519                    if (toRemove == null) {
6520                        lastForgetConfigurationAttempt = null;
6521                    } else {
6522                        lastForgetConfigurationAttempt = new WifiConfiguration(toRemove);
6523                    }
6524                    if (mWifiConfigStore.forgetNetwork(message.arg1)) {
6525                        replyToMessage(message, WifiManager.FORGET_NETWORK_SUCCEEDED);
6526                    } else {
6527                        loge("Failed to forget network");
6528                        replyToMessage(message, WifiManager.FORGET_NETWORK_FAILED,
6529                                WifiManager.ERROR);
6530                    }
6531                    break;
6532                case WifiManager.START_WPS:
6533                    WpsInfo wpsInfo = (WpsInfo) message.obj;
6534                    WpsResult wpsResult;
6535                    switch (wpsInfo.setup) {
6536                        case WpsInfo.PBC:
6537                            wpsResult = mWifiConfigStore.startWpsPbc(wpsInfo);
6538                            break;
6539                        case WpsInfo.KEYPAD:
6540                            wpsResult = mWifiConfigStore.startWpsWithPinFromAccessPoint(wpsInfo);
6541                            break;
6542                        case WpsInfo.DISPLAY:
6543                            wpsResult = mWifiConfigStore.startWpsWithPinFromDevice(wpsInfo);
6544                            break;
6545                        default:
6546                            wpsResult = new WpsResult(Status.FAILURE);
6547                            loge("Invalid setup for WPS");
6548                            break;
6549                    }
6550                    mWifiConfigStore.setLastSelectedConfiguration
6551                            (WifiConfiguration.INVALID_NETWORK_ID);
6552                    if (wpsResult.status == Status.SUCCESS) {
6553                        replyToMessage(message, WifiManager.START_WPS_SUCCEEDED, wpsResult);
6554                        transitionTo(mWpsRunningState);
6555                    } else {
6556                        loge("Failed to start WPS with config " + wpsInfo.toString());
6557                        replyToMessage(message, WifiManager.WPS_FAILED, WifiManager.ERROR);
6558                    }
6559                    break;
6560                case WifiMonitor.NETWORK_CONNECTION_EVENT:
6561                    if (DBG) log("Network connection established");
6562                    mLastNetworkId = message.arg1;
6563                    mLastBssid = (String) message.obj;
6564
6565                    mWifiInfo.setBSSID(mLastBssid);
6566                    mWifiInfo.setNetworkId(mLastNetworkId);
6567                    // Send event to CM & network change broadcast
6568                    setNetworkDetailedState(DetailedState.OBTAINING_IPADDR);
6569                    sendNetworkStateChangeBroadcast(mLastBssid);
6570                    transitionTo(mObtainingIpState);
6571                    break;
6572                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
6573                    // Calling handleNetworkDisconnect here is redundant because we might already
6574                    // have called it when leaving L2ConnectedState to go to disconnecting state
6575                    // or thru other path
6576                    // We should normally check the mWifiInfo or mLastNetworkId so as to check
6577                    // if they are valid, and only in this case call handleNEtworkDisconnect,
6578                    // TODO: this should be fixed for a L MR release
6579                    // The side effect of calling handleNetworkDisconnect twice is that a bunch of
6580                    // idempotent commands are executed twice (stopping Dhcp, enabling the SPS mode
6581                    // at the chip etc...
6582                    if (DBG) log("ConnectModeState: Network connection lost ");
6583                    handleNetworkDisconnect();
6584                    transitionTo(mDisconnectedState);
6585                    break;
6586                default:
6587                    return NOT_HANDLED;
6588            }
6589            return HANDLED;
6590        }
6591    }
6592
6593    private void updateCapabilities(WifiConfiguration config) {
6594        if (config.ephemeral) {
6595            mNetworkCapabilities.removeCapability(
6596                    NetworkCapabilities.NET_CAPABILITY_TRUSTED);
6597        } else {
6598            mNetworkCapabilities.addCapability(
6599                    NetworkCapabilities.NET_CAPABILITY_TRUSTED);
6600        }
6601        mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
6602    }
6603
6604    private class WifiNetworkAgent extends NetworkAgent {
6605        public WifiNetworkAgent(Looper l, Context c, String TAG, NetworkInfo ni,
6606                NetworkCapabilities nc, LinkProperties lp, int score) {
6607            super(l, c, TAG, ni, nc, lp, score);
6608        }
6609        protected void unwanted() {
6610            // Ignore if we're not the current networkAgent.
6611            if (this != mNetworkAgent) return;
6612            if (DBG) log("WifiNetworkAgent -> Wifi unwanted score "
6613                    + Integer.toString(mWifiInfo.score));
6614            unwantedNetwork(network_status_unwanted_disconnect);
6615        }
6616
6617        protected void networkStatus(int status) {
6618            if (status == NetworkAgent.INVALID_NETWORK) {
6619                if (DBG) log("WifiNetworkAgent -> Wifi networkStatus invalid score "
6620                        + Integer.toString(mWifiInfo.score));
6621                unwantedNetwork(network_status_unwanted_disable_autojoin);
6622            }
6623        }
6624    }
6625
6626    void unwantedNetwork(int reason) {
6627        sendMessage(CMD_UNWANTED_NETWORK, reason);
6628    }
6629
6630
6631    boolean startScanForConfiguration(WifiConfiguration config, boolean restrictChannelList) {
6632        if (config == null)
6633            return false;
6634
6635        // We are still seeing a fairly high power consumption triggered by autojoin scans
6636        // Hence do partial scans only for PSK configuration that are roamable since the
6637        // primary purpose of the partial scans is roaming.
6638        // Full badn scans with exponential backoff for the purpose or extended roaming and
6639        // network switching are performed unconditionally.
6640        if (config.scanResultCache == null
6641                || !config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)
6642                || config.scanResultCache.size() > 6) {
6643            //return true but to not trigger the scan
6644            return true;
6645        }
6646        HashSet<Integer> channels
6647                = mWifiConfigStore.makeChannelList(config,
6648                ONE_HOUR_MILLI, restrictChannelList);
6649        if (channels != null && channels.size() != 0) {
6650            StringBuilder freqs = new StringBuilder();
6651            boolean first = true;
6652            for (Integer channel : channels) {
6653                if (!first)
6654                    freqs.append(",");
6655                freqs.append(channel.toString());
6656                first = false;
6657            }
6658            //if (DBG) {
6659            loge("WifiStateMachine starting scan for " + config.configKey() + " with " + freqs);
6660            //}
6661            // Call wifi native to start the scan
6662            if (startScanNative(
6663                    WifiNative.SCAN_WITHOUT_CONNECTION_SETUP,
6664                    freqs.toString())) {
6665                // Only count battery consumption if scan request is accepted
6666                noteScanStart(SCAN_ALARM_SOURCE, null);
6667                messageHandlingStatus = MESSAGE_HANDLING_STATUS_OK;
6668            } else {
6669                // used for debug only, mark scan as failed
6670                messageHandlingStatus = MESSAGE_HANDLING_STATUS_HANDLING_ERROR;
6671            }
6672            return true;
6673        } else {
6674            if (DBG) loge("WifiStateMachine no channels for " + config.configKey());
6675            return false;
6676        }
6677    }
6678
6679    void clearCurrentConfigBSSID(String dbg) {
6680        // Clear the bssid in the current config's network block
6681        WifiConfiguration config = getCurrentWifiConfiguration();
6682        if (config == null)
6683            return;
6684        clearConfigBSSID(config, dbg);
6685    }
6686    void clearConfigBSSID(WifiConfiguration config, String dbg) {
6687        if (config == null)
6688            return;
6689        if (DBG) {
6690            loge(dbg + " " + mTargetRoamBSSID + " config " + config.configKey()
6691                    + " config.bssid " + config.BSSID);
6692        }
6693        config.autoJoinBSSID = "any";
6694        config.BSSID = "any";
6695        if (DBG) {
6696           loge(dbg + " " + config.SSID
6697                    + " nid=" + Integer.toString(config.networkId));
6698        }
6699        mWifiConfigStore.saveWifiConfigBSSID(config);
6700    }
6701
6702    class L2ConnectedState extends State {
6703        @Override
6704        public void enter() {
6705            mRssiPollToken++;
6706            if (mEnableRssiPolling) {
6707                sendMessage(CMD_RSSI_POLL, mRssiPollToken, 0);
6708            }
6709            if (mNetworkAgent != null) {
6710                loge("Have NetworkAgent when entering L2Connected");
6711                setNetworkDetailedState(DetailedState.DISCONNECTED);
6712            }
6713            setNetworkDetailedState(DetailedState.CONNECTING);
6714
6715            if (TextUtils.isEmpty(mTcpBufferSizes) == false) {
6716                mLinkProperties.setTcpBufferSizes(mTcpBufferSizes);
6717            }
6718            mNetworkAgent = new WifiNetworkAgent(getHandler().getLooper(), mContext,
6719                    "WifiNetworkAgent", mNetworkInfo, mNetworkCapabilitiesFilter,
6720                    mLinkProperties, 60);
6721
6722            // We must clear the config BSSID, as the wifi chipset may decide to roam
6723            // from this point on and having the BSSID specified in the network block would
6724            // cause the roam to faile and the device to disconnect
6725            clearCurrentConfigBSSID("L2ConnectedState");
6726        }
6727
6728        @Override
6729        public void exit() {
6730            // This is handled by receiving a NETWORK_DISCONNECTION_EVENT in ConnectModeState
6731            // Bug: 15347363
6732            // For paranoia's sake, call handleNetworkDisconnect
6733            // only if BSSID is null or last networkId
6734            // is not invalid.
6735            if (DBG) {
6736                StringBuilder sb = new StringBuilder();
6737                sb.append("leaving L2ConnectedState state nid=" + Integer.toString(mLastNetworkId));
6738                if (mLastBssid !=null) {
6739                    sb.append(" ").append(mLastBssid);
6740                }
6741            }
6742            if (mLastBssid != null || mLastNetworkId != WifiConfiguration.INVALID_NETWORK_ID) {
6743                handleNetworkDisconnect();
6744            }
6745        }
6746
6747        @Override
6748        public boolean processMessage(Message message) {
6749            logStateAndMessage(message, getClass().getSimpleName());
6750
6751            switch (message.what) {
6752              case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
6753                  handlePreDhcpSetup();
6754                  break;
6755              case DhcpStateMachine.CMD_POST_DHCP_ACTION:
6756                  handlePostDhcpSetup();
6757                  if (message.arg1 == DhcpStateMachine.DHCP_SUCCESS) {
6758                      if (DBG) log("WifiStateMachine DHCP successful");
6759                      handleIPv4Success((DhcpResults) message.obj, DhcpStateMachine.DHCP_SUCCESS);
6760                      // We advance to mVerifyingLinkState because handleIPv4Success will call
6761                      // updateLinkProperties, which then sends CMD_IP_CONFIGURATION_SUCCESSFUL.
6762                  } else if (message.arg1 == DhcpStateMachine.DHCP_FAILURE) {
6763                      if (DBG) {
6764                          int count = -1;
6765                          WifiConfiguration config = getCurrentWifiConfiguration();
6766                          if (config != null) {
6767                              count = config.numConnectionFailures;
6768                          }
6769                          log("WifiStateMachine DHCP failure count=" + count);
6770                      }
6771                      handleIPv4Failure(DhcpStateMachine.DHCP_FAILURE);
6772                      // As above, we transition to mDisconnectingState via updateLinkProperties.
6773                  }
6774                  break;
6775                case CMD_IP_CONFIGURATION_SUCCESSFUL:
6776                    handleSuccessfulIpConfiguration();
6777                    sendConnectedState();
6778                    transitionTo(mConnectedState);
6779                    break;
6780                case CMD_IP_CONFIGURATION_LOST:
6781                    // Get Link layer stats so as we get fresh tx packet counters
6782                    getWifiLinkLayerStats(true);
6783                    handleIpConfigurationLost();
6784                    transitionTo(mDisconnectingState);
6785                    break;
6786                case CMD_DISCONNECT:
6787                    mWifiNative.disconnect();
6788                    transitionTo(mDisconnectingState);
6789                    break;
6790                case WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST:
6791                    if (message.arg1 == 1) {
6792                        mWifiNative.disconnect();
6793                        mTemporarilyDisconnectWifi = true;
6794                        transitionTo(mDisconnectingState);
6795                    }
6796                    break;
6797                case CMD_SET_OPERATIONAL_MODE:
6798                    if (message.arg1 != CONNECT_MODE) {
6799                        sendMessage(CMD_DISCONNECT);
6800                        deferMessage(message);
6801                        if (message.arg1 == SCAN_ONLY_WITH_WIFI_OFF_MODE) {
6802                            noteWifiDisabledWhileAssociated();
6803                        }
6804                    }
6805                    break;
6806                case CMD_SET_COUNTRY_CODE:
6807                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_DEFERRED;
6808                    deferMessage(message);
6809                    break;
6810                case CMD_START_SCAN:
6811                    //if (DBG) {
6812                        loge("WifiStateMachine CMD_START_SCAN source " + message.arg1
6813                              + " txSuccessRate="+String.format( "%.2f", mWifiInfo.txSuccessRate)
6814                              + " rxSuccessRate="+String.format( "%.2f", mWifiInfo.rxSuccessRate)
6815                              + " targetRoamBSSID=" + mTargetRoamBSSID
6816                              + " RSSI=" + mWifiInfo.getRssi());
6817                    //}
6818                    if (message.arg1 == SCAN_ALARM_SOURCE) {
6819                        boolean tryFullBandScan = false;
6820                        boolean restrictChannelList = false;
6821                        long now_ms = System.currentTimeMillis();
6822                        if (DBG) {
6823                            loge("WifiStateMachine CMD_START_SCAN with age="
6824                                    + Long.toString(now_ms - lastFullBandConnectedTimeMilli)
6825                                    + " interval=" + fullBandConnectedTimeIntervalMilli
6826                                    + " maxinterval=" + maxFullBandConnectedTimeIntervalMilli);
6827                        }
6828                        if (mWifiInfo != null) {
6829                            if (mWifiConfigStore.enableFullBandScanWhenAssociated &&
6830                                    (now_ms - lastFullBandConnectedTimeMilli)
6831                                    > fullBandConnectedTimeIntervalMilli) {
6832                                if (DBG) {
6833                                    loge("WifiStateMachine CMD_START_SCAN try full band scan age="
6834                                         + Long.toString(now_ms - lastFullBandConnectedTimeMilli)
6835                                         + " interval=" + fullBandConnectedTimeIntervalMilli
6836                                         + " maxinterval=" + maxFullBandConnectedTimeIntervalMilli);
6837                                }
6838                                tryFullBandScan = true;
6839                            }
6840
6841                            if (mWifiInfo.txSuccessRate >
6842                                    mWifiConfigStore.maxTxPacketForFullScans
6843                                    || mWifiInfo.rxSuccessRate >
6844                                    mWifiConfigStore.maxRxPacketForFullScans) {
6845                                // Too much traffic at the interface, hence no full band scan
6846                                if (DBG) {
6847                                    loge("WifiStateMachine CMD_START_SCAN " +
6848                                            "prevent full band scan due to pkt rate");
6849                                }
6850                                tryFullBandScan = false;
6851                            }
6852
6853                            if (mWifiInfo.txSuccessRate >
6854                                    mWifiConfigStore.maxTxPacketForPartialScans
6855                                    || mWifiInfo.rxSuccessRate >
6856                                    mWifiConfigStore.maxRxPacketForPartialScans) {
6857                                // Don't scan if lots of packets are being sent
6858                                restrictChannelList = true;
6859                                if (mWifiConfigStore.alwaysEnableScansWhileAssociated == 0) {
6860                                    if (DBG) {
6861                                     loge("WifiStateMachine CMD_START_SCAN source " + message.arg1
6862                                        + " ...and ignore scans"
6863                                        + " tx=" + String.format("%.2f", mWifiInfo.txSuccessRate)
6864                                        + " rx=" + String.format("%.2f", mWifiInfo.rxSuccessRate));
6865                                    }
6866                                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_REFUSED;
6867                                    return HANDLED;
6868                                }
6869                            }
6870                        }
6871
6872                        WifiConfiguration currentConfiguration = getCurrentWifiConfiguration();
6873                        if (DBG) {
6874                            loge("WifiStateMachine CMD_START_SCAN full=" +
6875                                    tryFullBandScan);
6876                        }
6877                        if (currentConfiguration != null) {
6878                            if (fullBandConnectedTimeIntervalMilli
6879                                    < mWifiConfigStore.associatedPartialScanPeriodMilli) {
6880                                // Sanity
6881                                fullBandConnectedTimeIntervalMilli
6882                                        = mWifiConfigStore.associatedPartialScanPeriodMilli;
6883                            }
6884                            if (tryFullBandScan) {
6885                                lastFullBandConnectedTimeMilli = now_ms;
6886                                if (fullBandConnectedTimeIntervalMilli
6887                                        < mWifiConfigStore.associatedFullScanMaxIntervalMilli) {
6888                                    // Increase the interval
6889                                    fullBandConnectedTimeIntervalMilli
6890                                            = fullBandConnectedTimeIntervalMilli
6891                                            * mWifiConfigStore.associatedFullScanBackoff / 8;
6892
6893                                    if (DBG) {
6894                                        loge("WifiStateMachine CMD_START_SCAN bump interval ="
6895                                        + fullBandConnectedTimeIntervalMilli);
6896                                    }
6897                                }
6898                                handleScanRequest(
6899                                        WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, message);
6900                            } else {
6901                                if (!startScanForConfiguration(
6902                                        currentConfiguration, restrictChannelList)) {
6903                                    if (DBG) {
6904                                        loge("WifiStateMachine starting scan, " +
6905                                                " did not find channels -> full");
6906                                    }
6907                                    lastFullBandConnectedTimeMilli = now_ms;
6908                                    if (fullBandConnectedTimeIntervalMilli
6909                                            < mWifiConfigStore.associatedFullScanMaxIntervalMilli) {
6910                                        // Increase the interval
6911                                        fullBandConnectedTimeIntervalMilli
6912                                                = fullBandConnectedTimeIntervalMilli
6913                                                * mWifiConfigStore.associatedFullScanBackoff / 8;
6914
6915                                        if (DBG) {
6916                                            loge("WifiStateMachine CMD_START_SCAN bump interval ="
6917                                                    + fullBandConnectedTimeIntervalMilli);
6918                                        }
6919                                    }
6920                                    handleScanRequest(
6921                                                WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, message);
6922                                }
6923                            }
6924                        } else {
6925                            loge("CMD_START_SCAN : connected mode and no configuration");
6926                            messageHandlingStatus = MESSAGE_HANDLING_STATUS_HANDLING_ERROR;
6927                        }
6928                    } else {
6929                        // Not scan alarm source
6930                        handleScanRequest(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, message);
6931                    }
6932                    break;
6933                    /* Ignore connection to same network */
6934                case WifiManager.CONNECT_NETWORK:
6935                    int netId = message.arg1;
6936                    if (mWifiInfo.getNetworkId() == netId) {
6937                        break;
6938                    }
6939                    return NOT_HANDLED;
6940                    /* Ignore */
6941                case WifiMonitor.NETWORK_CONNECTION_EVENT:
6942                    break;
6943                case CMD_RSSI_POLL:
6944                    if (message.arg1 == mRssiPollToken) {
6945                        if (mWifiConfigStore.enableChipWakeUpWhenAssociated) {
6946                            if (VVDBG) log(" get link layer stats " + mWifiLinkLayerStatsSupported);
6947                            WifiLinkLayerStats stats = getWifiLinkLayerStats(VDBG);
6948                            if (stats != null) {
6949                                // Sanity check the results provided by driver
6950                                if (mWifiInfo.getRssi() != WifiInfo.INVALID_RSSI
6951                                        && (stats.rssi_mgmt == 0
6952                                        || stats.beacon_rx == 0)) {
6953                                    stats = null;
6954                                }
6955                            }
6956                            // Get Info and continue polling
6957                            fetchRssiLinkSpeedAndFrequencyNative();
6958                            calculateWifiScore(stats);
6959                        }
6960                        sendMessageDelayed(obtainMessage(CMD_RSSI_POLL,
6961                                mRssiPollToken, 0), POLL_RSSI_INTERVAL_MSECS);
6962
6963                        if (DBG) sendRssiChangeBroadcast(mWifiInfo.getRssi());
6964                    } else {
6965                        // Polling has completed
6966                    }
6967                    break;
6968                case CMD_ENABLE_RSSI_POLL:
6969                    if (mWifiConfigStore.enableRssiPollWhenAssociated) {
6970                        mEnableRssiPolling = (message.arg1 == 1);
6971                    } else {
6972                        mEnableRssiPolling = false;
6973                    }
6974                    mRssiPollToken++;
6975                    if (mEnableRssiPolling) {
6976                        // First poll
6977                        fetchRssiLinkSpeedAndFrequencyNative();
6978                        sendMessageDelayed(obtainMessage(CMD_RSSI_POLL,
6979                                mRssiPollToken, 0), POLL_RSSI_INTERVAL_MSECS);
6980                    } else {
6981                        cleanWifiScore();
6982                    }
6983                    break;
6984                case WifiManager.RSSI_PKTCNT_FETCH:
6985                    RssiPacketCountInfo info = new RssiPacketCountInfo();
6986                    fetchRssiLinkSpeedAndFrequencyNative();
6987                    info.rssi = mWifiInfo.getRssi();
6988                    fetchPktcntNative(info);
6989                    replyToMessage(message, WifiManager.RSSI_PKTCNT_FETCH_SUCCEEDED, info);
6990                    break;
6991                case CMD_DELAYED_NETWORK_DISCONNECT:
6992                    if (!linkDebouncing && mWifiConfigStore.enableLinkDebouncing) {
6993
6994                        // Ignore if we are not debouncing
6995                        loge("CMD_DELAYED_NETWORK_DISCONNECT and not debouncing - ignore "
6996                                + message.arg1);
6997                        return HANDLED;
6998                    } else {
6999                        loge("CMD_DELAYED_NETWORK_DISCONNECT and debouncing - disconnect "
7000                                + message.arg1);
7001
7002                        linkDebouncing = false;
7003                        // If we are still debouncing while this message comes,
7004                        // it means we were not able to reconnect within the alloted time
7005                        // = LINK_FLAPPING_DEBOUNCE_MSEC
7006                        // and thus, trigger a real disconnect
7007                        handleNetworkDisconnect();
7008                        transitionTo(mDisconnectedState);
7009                    }
7010                    break;
7011                case CMD_ASSOCIATED_BSSID:
7012                    if ((String) message.obj == null) {
7013                        loge("Associated command w/o BSSID");
7014                        break;
7015                    }
7016                    mLastBssid = (String) message.obj;
7017                    mWifiInfo.setBSSID((String) message.obj);
7018                    break;
7019                default:
7020                    return NOT_HANDLED;
7021            }
7022
7023            return HANDLED;
7024        }
7025    }
7026
7027    class ObtainingIpState extends State {
7028        @Override
7029        public void enter() {
7030            if (DBG) {
7031                String key = "";
7032                if (getCurrentWifiConfiguration() != null) {
7033                    key = getCurrentWifiConfiguration().configKey();
7034                }
7035                log("enter ObtainingIpState netId=" + Integer.toString(mLastNetworkId)
7036                        + " " + key + " "
7037                        + " roam=" + mAutoRoaming
7038                        + " static=" + mWifiConfigStore.isUsingStaticIp(mLastNetworkId)
7039                        + " watchdog= " + obtainingIpWatchdogCount);
7040            }
7041
7042            // Reset link Debouncing, indicating we have successfully re-connected to the AP
7043            // We might still be roaming
7044            linkDebouncing = false;
7045
7046            // We must clear the config BSSID, as the wifi chipset may decide to roam
7047            // from this point on and having the BSSID specified in the network block would
7048            // cause the roam to faile and the device to disconnect
7049            clearCurrentConfigBSSID("ObtainingIpAddress");
7050
7051            try {
7052                mNwService.enableIpv6(mInterfaceName);
7053            } catch (RemoteException re) {
7054                loge("Failed to enable IPv6: " + re);
7055            } catch (IllegalStateException e) {
7056                loge("Failed to enable IPv6: " + e);
7057            }
7058
7059            if (!mWifiConfigStore.isUsingStaticIp(mLastNetworkId)) {
7060                if (isRoaming()) {
7061                    renewDhcp();
7062                } else {
7063                    // Remove any IP address on the interface in case we're switching from static
7064                    // IP configuration to DHCP. This is safe because if we get here when not
7065                    // roaming, we don't have a usable address.
7066                    clearIPv4Address(mInterfaceName);
7067                    startDhcp();
7068                }
7069                obtainingIpWatchdogCount++;
7070                loge("Start Dhcp Watchdog " + obtainingIpWatchdogCount);
7071                sendMessageDelayed(obtainMessage(CMD_OBTAINING_IP_ADDRESS_WATCHDOG_TIMER,
7072                        obtainingIpWatchdogCount, 0), OBTAINING_IP_ADDRESS_GUARD_TIMER_MSEC);
7073            } else {
7074                // stop any running dhcp before assigning static IP
7075                stopDhcp();
7076                StaticIpConfiguration config = mWifiConfigStore.getStaticIpConfiguration(
7077                        mLastNetworkId);
7078                if (config.ipAddress == null) {
7079                    loge("Static IP lacks address");
7080                    sendMessage(CMD_STATIC_IP_FAILURE);
7081                } else {
7082                    InterfaceConfiguration ifcg = new InterfaceConfiguration();
7083                    ifcg.setLinkAddress(config.ipAddress);
7084                    ifcg.setInterfaceUp();
7085                    try {
7086                        mNwService.setInterfaceConfig(mInterfaceName, ifcg);
7087                        if (DBG) log("Static IP configuration succeeded");
7088                        DhcpResults dhcpResults = new DhcpResults(config);
7089                        sendMessage(CMD_STATIC_IP_SUCCESS, dhcpResults);
7090                    } catch (RemoteException re) {
7091                        loge("Static IP configuration failed: " + re);
7092                        sendMessage(CMD_STATIC_IP_FAILURE);
7093                    } catch (IllegalStateException e) {
7094                        loge("Static IP configuration failed: " + e);
7095                        sendMessage(CMD_STATIC_IP_FAILURE);
7096                    }
7097                }
7098            }
7099        }
7100      @Override
7101      public boolean processMessage(Message message) {
7102          logStateAndMessage(message, getClass().getSimpleName());
7103
7104          switch(message.what) {
7105              case CMD_STATIC_IP_SUCCESS:
7106                  handleIPv4Success((DhcpResults) message.obj, CMD_STATIC_IP_SUCCESS);
7107                  break;
7108              case CMD_STATIC_IP_FAILURE:
7109                  handleIPv4Failure(CMD_STATIC_IP_FAILURE);
7110                  break;
7111              case CMD_AUTO_CONNECT:
7112              case CMD_AUTO_ROAM:
7113                  messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
7114                  break;
7115              case WifiManager.SAVE_NETWORK:
7116              case WifiStateMachine.CMD_AUTO_SAVE_NETWORK:
7117                  messageHandlingStatus = MESSAGE_HANDLING_STATUS_DEFERRED;
7118                  deferMessage(message);
7119                  break;
7120                  /* Defer any power mode changes since we must keep active power mode at DHCP */
7121              case CMD_SET_HIGH_PERF_MODE:
7122                  messageHandlingStatus = MESSAGE_HANDLING_STATUS_DEFERRED;
7123                  deferMessage(message);
7124                  break;
7125                  /* Defer scan request since we should not switch to other channels at DHCP */
7126              case CMD_START_SCAN:
7127                  messageHandlingStatus = MESSAGE_HANDLING_STATUS_DEFERRED;
7128                  deferMessage(message);
7129                  break;
7130              case CMD_OBTAINING_IP_ADDRESS_WATCHDOG_TIMER:
7131                  if (message.arg1 == obtainingIpWatchdogCount) {
7132                      loge("ObtainingIpAddress: Watchdog Triggered, count="
7133                              + obtainingIpWatchdogCount);
7134                      handleIpConfigurationLost();
7135                      transitionTo(mDisconnectingState);
7136                      break;
7137                  }
7138                  messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
7139                  break;
7140              default:
7141                  return NOT_HANDLED;
7142          }
7143          return HANDLED;
7144      }
7145    }
7146
7147    class VerifyingLinkState extends State {
7148        @Override
7149        public void enter() {
7150            log(getName() + " enter");
7151            setNetworkDetailedState(DetailedState.VERIFYING_POOR_LINK);
7152            mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.VERIFYING_POOR_LINK);
7153            sendNetworkStateChangeBroadcast(mLastBssid);
7154            // End roaming
7155            mAutoRoaming = WifiAutoJoinController.AUTO_JOIN_IDLE;
7156        }
7157        @Override
7158        public boolean processMessage(Message message) {
7159            logStateAndMessage(message, getClass().getSimpleName());
7160
7161            switch (message.what) {
7162                case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
7163                    // Stay here
7164                    log(getName() + " POOR_LINK_DETECTED: no transition");
7165                    break;
7166                case WifiWatchdogStateMachine.GOOD_LINK_DETECTED:
7167                    log(getName() + " GOOD_LINK_DETECTED: transition to captive portal check");
7168
7169                    log(getName() + " GOOD_LINK_DETECTED: transition to CONNECTED");
7170                    sendConnectedState();
7171                    transitionTo(mConnectedState);
7172                    break;
7173                default:
7174                    if (DBG) log(getName() + " what=" + message.what + " NOT_HANDLED");
7175                    return NOT_HANDLED;
7176            }
7177            return HANDLED;
7178        }
7179    }
7180
7181    private void sendConnectedState() {
7182        // Send out a broadcast with the CAPTIVE_PORTAL_CHECK to preserve
7183        // existing behaviour. The captive portal check really happens after we
7184        // transition into DetailedState.CONNECTED.
7185        setNetworkDetailedState(DetailedState.CAPTIVE_PORTAL_CHECK);
7186        mWifiConfigStore.updateStatus(mLastNetworkId,
7187        DetailedState.CAPTIVE_PORTAL_CHECK);
7188        sendNetworkStateChangeBroadcast(mLastBssid);
7189
7190        if (mWifiConfigStore.getLastSelectedConfiguration() != null) {
7191            if (mNetworkAgent != null) mNetworkAgent.explicitlySelected();
7192        }
7193
7194        setNetworkDetailedState(DetailedState.CONNECTED);
7195        mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.CONNECTED);
7196        sendNetworkStateChangeBroadcast(mLastBssid);
7197    }
7198
7199    class RoamingState extends State {
7200        boolean mAssociated;
7201        @Override
7202        public void enter() {
7203            if (DBG) {
7204                log("RoamingState Enter"
7205                        + " mScreenOn=" + mScreenOn );
7206            }
7207            setScanAlarm(false, 0);
7208
7209            // Make sure we disconnect if roaming fails
7210            roamWatchdogCount++;
7211            loge("Start Roam Watchdog " + roamWatchdogCount);
7212            sendMessageDelayed(obtainMessage(CMD_ROAM_WATCHDOG_TIMER,
7213                    roamWatchdogCount, 0), ROAM_GUARD_TIMER_MSEC);
7214            mAssociated = false;
7215        }
7216        @Override
7217        public boolean processMessage(Message message) {
7218            logStateAndMessage(message, getClass().getSimpleName());
7219
7220            switch (message.what) {
7221               case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
7222                    if (DBG) log("Roaming and Watchdog reports poor link -> ignore");
7223                    return HANDLED;
7224               case CMD_UNWANTED_NETWORK:
7225                    if (DBG) log("Roaming and CS doesnt want the network -> ignore");
7226                    return HANDLED;
7227               case CMD_SET_OPERATIONAL_MODE:
7228                    if (message.arg1 != CONNECT_MODE) {
7229                        deferMessage(message);
7230                    }
7231                    break;
7232               case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
7233                    /**
7234                     * If we get a SUPPLICANT_STATE_CHANGE_EVENT indicating a DISCONNECT
7235                     * before NETWORK_DISCONNECTION_EVENT
7236                     * And there is an associated BSSID corresponding to our target BSSID, then
7237                     * we have missed the network disconnection, transition to mDisconnectedState
7238                     * and handle the rest of the events there.
7239                     */
7240                    StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
7241                    if (stateChangeResult.state == SupplicantState.DISCONNECTED
7242                            || stateChangeResult.state == SupplicantState.INACTIVE
7243                            || stateChangeResult.state == SupplicantState.INTERFACE_DISABLED) {
7244                        if (DBG) {
7245                            log("STATE_CHANGE_EVENT in roaming state "
7246                                    + stateChangeResult.toString() );
7247                        }
7248                        if (stateChangeResult.BSSID != null
7249                                && stateChangeResult.BSSID.equals(mTargetRoamBSSID)) {
7250                            handleNetworkDisconnect();
7251                            transitionTo(mDisconnectedState);
7252                        }
7253                    }
7254                    if (stateChangeResult.state == SupplicantState.ASSOCIATED) {
7255                        // We completed the layer2 roaming part
7256                        mAssociated = true;
7257                        if (stateChangeResult.BSSID != null) {
7258                            mTargetRoamBSSID = (String) stateChangeResult.BSSID;
7259                        }
7260                    }
7261                    break;
7262                case CMD_ROAM_WATCHDOG_TIMER:
7263                    if (roamWatchdogCount == message.arg1) {
7264                        if (DBG) log("roaming watchdog! -> disconnect");
7265                        mRoamFailCount++;
7266                        handleNetworkDisconnect();
7267                        mWifiNative.disconnect();
7268                        transitionTo(mDisconnectedState);
7269                    }
7270                    break;
7271               case WifiMonitor.NETWORK_CONNECTION_EVENT:
7272                   if (mAssociated) {
7273                       if (DBG) log("roaming and Network connection established");
7274                       mLastNetworkId = message.arg1;
7275                       mLastBssid = (String) message.obj;
7276                       mWifiInfo.setBSSID(mLastBssid);
7277                       mWifiInfo.setNetworkId(mLastNetworkId);
7278                       mWifiConfigStore.handleBSSIDBlackList(mLastNetworkId, mLastBssid, true);
7279                       transitionTo(mObtainingIpState);
7280                   } else {
7281                       messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
7282                   }
7283                   break;
7284               case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
7285                   // Throw away but only if it corresponds to the network we're roaming to
7286                   String bssid = (String)message.obj;
7287                   if (true) {
7288                       String target = "";
7289                       if (mTargetRoamBSSID != null) target = mTargetRoamBSSID;
7290                       log("NETWORK_DISCONNECTION_EVENT in roaming state"
7291                               + " BSSID=" + bssid
7292                               + " target=" + target);
7293                   }
7294                   if (bssid != null && bssid.equals(mTargetRoamBSSID)) {
7295                       handleNetworkDisconnect();
7296                       transitionTo(mDisconnectedState);
7297                   }
7298                   break;
7299                case WifiMonitor.SSID_TEMP_DISABLED:
7300                    // Auth error while roaming
7301                    loge("SSID_TEMP_DISABLED nid=" + Integer.toString(mLastNetworkId)
7302                            + " id=" + Integer.toString(message.arg1)
7303                            + " isRoaming=" + isRoaming()
7304                            + " roam=" + Integer.toString(mAutoRoaming));
7305                    if (message.arg1 == mLastNetworkId) {
7306                        handleNetworkDisconnect();
7307                        transitionTo(mDisconnectingState);
7308                    }
7309                    return NOT_HANDLED;
7310                case CMD_START_SCAN:
7311                    deferMessage(message);
7312                    break;
7313                default:
7314                    return NOT_HANDLED;
7315            }
7316            return HANDLED;
7317        }
7318
7319        @Override
7320        public void exit() {
7321            loge("WifiStateMachine: Leaving Roaming state");
7322        }
7323    }
7324
7325    class ConnectedState extends State {
7326        @Override
7327        public void enter() {
7328            String address;
7329            updateDefaultRouteMacAddress(1000);
7330            if (DBG) {
7331                log("ConnectedState Enter "
7332                        + " mScreenOn=" + mScreenOn
7333                        + " scanperiod="
7334                        + Integer.toString(mWifiConfigStore.associatedPartialScanPeriodMilli) );
7335            }
7336            if (mScreenOn
7337                    && mWifiConfigStore.enableAutoJoinScanWhenAssociated) {
7338                mCurrentScanAlarmMs = mWifiConfigStore.associatedPartialScanPeriodMilli;
7339                // Scan after 200ms
7340                setScanAlarm(true, 200);
7341            } else {
7342                mCurrentScanAlarmMs = 0;
7343            }
7344            registerConnected();
7345            lastConnectAttempt = 0;
7346            targetWificonfiguration = null;
7347            // Paranoia
7348            linkDebouncing = false;
7349
7350            // Not roaming anymore
7351            mAutoRoaming = WifiAutoJoinController.AUTO_JOIN_IDLE;
7352
7353            if (testNetworkDisconnect) {
7354                testNetworkDisconnectCounter++;
7355                loge("ConnectedState Enter start disconnect test " +
7356                        testNetworkDisconnectCounter);
7357                sendMessageDelayed(obtainMessage(CMD_TEST_NETWORK_DISCONNECT,
7358                        testNetworkDisconnectCounter, 0), 15000);
7359            }
7360
7361            // Reenable all networks, allow for hidden networks to be scanned
7362            mWifiConfigStore.enableAllNetworks();
7363
7364            mLastDriverRoamAttempt = 0;
7365        }
7366        @Override
7367        public boolean processMessage(Message message) {
7368            WifiConfiguration config = null;
7369            logStateAndMessage(message, getClass().getSimpleName());
7370
7371            switch (message.what) {
7372                case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
7373                    if (DBG) log("Watchdog reports poor link");
7374                    transitionTo(mVerifyingLinkState);
7375                    break;
7376                case CMD_UNWANTED_NETWORK:
7377                    if (message.arg1 == network_status_unwanted_disconnect) {
7378                        mWifiConfigStore.handleBadNetworkDisconnectReport(mLastNetworkId, mWifiInfo);
7379                        mWifiNative.disconnect();
7380                        transitionTo(mDisconnectingState);
7381                    } else if (message.arg1 == network_status_unwanted_disable_autojoin) {
7382                        config = getCurrentWifiConfiguration();
7383                        if (config != null) {
7384                            // Disable autojoin
7385                            config.noInternetAccess = true;
7386                        }
7387                    }
7388                    return HANDLED;
7389                case CMD_TEST_NETWORK_DISCONNECT:
7390                    // Force a disconnect
7391                    if (message.arg1 == testNetworkDisconnectCounter) {
7392                        mWifiNative.disconnect();
7393                    }
7394                    break;
7395                case CMD_ASSOCIATED_BSSID:
7396                    // ASSOCIATING to a new BSSID while already connected, indicates
7397                    // that driver is roaming
7398                    mLastDriverRoamAttempt = System.currentTimeMillis();
7399                    String toBSSID = (String)message.obj;
7400                    if (toBSSID != null && !toBSSID.equals(mWifiInfo.getBSSID())) {
7401                        mWifiConfigStore.driverRoamedFrom(mWifiInfo);
7402                    }
7403                    return NOT_HANDLED;
7404                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
7405                    long lastRoam = 0;
7406                    if (mLastDriverRoamAttempt != 0) {
7407                        // Calculate time since last driver roam attempt
7408                        lastRoam = System.currentTimeMillis() - mLastDriverRoamAttempt;
7409                        mLastDriverRoamAttempt = 0;
7410                    }
7411                    config = getCurrentWifiConfiguration();
7412                    if (mScreenOn
7413                            && !linkDebouncing
7414                            && config != null
7415                            && config.autoJoinStatus == WifiConfiguration.AUTO_JOIN_ENABLED
7416                            && !mWifiConfigStore.isLastSelectedConfiguration(config)
7417                            && (message.arg2 != 3 /* reason cannot be 3, i.e. locally generated */
7418                                || (lastRoam > 0 && lastRoam < 2000) /* unless driver is roaming */)
7419                            && ((ScanResult.is24GHz(mWifiInfo.getFrequency())
7420                                    && mWifiInfo.getRssi() >
7421                                    WifiConfiguration.BAD_RSSI_24)
7422                                    || (ScanResult.is5GHz(mWifiInfo.getFrequency())
7423                                    && mWifiInfo.getRssi() >
7424                                    WifiConfiguration.BAD_RSSI_5))) {
7425                        // Start de-bouncing the L2 disconnection:
7426                        // this L2 disconnection might be spurious.
7427                        // Hence we allow 7 seconds for the state machine to try
7428                        // to reconnect, go thru the
7429                        // roaming cycle and enter Obtaining IP address
7430                        // before signalling the disconnect to ConnectivityService and L3
7431                        startScanForConfiguration(getCurrentWifiConfiguration(), false);
7432                        linkDebouncing = true;
7433
7434                        sendMessageDelayed(obtainMessage(CMD_DELAYED_NETWORK_DISCONNECT,
7435                                0, mLastNetworkId), LINK_FLAPPING_DEBOUNCE_MSEC);
7436                        if (DBG) {
7437                            log("NETWORK_DISCONNECTION_EVENT in connected state"
7438                                    + " BSSID=" + mWifiInfo.getBSSID()
7439                                    + " RSSI=" + mWifiInfo.getRssi()
7440                                    + " freq=" + mWifiInfo.getFrequency()
7441                                    + " reason=" + message.arg2
7442                                    + " -> debounce");
7443                        }
7444                        return HANDLED;
7445                    } else {
7446                        if (DBG) {
7447                            int ajst = -1;
7448                            if (config != null) ajst = config.autoJoinStatus;
7449                            log("NETWORK_DISCONNECTION_EVENT in connected state"
7450                                    + " BSSID=" + mWifiInfo.getBSSID()
7451                                    + " RSSI=" + mWifiInfo.getRssi()
7452                                    + " freq=" + mWifiInfo.getFrequency()
7453                                    + " was debouncing=" + linkDebouncing
7454                                    + " reason=" + message.arg2
7455                                    + " ajst=" + ajst);
7456                        }
7457                    }
7458                    break;
7459                case CMD_AUTO_ROAM:
7460                    // Clear the driver roam indication since we are attempting a framerwork roam
7461                    mLastDriverRoamAttempt = 0;
7462
7463                    /* Connect command coming from auto-join */
7464                    ScanResult candidate = (ScanResult)message.obj;
7465                    String bssid = "any";
7466                    if (candidate != null && candidate.is5GHz()) {
7467                        // Only lock BSSID for 5GHz networks
7468                        bssid = candidate.BSSID;
7469                    }
7470                    int netId = mLastNetworkId;
7471                    config = getCurrentWifiConfiguration();
7472
7473
7474                    if (config == null) {
7475                        loge("AUTO_ROAM and no config, bail out...");
7476                        break;
7477                    }
7478
7479                    loge("CMD_AUTO_ROAM sup state "
7480                            + mSupplicantStateTracker.getSupplicantStateName()
7481                            + " my state " + getCurrentState().getName()
7482                            + " nid=" + Integer.toString(netId)
7483                            + " config " + config.configKey()
7484                            + " roam=" + Integer.toString(message.arg2)
7485                            + " to " + bssid
7486                            + " targetRoamBSSID " + mTargetRoamBSSID);
7487
7488                    /* Save the BSSID so as to lock it @ firmware */
7489                    if (!autoRoamSetBSSID(config, bssid) && !linkDebouncing) {
7490                        loge("AUTO_ROAM nothing to do");
7491                        // Same BSSID, nothing to do
7492                        messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
7493                        break;
7494                    };
7495
7496                    // Make sure the network is enabled, since supplicant will not reenable it
7497                    mWifiConfigStore.enableNetworkWithoutBroadcast(netId, false);
7498
7499                    boolean ret = false;
7500                    if (mLastNetworkId != netId) {
7501                       if (mWifiConfigStore.selectNetwork(netId) &&
7502                           mWifiNative.reconnect()) {
7503                           ret = true;
7504                       }
7505                    } else {
7506                         ret = mWifiNative.reassociate();
7507                    }
7508                    if (ret) {
7509                        lastConnectAttempt = System.currentTimeMillis();
7510                        targetWificonfiguration = mWifiConfigStore.getWifiConfiguration(netId);
7511
7512                        // replyToMessage(message, WifiManager.CONNECT_NETWORK_SUCCEEDED);
7513                        mAutoRoaming = message.arg2;
7514                        transitionTo(mRoamingState);
7515
7516                    } else {
7517                        loge("Failed to connect config: " + config + " netId: " + netId);
7518                        replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
7519                                WifiManager.ERROR);
7520                        messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
7521                        break;
7522                    }
7523                    break;
7524                default:
7525                    return NOT_HANDLED;
7526            }
7527            return HANDLED;
7528        }
7529
7530        @Override
7531        public void exit() {
7532            loge("WifiStateMachine: Leaving Connected state");
7533            setScanAlarm(false, 0);
7534            mLastDriverRoamAttempt = 0;
7535        }
7536    }
7537
7538    class DisconnectingState extends State {
7539
7540        @Override
7541        public void enter() {
7542            mCurrentScanAlarmMs = mDisconnectedScanPeriodMs;
7543
7544            if (PDBG) {
7545                loge(" Enter DisconnectingState State scan interval " + mFrameworkScanIntervalMs
7546                        + " mEnableBackgroundScan= " + mEnableBackgroundScan
7547                        + " screenOn=" + mScreenOn);
7548            }
7549
7550            // Make sure we disconnect: we enter this state prior connecting to a new
7551            // network, waiting for either a DISCONECT event or a SUPPLICANT_STATE_CHANGE
7552            // event which in this case will be indicating that supplicant started to associate.
7553            // In some cases supplicant doesn't ignore the connect requests (it might not
7554            // find the target SSID in its cache),
7555            // Therefore we end up stuck that state, hence the need for the watchdog.
7556            disconnectingWatchdogCount++;
7557            loge("Start Disconnecting Watchdog " + disconnectingWatchdogCount);
7558            sendMessageDelayed(obtainMessage(CMD_DISCONNECTING_WATCHDOG_TIMER,
7559                    disconnectingWatchdogCount, 0), DISCONNECTING_GUARD_TIMER_MSEC);
7560        }
7561
7562        @Override
7563        public boolean processMessage(Message message) {
7564            logStateAndMessage(message, getClass().getSimpleName());
7565            switch (message.what) {
7566                case CMD_SET_OPERATIONAL_MODE:
7567                    if (message.arg1 != CONNECT_MODE) {
7568                        deferMessage(message);
7569                    }
7570                    break;
7571                case CMD_START_SCAN:
7572                    // Ignore scans while disconnecting
7573                    return HANDLED;
7574                case CMD_DISCONNECTING_WATCHDOG_TIMER:
7575                    if (disconnectingWatchdogCount == message.arg1) {
7576                        if (DBG) log("disconnecting watchdog! -> disconnect");
7577                        handleNetworkDisconnect();
7578                        transitionTo(mDisconnectedState);
7579                    }
7580                    break;
7581                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
7582                    /**
7583                     * If we get a SUPPLICANT_STATE_CHANGE_EVENT before NETWORK_DISCONNECTION_EVENT
7584                     * we have missed the network disconnection, transition to mDisconnectedState
7585                     * and handle the rest of the events there
7586                     */
7587                    deferMessage(message);
7588                    handleNetworkDisconnect();
7589                    transitionTo(mDisconnectedState);
7590                    break;
7591                default:
7592                    return NOT_HANDLED;
7593            }
7594            return HANDLED;
7595        }
7596
7597        @Override
7598        public void exit() {
7599            mCurrentScanAlarmMs = 0;
7600        }
7601    }
7602
7603    class DisconnectedState extends State {
7604        @Override
7605        public void enter() {
7606            // We dont scan frequently if this is a temporary disconnect
7607            // due to p2p
7608            if (mTemporarilyDisconnectWifi) {
7609                mWifiP2pChannel.sendMessage(WifiP2pServiceImpl.DISCONNECT_WIFI_RESPONSE);
7610                return;
7611            }
7612
7613            // Loose the last selection choice
7614            // mWifiAutoJoinController.setLastSelectedConfiguration
7615            // (WifiConfiguration.INVALID_NETWORK_ID);
7616
7617            mFrameworkScanIntervalMs = Settings.Global.getLong(mContext.getContentResolver(),
7618                    Settings.Global.WIFI_FRAMEWORK_SCAN_INTERVAL_MS,
7619                    mDefaultFrameworkScanIntervalMs);
7620
7621            if (mScreenOn)
7622                mCurrentScanAlarmMs = mDisconnectedScanPeriodMs;
7623
7624            if (PDBG) {
7625                loge(" Enter disconnected State scan interval " + mFrameworkScanIntervalMs
7626                        + " mEnableBackgroundScan= " + mEnableBackgroundScan
7627                        + " screenOn=" + mScreenOn);
7628            }
7629
7630            /** clear the roaming state, if we were roaming, we failed */
7631            mAutoRoaming = WifiAutoJoinController.AUTO_JOIN_IDLE;
7632
7633            // Reenable all networks, allow for hidden networks to be scanned
7634            mWifiConfigStore.enableAllNetworks();
7635
7636            /**
7637             * - screen dark and PNO supported => scan alarm disabled
7638             * - everything else => scan alarm enabled with mDefaultFrameworkScanIntervalMs period
7639             */
7640            if ((mScreenOn == false) && mEnableBackgroundScan) { //mEnableBackgroundScan) {
7641                /* If a regular scan result is pending, do not initiate background
7642                 * scan until the scan results are returned. This is needed because
7643                 * initiating a background scan will cancel the regular scan and
7644                 * scan results will not be returned until background scanning is
7645                 * cleared
7646                 */
7647                if (!mIsScanOngoing) {
7648                    mWifiNative.enableBackgroundScan(true);
7649                }
7650            } else {
7651                setScanAlarm(true, 200);
7652            }
7653
7654            /**
7655             * If we have no networks saved, the supplicant stops doing the periodic scan.
7656             * The scans are useful to notify the user of the presence of an open network.
7657             * Note that these are not wake up scans.
7658             */
7659            if (!mP2pConnected.get() && mWifiConfigStore.getConfiguredNetworks().size() == 0) {
7660                sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,
7661                        ++mPeriodicScanToken, 0), mSupplicantScanIntervalMs);
7662            }
7663
7664            mDisconnectedTimeStamp = System.currentTimeMillis();
7665
7666        }
7667        @Override
7668        public boolean processMessage(Message message) {
7669            boolean ret = HANDLED;
7670
7671            logStateAndMessage(message, getClass().getSimpleName());
7672
7673            switch (message.what) {
7674                case CMD_NO_NETWORKS_PERIODIC_SCAN:
7675                    if (mP2pConnected.get()) break;
7676                    if (message.arg1 == mPeriodicScanToken &&
7677                            mWifiConfigStore.getConfiguredNetworks().size() == 0) {
7678                        startScan(UNKNOWN_SCAN_SOURCE, -1, null, null);
7679                        sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,
7680                                    ++mPeriodicScanToken, 0), mSupplicantScanIntervalMs);
7681                    }
7682                    break;
7683                case WifiManager.FORGET_NETWORK:
7684                case CMD_REMOVE_NETWORK:
7685                    // Set up a delayed message here. After the forget/remove is handled
7686                    // the handled delayed message will determine if there is a need to
7687                    // scan and continue
7688                    sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,
7689                                ++mPeriodicScanToken, 0), mSupplicantScanIntervalMs);
7690                    ret = NOT_HANDLED;
7691                    break;
7692                case CMD_SET_OPERATIONAL_MODE:
7693                    if (message.arg1 != CONNECT_MODE) {
7694                        mOperationalMode = message.arg1;
7695
7696                        mWifiConfigStore.disableAllNetworks();
7697                        if (mOperationalMode == SCAN_ONLY_WITH_WIFI_OFF_MODE) {
7698                            mWifiP2pChannel.sendMessage(CMD_DISABLE_P2P_REQ);
7699                            setWifiState(WIFI_STATE_DISABLED);
7700                        }
7701
7702                        transitionTo(mScanModeState);
7703                    }
7704                    break;
7705                    /* Ignore network disconnect */
7706                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
7707                    break;
7708                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
7709                    StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
7710                    if (DBG) {
7711                        loge("SUPPLICANT_STATE_CHANGE_EVENT state=" + stateChangeResult.state +
7712                                " -> state= " + WifiInfo.getDetailedStateOf(stateChangeResult.state)
7713                                + " debouncing=" + linkDebouncing);
7714                    }
7715                    setNetworkDetailedState(WifiInfo.getDetailedStateOf(stateChangeResult.state));
7716                    /* ConnectModeState does the rest of the handling */
7717                    ret = NOT_HANDLED;
7718                    break;
7719                case CMD_START_SCAN:
7720                    if (!isScanAllowed()) {
7721                        // Ignore the scan request
7722                        return HANDLED;
7723                    }
7724                    /* Disable background scan temporarily during a regular scan */
7725                    if (mEnableBackgroundScan) {
7726                        mWifiNative.enableBackgroundScan(false);
7727                    }
7728                    /* Handled in parent state */
7729                    ret = NOT_HANDLED;
7730                    break;
7731                case WifiMonitor.SCAN_RESULTS_EVENT:
7732                    /* Re-enable background scan when a pending scan result is received */
7733                    if (mEnableBackgroundScan && mIsScanOngoing) {
7734                        mWifiNative.enableBackgroundScan(true);
7735                    }
7736                    /* Handled in parent state */
7737                    ret = NOT_HANDLED;
7738                    break;
7739                case WifiP2pServiceImpl.P2P_CONNECTION_CHANGED:
7740                    NetworkInfo info = (NetworkInfo) message.obj;
7741                    mP2pConnected.set(info.isConnected());
7742                    if (mP2pConnected.get()) {
7743                        int defaultInterval = mContext.getResources().getInteger(
7744                                R.integer.config_wifi_scan_interval_p2p_connected);
7745                        long scanIntervalMs = Settings.Global.getLong(mContext.getContentResolver(),
7746                                Settings.Global.WIFI_SCAN_INTERVAL_WHEN_P2P_CONNECTED_MS,
7747                                defaultInterval);
7748                        mWifiNative.setScanInterval((int) scanIntervalMs/1000);
7749                    } else if (mWifiConfigStore.getConfiguredNetworks().size() == 0) {
7750                        if (DBG) log("Turn on scanning after p2p disconnected");
7751                        sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,
7752                                    ++mPeriodicScanToken, 0), mSupplicantScanIntervalMs);
7753                    }
7754                case CMD_RECONNECT:
7755                case CMD_REASSOCIATE:
7756                    if (mTemporarilyDisconnectWifi) {
7757                        // Drop a third party reconnect/reassociate if STA is
7758                        // temporarily disconnected for p2p
7759                        break;
7760                    } else {
7761                        // ConnectModeState handles it
7762                        ret = NOT_HANDLED;
7763                    }
7764                    break;
7765                case CMD_SCREEN_STATE_CHANGED:
7766                    handleScreenStateChanged(message.arg1 != 0,
7767                            /* startBackgroundScanIfNeeded = */ true);
7768                    break;
7769                default:
7770                    ret = NOT_HANDLED;
7771            }
7772            return ret;
7773        }
7774
7775        @Override
7776        public void exit() {
7777            /* No need for a background scan upon exit from a disconnected state */
7778            if (mEnableBackgroundScan) {
7779                mWifiNative.enableBackgroundScan(false);
7780            }
7781            mCurrentScanAlarmMs = 0;
7782            setScanAlarm(false, 0);
7783        }
7784    }
7785
7786    class WpsRunningState extends State {
7787        // Tracks the source to provide a reply
7788        private Message mSourceMessage;
7789        @Override
7790        public void enter() {
7791            mSourceMessage = Message.obtain(getCurrentMessage());
7792        }
7793        @Override
7794        public boolean processMessage(Message message) {
7795            logStateAndMessage(message, getClass().getSimpleName());
7796
7797            switch (message.what) {
7798                case WifiMonitor.WPS_SUCCESS_EVENT:
7799                    // Ignore intermediate success, wait for full connection
7800                    break;
7801                case WifiMonitor.NETWORK_CONNECTION_EVENT:
7802                    replyToMessage(mSourceMessage, WifiManager.WPS_COMPLETED);
7803                    mSourceMessage.recycle();
7804                    mSourceMessage = null;
7805                    deferMessage(message);
7806                    transitionTo(mDisconnectedState);
7807                    break;
7808                case WifiMonitor.WPS_OVERLAP_EVENT:
7809                    replyToMessage(mSourceMessage, WifiManager.WPS_FAILED,
7810                            WifiManager.WPS_OVERLAP_ERROR);
7811                    mSourceMessage.recycle();
7812                    mSourceMessage = null;
7813                    transitionTo(mDisconnectedState);
7814                    break;
7815                case WifiMonitor.WPS_FAIL_EVENT:
7816                    // Arg1 has the reason for the failure
7817                    if ((message.arg1 != WifiManager.ERROR) || (message.arg2 != 0)) {
7818                        replyToMessage(mSourceMessage, WifiManager.WPS_FAILED, message.arg1);
7819                        mSourceMessage.recycle();
7820                        mSourceMessage = null;
7821                        transitionTo(mDisconnectedState);
7822                    } else {
7823                        if (DBG) log("Ignore unspecified fail event during WPS connection");
7824                    }
7825                    break;
7826                case WifiMonitor.WPS_TIMEOUT_EVENT:
7827                    replyToMessage(mSourceMessage, WifiManager.WPS_FAILED,
7828                            WifiManager.WPS_TIMED_OUT);
7829                    mSourceMessage.recycle();
7830                    mSourceMessage = null;
7831                    transitionTo(mDisconnectedState);
7832                    break;
7833                case WifiManager.START_WPS:
7834                    replyToMessage(message, WifiManager.WPS_FAILED, WifiManager.IN_PROGRESS);
7835                    break;
7836                case WifiManager.CANCEL_WPS:
7837                    if (mWifiNative.cancelWps()) {
7838                        replyToMessage(message, WifiManager.CANCEL_WPS_SUCCEDED);
7839                    } else {
7840                        replyToMessage(message, WifiManager.CANCEL_WPS_FAILED, WifiManager.ERROR);
7841                    }
7842                    transitionTo(mDisconnectedState);
7843                    break;
7844                /**
7845                 * Defer all commands that can cause connections to a different network
7846                 * or put the state machine out of connect mode
7847                 */
7848                case CMD_STOP_DRIVER:
7849                case CMD_SET_OPERATIONAL_MODE:
7850                case WifiManager.CONNECT_NETWORK:
7851                case CMD_ENABLE_NETWORK:
7852                case CMD_RECONNECT:
7853                case CMD_REASSOCIATE:
7854                    deferMessage(message);
7855                    break;
7856                case CMD_AUTO_CONNECT:
7857                case CMD_AUTO_ROAM:
7858                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
7859                    return HANDLED;
7860                case CMD_START_SCAN:
7861                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
7862                    return HANDLED;
7863                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
7864                    if (DBG) log("Network connection lost");
7865                    handleNetworkDisconnect();
7866                    break;
7867                case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
7868                    if (DBG) log("Ignore Assoc reject event during WPS Connection");
7869                    break;
7870                case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
7871                    // Disregard auth failure events during WPS connection. The
7872                    // EAP sequence is retried several times, and there might be
7873                    // failures (especially for wps pin). We will get a WPS_XXX
7874                    // event at the end of the sequence anyway.
7875                    if (DBG) log("Ignore auth failure during WPS connection");
7876                    break;
7877                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
7878                    // Throw away supplicant state changes when WPS is running.
7879                    // We will start getting supplicant state changes once we get
7880                    // a WPS success or failure
7881                    break;
7882                default:
7883                    return NOT_HANDLED;
7884            }
7885            return HANDLED;
7886        }
7887
7888        @Override
7889        public void exit() {
7890            mWifiConfigStore.enableAllNetworks();
7891            mWifiConfigStore.loadConfiguredNetworks();
7892        }
7893    }
7894
7895    class SoftApStartingState extends State {
7896        @Override
7897        public void enter() {
7898            final Message message = getCurrentMessage();
7899            if (message.what == CMD_START_AP) {
7900                final WifiConfiguration config = (WifiConfiguration) message.obj;
7901
7902                if (config == null) {
7903                    mWifiApConfigChannel.sendMessage(CMD_REQUEST_AP_CONFIG);
7904                } else {
7905                    mWifiApConfigChannel.sendMessage(CMD_SET_AP_CONFIG, config);
7906                    startSoftApWithConfig(config);
7907                }
7908            } else {
7909                throw new RuntimeException("Illegal transition to SoftApStartingState: " + message);
7910            }
7911        }
7912        @Override
7913        public boolean processMessage(Message message) {
7914            logStateAndMessage(message, getClass().getSimpleName());
7915
7916            switch(message.what) {
7917                case CMD_START_SUPPLICANT:
7918                case CMD_STOP_SUPPLICANT:
7919                case CMD_START_AP:
7920                case CMD_STOP_AP:
7921                case CMD_START_DRIVER:
7922                case CMD_STOP_DRIVER:
7923                case CMD_SET_OPERATIONAL_MODE:
7924                case CMD_SET_COUNTRY_CODE:
7925                case CMD_SET_FREQUENCY_BAND:
7926                case CMD_START_PACKET_FILTERING:
7927                case CMD_STOP_PACKET_FILTERING:
7928                case CMD_TETHER_STATE_CHANGE:
7929                    deferMessage(message);
7930                    break;
7931                case WifiStateMachine.CMD_RESPONSE_AP_CONFIG:
7932                    WifiConfiguration config = (WifiConfiguration) message.obj;
7933                    if (config != null) {
7934                        startSoftApWithConfig(config);
7935                    } else {
7936                        loge("Softap config is null!");
7937                        sendMessage(CMD_START_AP_FAILURE);
7938                    }
7939                    break;
7940                case CMD_START_AP_SUCCESS:
7941                    setWifiApState(WIFI_AP_STATE_ENABLED);
7942                    transitionTo(mSoftApStartedState);
7943                    break;
7944                case CMD_START_AP_FAILURE:
7945                    setWifiApState(WIFI_AP_STATE_FAILED);
7946                    transitionTo(mInitialState);
7947                    break;
7948                default:
7949                    return NOT_HANDLED;
7950            }
7951            return HANDLED;
7952        }
7953    }
7954
7955    class SoftApStartedState extends State {
7956        @Override
7957        public boolean processMessage(Message message) {
7958            logStateAndMessage(message, getClass().getSimpleName());
7959
7960            switch(message.what) {
7961                case CMD_STOP_AP:
7962                    if (DBG) log("Stopping Soft AP");
7963                    /* We have not tethered at this point, so we just shutdown soft Ap */
7964                    try {
7965                        mNwService.stopAccessPoint(mInterfaceName);
7966                    } catch(Exception e) {
7967                        loge("Exception in stopAccessPoint()");
7968                    }
7969                    setWifiApState(WIFI_AP_STATE_DISABLED);
7970                    transitionTo(mInitialState);
7971                    break;
7972                case CMD_START_AP:
7973                    // Ignore a start on a running access point
7974                    break;
7975                    // Fail client mode operation when soft AP is enabled
7976                case CMD_START_SUPPLICANT:
7977                    loge("Cannot start supplicant with a running soft AP");
7978                    setWifiState(WIFI_STATE_UNKNOWN);
7979                    break;
7980                case CMD_TETHER_STATE_CHANGE:
7981                    TetherStateChange stateChange = (TetherStateChange) message.obj;
7982                    if (startTethering(stateChange.available)) {
7983                        transitionTo(mTetheringState);
7984                    }
7985                    break;
7986                default:
7987                    return NOT_HANDLED;
7988            }
7989            return HANDLED;
7990        }
7991    }
7992
7993    class TetheringState extends State {
7994        @Override
7995        public void enter() {
7996            /* Send ourselves a delayed message to shut down if tethering fails to notify */
7997            sendMessageDelayed(obtainMessage(CMD_TETHER_NOTIFICATION_TIMED_OUT,
7998                    ++mTetherToken, 0), TETHER_NOTIFICATION_TIME_OUT_MSECS);
7999        }
8000        @Override
8001        public boolean processMessage(Message message) {
8002            logStateAndMessage(message, getClass().getSimpleName());
8003
8004            switch(message.what) {
8005                case CMD_TETHER_STATE_CHANGE:
8006                    TetherStateChange stateChange = (TetherStateChange) message.obj;
8007                    if (isWifiTethered(stateChange.active)) {
8008                        transitionTo(mTetheredState);
8009                    }
8010                    return HANDLED;
8011                case CMD_TETHER_NOTIFICATION_TIMED_OUT:
8012                    if (message.arg1 == mTetherToken) {
8013                        loge("Failed to get tether update, shutdown soft access point");
8014                        transitionTo(mSoftApStartedState);
8015                        // Needs to be first thing handled
8016                        sendMessageAtFrontOfQueue(CMD_STOP_AP);
8017                    }
8018                    break;
8019                case CMD_START_SUPPLICANT:
8020                case CMD_STOP_SUPPLICANT:
8021                case CMD_START_AP:
8022                case CMD_STOP_AP:
8023                case CMD_START_DRIVER:
8024                case CMD_STOP_DRIVER:
8025                case CMD_SET_OPERATIONAL_MODE:
8026                case CMD_SET_COUNTRY_CODE:
8027                case CMD_SET_FREQUENCY_BAND:
8028                case CMD_START_PACKET_FILTERING:
8029                case CMD_STOP_PACKET_FILTERING:
8030                    deferMessage(message);
8031                    break;
8032                default:
8033                    return NOT_HANDLED;
8034            }
8035            return HANDLED;
8036        }
8037    }
8038
8039    class TetheredState extends State {
8040        @Override
8041        public boolean processMessage(Message message) {
8042            logStateAndMessage(message, getClass().getSimpleName());
8043
8044            switch(message.what) {
8045                case CMD_TETHER_STATE_CHANGE:
8046                    TetherStateChange stateChange = (TetherStateChange) message.obj;
8047                    if (!isWifiTethered(stateChange.active)) {
8048                        loge("Tethering reports wifi as untethered!, shut down soft Ap");
8049                        setHostApRunning(null, false);
8050                        setHostApRunning(null, true);
8051                    }
8052                    return HANDLED;
8053                case CMD_STOP_AP:
8054                    if (DBG) log("Untethering before stopping AP");
8055                    setWifiApState(WIFI_AP_STATE_DISABLING);
8056                    stopTethering();
8057                    transitionTo(mUntetheringState);
8058                    // More work to do after untethering
8059                    deferMessage(message);
8060                    break;
8061                default:
8062                    return NOT_HANDLED;
8063            }
8064            return HANDLED;
8065        }
8066    }
8067
8068    class UntetheringState extends State {
8069        @Override
8070        public void enter() {
8071            /* Send ourselves a delayed message to shut down if tethering fails to notify */
8072            sendMessageDelayed(obtainMessage(CMD_TETHER_NOTIFICATION_TIMED_OUT,
8073                    ++mTetherToken, 0), TETHER_NOTIFICATION_TIME_OUT_MSECS);
8074
8075        }
8076        @Override
8077        public boolean processMessage(Message message) {
8078            logStateAndMessage(message, getClass().getSimpleName());
8079
8080            switch(message.what) {
8081                case CMD_TETHER_STATE_CHANGE:
8082                    TetherStateChange stateChange = (TetherStateChange) message.obj;
8083
8084                    /* Wait till wifi is untethered */
8085                    if (isWifiTethered(stateChange.active)) break;
8086
8087                    transitionTo(mSoftApStartedState);
8088                    break;
8089                case CMD_TETHER_NOTIFICATION_TIMED_OUT:
8090                    if (message.arg1 == mTetherToken) {
8091                        loge("Failed to get tether update, force stop access point");
8092                        transitionTo(mSoftApStartedState);
8093                    }
8094                    break;
8095                case CMD_START_SUPPLICANT:
8096                case CMD_STOP_SUPPLICANT:
8097                case CMD_START_AP:
8098                case CMD_STOP_AP:
8099                case CMD_START_DRIVER:
8100                case CMD_STOP_DRIVER:
8101                case CMD_SET_OPERATIONAL_MODE:
8102                case CMD_SET_COUNTRY_CODE:
8103                case CMD_SET_FREQUENCY_BAND:
8104                case CMD_START_PACKET_FILTERING:
8105                case CMD_STOP_PACKET_FILTERING:
8106                    deferMessage(message);
8107                    break;
8108                default:
8109                    return NOT_HANDLED;
8110            }
8111            return HANDLED;
8112        }
8113    }
8114
8115    //State machine initiated requests can have replyTo set to null indicating
8116    //there are no recepients, we ignore those reply actions
8117    private void replyToMessage(Message msg, int what) {
8118        if (msg.replyTo == null) return;
8119        Message dstMsg = obtainMessageWithArg2(msg);
8120        dstMsg.what = what;
8121        mReplyChannel.replyToMessage(msg, dstMsg);
8122    }
8123
8124    private void replyToMessage(Message msg, int what, int arg1) {
8125        if (msg.replyTo == null) return;
8126        Message dstMsg = obtainMessageWithArg2(msg);
8127        dstMsg.what = what;
8128        dstMsg.arg1 = arg1;
8129        mReplyChannel.replyToMessage(msg, dstMsg);
8130    }
8131
8132    private void replyToMessage(Message msg, int what, Object obj) {
8133        if (msg.replyTo == null) return;
8134        Message dstMsg = obtainMessageWithArg2(msg);
8135        dstMsg.what = what;
8136        dstMsg.obj = obj;
8137        mReplyChannel.replyToMessage(msg, dstMsg);
8138    }
8139
8140    /**
8141     * arg2 on the source message has a unique id that needs to be retained in replies
8142     * to match the request
8143
8144     * see WifiManager for details
8145     */
8146    private Message obtainMessageWithArg2(Message srcMsg) {
8147        Message msg = Message.obtain();
8148        msg.arg2 = srcMsg.arg2;
8149        return msg;
8150    }
8151
8152    private static int parseHex(char ch) {
8153        if ('0' <= ch && ch <= '9') {
8154            return ch - '0';
8155        } else if ('a' <= ch && ch <= 'f') {
8156            return ch - 'a' + 10;
8157        } else if ('A' <= ch && ch <= 'F') {
8158            return ch - 'A' + 10;
8159        } else {
8160            throw new NumberFormatException("" + ch + " is not a valid hex digit");
8161        }
8162    }
8163
8164    private byte[] parseHex(String hex) {
8165        /* This only works for good input; don't throw bad data at it */
8166        if (hex == null) {
8167            return new byte[0];
8168        }
8169
8170        if (hex.length() % 2 != 0) {
8171            throw new NumberFormatException(hex + " is not a valid hex string");
8172        }
8173
8174        byte[] result = new byte[(hex.length())/2 + 1];
8175        result[0] = (byte) ((hex.length())/2);
8176        for (int i = 0, j = 1; i < hex.length(); i += 2, j++) {
8177            int val = parseHex(hex.charAt(i)) * 16 + parseHex(hex.charAt(i+1));
8178            byte b = (byte) (val & 0xFF);
8179            result[j] = b;
8180        }
8181
8182        return result;
8183    }
8184
8185    private static String makeHex(byte[] bytes) {
8186        StringBuilder sb = new StringBuilder();
8187        for (byte b : bytes) {
8188            sb.append(String.format("%02x", b));
8189        }
8190        return sb.toString();
8191    }
8192
8193    private static String makeHex(byte[] bytes, int from, int len) {
8194        StringBuilder sb = new StringBuilder();
8195        for (int i = 0; i < len; i++) {
8196            sb.append(String.format("%02x", bytes[from+i]));
8197        }
8198        return sb.toString();
8199    }
8200
8201
8202    private static byte[] concat(byte[] array1, byte[] array2, byte[] array3) {
8203
8204        int len = array1.length + array2.length + array3.length;
8205
8206        if (array1.length != 0) {
8207            len++;                      /* add another byte for size */
8208        }
8209
8210        if (array2.length != 0) {
8211            len++;                      /* add another byte for size */
8212        }
8213
8214        if (array3.length != 0) {
8215            len++;                      /* add another byte for size */
8216        }
8217
8218        byte[] result = new byte[len];
8219
8220        int index = 0;
8221        if (array1.length != 0) {
8222            result[index] = (byte) (array1.length & 0xFF);
8223            index++;
8224            for (byte b : array1) {
8225                result[index] = b;
8226                index++;
8227            }
8228        }
8229
8230        if (array2.length != 0) {
8231            result[index] = (byte) (array2.length & 0xFF);
8232            index++;
8233            for (byte b : array2) {
8234                result[index] = b;
8235                index++;
8236            }
8237        }
8238
8239        if (array3.length != 0) {
8240            result[index] = (byte) (array3.length & 0xFF);
8241            index++;
8242            for (byte b : array3) {
8243                result[index] = b;
8244                index++;
8245            }
8246        }
8247        return result;
8248    }
8249
8250    void handleGsmAuthRequest(SimAuthRequestData requestData) {
8251        if (targetWificonfiguration == null
8252                || targetWificonfiguration.networkId == requestData.networkId) {
8253            logd("id matches targetWifiConfiguration");
8254        } else {
8255            logd("id does not match targetWifiConfiguration");
8256            return;
8257        }
8258
8259        TelephonyManager tm = (TelephonyManager)
8260                mContext.getSystemService(Context.TELEPHONY_SERVICE);
8261
8262        if (tm != null) {
8263            StringBuilder sb = new StringBuilder();
8264            for (String challenge : requestData.challenges) {
8265
8266                logd("RAND = " + challenge);
8267
8268                byte[] rand = null;
8269                try {
8270                    rand = parseHex(challenge);
8271                } catch (NumberFormatException e) {
8272                    loge("malformed challenge");
8273                    continue;
8274                }
8275
8276                String base64Challenge = android.util.Base64.encodeToString(
8277                        rand, android.util.Base64.NO_WRAP);
8278                /*
8279                 * appType = 1 => SIM, 2 => USIM according to
8280                 * com.android.internal.telephony.PhoneConstants#APPTYPE_xxx
8281                 */
8282                int appType = 2;
8283                String tmResponse = tm.getIccSimChallengeResponse(appType, base64Challenge);
8284                logv("Raw Response - " + tmResponse);
8285
8286                if (tmResponse != null && tmResponse.length() > 4) {
8287                    byte[] result = android.util.Base64.decode(tmResponse,
8288                            android.util.Base64.DEFAULT);
8289                    logv("Hex Response -" + makeHex(result));
8290                    int sres_len = result[0];
8291                    String sres = makeHex(result, 1, sres_len);
8292                    int kc_offset = 1+sres_len;
8293                    int kc_len = result[kc_offset];
8294                    String kc = makeHex(result, 1+kc_offset, kc_len);
8295                    sb.append(":" + kc + ":" + sres);
8296                    logv("kc:" + kc + " sres:" + sres);
8297                } else {
8298                    loge("bad response - " + tmResponse);
8299                }
8300            }
8301
8302            String response = sb.toString();
8303            logv("Supplicant Response -" + response);
8304            mWifiNative.simAuthResponse(requestData.networkId, response);
8305        } else {
8306            loge("could not get telephony manager");
8307        }
8308    }
8309
8310    void handle3GAuthRequest(SimAuthRequestData requestData) {
8311
8312    }
8313}
8314