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