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