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