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