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