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