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