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