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