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