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