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