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