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