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