WifiStateMachine.java revision f97140d51d14ce0659d381f443c08dbd94dfea28
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.e(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: 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                    if (c.scanResultCache != null) {
2968                        for (ScanResult r : c.scanResultCache.values()) {
2969                            if (r.BSSID.equals(mWifiInfo.getBSSID())) {
2970                                sb.append(" ipfail=").append(r.numIpConfigFailures);
2971                                sb.append(",st=").append(r.autoJoinStatus);
2972                            }
2973                        }
2974                    }
2975                    sb.append(" -> ajst=").append(c.autoJoinStatus);
2976                    sb.append(" ").append(c.disableReason);
2977                    sb.append(" txpkts=").append(mWifiInfo.txSuccess);
2978                    sb.append(",").append(mWifiInfo.txBad);
2979                    sb.append(",").append(mWifiInfo.txRetries);
2980                }
2981                sb.append(printTime());
2982                sb.append(String.format(" bcn=%d", mRunningBeaconCount));
2983                break;
2984            case CMD_UPDATE_LINKPROPERTIES:
2985                sb.append(" ");
2986                sb.append(Integer.toString(msg.arg1));
2987                sb.append(" ");
2988                sb.append(Integer.toString(msg.arg2));
2989                if (mLinkProperties != null) {
2990                    if (mLinkProperties.hasIPv4Address()) {
2991                        sb.append(" v4");
2992                    }
2993                    if (mLinkProperties.hasGlobalIPv6Address()) {
2994                        sb.append(" v6");
2995                    }
2996                    if (mLinkProperties.hasIPv4DefaultRoute()) {
2997                        sb.append(" v4r");
2998                    }
2999                    if (mLinkProperties.hasIPv6DefaultRoute()) {
3000                        sb.append(" v6r");
3001                    }
3002                    if (mLinkProperties.hasIPv4DnsServer()) {
3003                        sb.append(" v4dns");
3004                    }
3005                    if (mLinkProperties.hasIPv6DnsServer()) {
3006                        sb.append(" v6dns");
3007                    }
3008                }
3009                break;
3010            case CMD_SET_COUNTRY_CODE:
3011                sb.append(" ");
3012                sb.append(Integer.toString(msg.arg1));
3013                sb.append(" ");
3014                sb.append(Integer.toString(msg.arg2));
3015                if (msg.obj != null) {
3016                    sb.append(" ").append((String)msg.obj);
3017                }
3018                break;
3019            case CMD_ROAM_WATCHDOG_TIMER:
3020                sb.append(" ");
3021                sb.append(Integer.toString(msg.arg1));
3022                sb.append(" ");
3023                sb.append(Integer.toString(msg.arg2));
3024                sb.append(" cur=").append(roamWatchdogCount);
3025                break;
3026            case CMD_DISCONNECTING_WATCHDOG_TIMER:
3027                sb.append(" ");
3028                sb.append(Integer.toString(msg.arg1));
3029                sb.append(" ");
3030                sb.append(Integer.toString(msg.arg2));
3031                sb.append(" cur=").append(disconnectingWatchdogCount);
3032                break;
3033            default:
3034                sb.append(" ");
3035                sb.append(Integer.toString(msg.arg1));
3036                sb.append(" ");
3037                sb.append(Integer.toString(msg.arg2));
3038                break;
3039        }
3040
3041        return sb.toString();
3042    }
3043
3044    private void handleScreenStateChanged(boolean screenOn, boolean startBackgroundScanIfNeeded) {
3045        mScreenOn = screenOn;
3046        if (PDBG) {
3047            loge(" handleScreenStateChanged Enter: screenOn=" + screenOn
3048                    + " mUserWantsSuspendOpt=" + mUserWantsSuspendOpt
3049                    + " state " + getCurrentState().getName()
3050                    + " suppState:" + mSupplicantStateTracker.getSupplicantStateName());
3051        }
3052        enableRssiPolling(screenOn);
3053        if (screenOn) enableAllNetworks();
3054        if (mUserWantsSuspendOpt.get()) {
3055            if (screenOn) {
3056                sendMessage(CMD_SET_SUSPEND_OPT_ENABLED, 0, 0);
3057            } else {
3058                // Allow 2s for suspend optimizations to be set
3059                mSuspendWakeLock.acquire(2000);
3060                sendMessage(CMD_SET_SUSPEND_OPT_ENABLED, 1, 0);
3061            }
3062        }
3063        mScreenBroadcastReceived.set(true);
3064
3065        getWifiLinkLayerStats(false);
3066        mOnTimeScreenStateChange = mOnTime;
3067        lastScreenStateChangeTimeStamp = lastLinkLayerStatsUpdate;
3068        mEnableBackgroundScan = false;
3069        cancelDelayedScan();
3070
3071        if (screenOn) {
3072            setScanAlarm(false);
3073            clearBlacklist();
3074
3075            fullBandConnectedTimeIntervalMilli = mWifiConfigStore.associatedPartialScanPeriodMilli.get();
3076            // In either Disconnectedstate or ConnectedState,
3077            // start the scan alarm so as to enable autojoin
3078            if (getCurrentState() == mConnectedState
3079                    && mWifiConfigStore.enableAutoJoinScanWhenAssociated.get()) {
3080                // Scan after 500ms
3081                startDelayedScan(500, null, null);
3082            } else if (getCurrentState() == mDisconnectedState) {
3083                // Scan after 200ms
3084                startDelayedScan(200, null, null);
3085            }
3086        } else if (startBackgroundScanIfNeeded) {
3087            // Screen Off and Disconnected and chipset doesn't support scan offload
3088            //              => start scan alarm
3089            // Screen Off and Disconnected and chipset does support scan offload
3090            //              => will use scan offload (i.e. background scan)
3091            if (!mBackgroundScanSupported) {
3092                setScanAlarm(true);
3093            } else {
3094                mEnableBackgroundScan = true;
3095            }
3096        }
3097        if (DBG) logd("backgroundScan enabled=" + mEnableBackgroundScan
3098                + " startBackgroundScanIfNeeded:" + startBackgroundScanIfNeeded);
3099        if (startBackgroundScanIfNeeded) {
3100            // to scan for them in background, we need all networks enabled
3101            enableBackgroundScan(mEnableBackgroundScan);
3102        }
3103        if (DBG) log("handleScreenStateChanged Exit: " + screenOn);
3104    }
3105
3106    private void checkAndSetConnectivityInstance() {
3107        if (mCm == null) {
3108            mCm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
3109        }
3110    }
3111
3112    private boolean startTethering(ArrayList<String> available) {
3113
3114        boolean wifiAvailable = false;
3115
3116        checkAndSetConnectivityInstance();
3117
3118        String[] wifiRegexs = mCm.getTetherableWifiRegexs();
3119
3120        for (String intf : available) {
3121            for (String regex : wifiRegexs) {
3122                if (intf.matches(regex)) {
3123
3124                    InterfaceConfiguration ifcg = null;
3125                    try {
3126                        ifcg = mNwService.getInterfaceConfig(intf);
3127                        if (ifcg != null) {
3128                            /* IP/netmask: 192.168.43.1/255.255.255.0 */
3129                            ifcg.setLinkAddress(new LinkAddress(
3130                                    NetworkUtils.numericToInetAddress("192.168.43.1"), 24));
3131                            ifcg.setInterfaceUp();
3132
3133                            mNwService.setInterfaceConfig(intf, ifcg);
3134                        }
3135                    } catch (Exception e) {
3136                        loge("Error configuring interface " + intf + ", :" + e);
3137                        return false;
3138                    }
3139
3140                    if(mCm.tether(intf) != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
3141                        loge("Error tethering on " + intf);
3142                        return false;
3143                    }
3144                    mTetherInterfaceName = intf;
3145                    return true;
3146                }
3147            }
3148        }
3149        // We found no interfaces to tether
3150        return false;
3151    }
3152
3153    private void stopTethering() {
3154
3155        checkAndSetConnectivityInstance();
3156
3157        /* Clear the interface config to allow dhcp correctly configure new
3158           ip settings */
3159        InterfaceConfiguration ifcg = null;
3160        try {
3161            ifcg = mNwService.getInterfaceConfig(mTetherInterfaceName);
3162            if (ifcg != null) {
3163                ifcg.setLinkAddress(
3164                        new LinkAddress(NetworkUtils.numericToInetAddress("0.0.0.0"), 0));
3165                mNwService.setInterfaceConfig(mTetherInterfaceName, ifcg);
3166            }
3167        } catch (Exception e) {
3168            loge("Error resetting interface " + mTetherInterfaceName + ", :" + e);
3169        }
3170
3171        if (mCm.untether(mTetherInterfaceName) != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
3172            loge("Untether initiate failed!");
3173        }
3174    }
3175
3176    private boolean isWifiTethered(ArrayList<String> active) {
3177
3178        checkAndSetConnectivityInstance();
3179
3180        String[] wifiRegexs = mCm.getTetherableWifiRegexs();
3181        for (String intf : active) {
3182            for (String regex : wifiRegexs) {
3183                if (intf.matches(regex)) {
3184                    return true;
3185                }
3186            }
3187        }
3188        // We found no interfaces that are tethered
3189        return false;
3190    }
3191
3192    /**
3193     * Set the country code from the system setting value, if any.
3194     */
3195    private void setCountryCode() {
3196        String countryCode = Settings.Global.getString(mContext.getContentResolver(),
3197                Settings.Global.WIFI_COUNTRY_CODE);
3198        if (countryCode != null && !countryCode.isEmpty()) {
3199            setCountryCode(countryCode, false);
3200        } else {
3201            //use driver default
3202        }
3203    }
3204
3205    /**
3206     * Set the frequency band from the system setting value, if any.
3207     */
3208    private void setFrequencyBand() {
3209        int band = Settings.Global.getInt(mContext.getContentResolver(),
3210                Settings.Global.WIFI_FREQUENCY_BAND, WifiManager.WIFI_FREQUENCY_BAND_AUTO);
3211        setFrequencyBand(band, false);
3212    }
3213
3214    private void setSuspendOptimizationsNative(int reason, boolean enabled) {
3215        if (DBG) {
3216            log("setSuspendOptimizationsNative: " + reason + " " + enabled
3217                    + " -want " + mUserWantsSuspendOpt.get()
3218                    + " stack:" + Thread.currentThread().getStackTrace()[2].getMethodName()
3219                    +" - "+ Thread.currentThread().getStackTrace()[3].getMethodName()
3220                    +" - "+ Thread.currentThread().getStackTrace()[4].getMethodName()
3221                    +" - "+ Thread.currentThread().getStackTrace()[5].getMethodName());
3222        }
3223        //mWifiNative.setSuspendOptimizations(enabled);
3224
3225        if (enabled) {
3226            mSuspendOptNeedsDisabled &= ~reason;
3227            /* None of dhcp, screen or highperf need it disabled and user wants it enabled */
3228            if (mSuspendOptNeedsDisabled == 0 && mUserWantsSuspendOpt.get()) {
3229                if (DBG) {
3230                    log("setSuspendOptimizationsNative do it " + reason + " " + enabled
3231                            + " stack:" + Thread.currentThread().getStackTrace()[2].getMethodName()
3232                            +" - "+ Thread.currentThread().getStackTrace()[3].getMethodName()
3233                            +" - "+ Thread.currentThread().getStackTrace()[4].getMethodName()
3234                            +" - "+ Thread.currentThread().getStackTrace()[5].getMethodName());
3235                }
3236                mWifiNative.setSuspendOptimizations(true);
3237            }
3238        } else {
3239            mSuspendOptNeedsDisabled |= reason;
3240            mWifiNative.setSuspendOptimizations(false);
3241        }
3242    }
3243
3244    private void setSuspendOptimizations(int reason, boolean enabled) {
3245        if (DBG) log("setSuspendOptimizations: " + reason + " " + enabled);
3246        if (enabled) {
3247            mSuspendOptNeedsDisabled &= ~reason;
3248        } else {
3249            mSuspendOptNeedsDisabled |= reason;
3250        }
3251        if (DBG) log("mSuspendOptNeedsDisabled " + mSuspendOptNeedsDisabled);
3252    }
3253
3254    private void setWifiState(int wifiState) {
3255        final int previousWifiState = mWifiState.get();
3256
3257        try {
3258            if (wifiState == WIFI_STATE_ENABLED) {
3259                mBatteryStats.noteWifiOn();
3260            } else if (wifiState == WIFI_STATE_DISABLED) {
3261                mBatteryStats.noteWifiOff();
3262            }
3263        } catch (RemoteException e) {
3264            loge("Failed to note battery stats in wifi");
3265        }
3266
3267        mWifiState.set(wifiState);
3268
3269        if (DBG) log("setWifiState: " + syncGetWifiStateByName());
3270
3271        final Intent intent = new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION);
3272        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
3273        intent.putExtra(WifiManager.EXTRA_WIFI_STATE, wifiState);
3274        intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_STATE, previousWifiState);
3275        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
3276    }
3277
3278    private void setWifiApState(int wifiApState) {
3279        final int previousWifiApState = mWifiApState.get();
3280
3281        try {
3282            if (wifiApState == WIFI_AP_STATE_ENABLED) {
3283                mBatteryStats.noteWifiOn();
3284            } else if (wifiApState == WIFI_AP_STATE_DISABLED) {
3285                mBatteryStats.noteWifiOff();
3286            }
3287        } catch (RemoteException e) {
3288            loge("Failed to note battery stats in wifi");
3289        }
3290
3291        // Update state
3292        mWifiApState.set(wifiApState);
3293
3294        if (DBG) log("setWifiApState: " + syncGetWifiApStateByName());
3295
3296        final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
3297        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
3298        intent.putExtra(WifiManager.EXTRA_WIFI_AP_STATE, wifiApState);
3299        intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_AP_STATE, previousWifiApState);
3300        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
3301    }
3302
3303    /*
3304    void ageOutScanResults(int age) {
3305        synchronized(mScanResultCache) {
3306            // Trim mScanResults, which prevent WifiStateMachine to return
3307            // obsolete scan results to queriers
3308            long now = System.CurrentTimeMillis();
3309            for (int i = 0; i < mScanResults.size(); i++) {
3310                ScanResult result = mScanResults.get(i);
3311                if ((result.seen > now || (now - result.seen) > age)) {
3312                    mScanResults.remove(i);
3313                }
3314            }
3315        }
3316    }*/
3317
3318    private static final String IE_STR = "ie=";
3319    private static final String ID_STR = "id=";
3320    private static final String BSSID_STR = "bssid=";
3321    private static final String FREQ_STR = "freq=";
3322    private static final String LEVEL_STR = "level=";
3323    private static final String TSF_STR = "tsf=";
3324    private static final String FLAGS_STR = "flags=";
3325    private static final String SSID_STR = "ssid=";
3326    private static final String DELIMITER_STR = "====";
3327    private static final String END_STR = "####";
3328
3329    int emptyScanResultCount = 0;
3330
3331    // Used for matching BSSID strings, at least one characteer must be a non-zero number
3332    private static Pattern mNotZero = Pattern.compile("[1-9a-fA-F]");
3333
3334    /**
3335     * Format:
3336     *
3337     * id=1
3338     * bssid=68:7f:76:d7:1a:6e
3339     * freq=2412
3340     * level=-44
3341     * tsf=1344626243700342
3342     * flags=[WPA2-PSK-CCMP][WPS][ESS]
3343     * ssid=zfdy
3344     * ====
3345     * id=2
3346     * bssid=68:5f:74:d7:1a:6f
3347     * freq=5180
3348     * level=-73
3349     * tsf=1344626243700373
3350     * flags=[WPA2-PSK-CCMP][WPS][ESS]
3351     * ssid=zuby
3352     * ====
3353     */
3354    private void setScanResults() {
3355        mNumScanResultsKnown = 0;
3356        mNumScanResultsReturned = 0;
3357        String bssid = "";
3358        int level = 0;
3359        int freq = 0;
3360        long tsf = 0;
3361        String flags = "";
3362        WifiSsid wifiSsid = null;
3363        String scanResults;
3364        String tmpResults;
3365        StringBuffer scanResultsBuf = new StringBuffer();
3366        int sid = 0;
3367
3368        while (true) {
3369            tmpResults = mWifiNative.scanResults(sid);
3370            if (TextUtils.isEmpty(tmpResults)) break;
3371            scanResultsBuf.append(tmpResults);
3372            scanResultsBuf.append("\n");
3373            String[] lines = tmpResults.split("\n");
3374            sid = -1;
3375            for (int i=lines.length - 1; i >= 0; i--) {
3376                if (lines[i].startsWith(END_STR)) {
3377                    break;
3378                } else if (lines[i].startsWith(ID_STR)) {
3379                    try {
3380                        sid = Integer.parseInt(lines[i].substring(ID_STR.length())) + 1;
3381                    } catch (NumberFormatException e) {
3382                        // Nothing to do
3383                    }
3384                    break;
3385                }
3386            }
3387            if (sid == -1) break;
3388        }
3389
3390        // Age out scan results, we return all scan results found in the last 12 seconds,
3391        // and NOT all scan results since last scan.
3392        // ageOutScanResults(12000);
3393
3394        scanResults = scanResultsBuf.toString();
3395        if (TextUtils.isEmpty(scanResults)) {
3396            emptyScanResultCount++;
3397            if (emptyScanResultCount > 10) {
3398                // If we got too many empty scan results, the current scan cache is stale,
3399                // hence clear it.
3400                mScanResults = new ArrayList<>();
3401            }
3402           return;
3403        }
3404
3405        emptyScanResultCount = 0;
3406
3407        // note that all these splits and substrings keep references to the original
3408        // huge string buffer while the amount we really want is generally pretty small
3409        // so make copies instead (one example b/11087956 wasted 400k of heap here).
3410        synchronized(mScanResultCache) {
3411            mScanResults = new ArrayList<>();
3412            String[] lines = scanResults.split("\n");
3413            final int bssidStrLen = BSSID_STR.length();
3414            final int flagLen = FLAGS_STR.length();
3415            String infoElements = null;
3416            List<String> anqpLines = null;
3417
3418            for (String line : lines) {
3419                if (line.startsWith(BSSID_STR)) {
3420                    bssid = new String(line.getBytes(), bssidStrLen, line.length() - bssidStrLen);
3421                } else if (line.startsWith(FREQ_STR)) {
3422                    try {
3423                        freq = Integer.parseInt(line.substring(FREQ_STR.length()));
3424                    } catch (NumberFormatException e) {
3425                        freq = 0;
3426                    }
3427                } else if (line.startsWith(LEVEL_STR)) {
3428                    try {
3429                        level = Integer.parseInt(line.substring(LEVEL_STR.length()));
3430                        /* some implementations avoid negative values by adding 256
3431                         * so we need to adjust for that here.
3432                         */
3433                        if (level > 0) level -= 256;
3434                    } catch(NumberFormatException e) {
3435                        level = 0;
3436                    }
3437                } else if (line.startsWith(TSF_STR)) {
3438                    try {
3439                        tsf = Long.parseLong(line.substring(TSF_STR.length()));
3440                    } catch (NumberFormatException e) {
3441                        tsf = 0;
3442                    }
3443                } else if (line.startsWith(FLAGS_STR)) {
3444                    flags = new String(line.getBytes(), flagLen, line.length() - flagLen);
3445                } else if (line.startsWith(SSID_STR)) {
3446                    wifiSsid = WifiSsid.createFromAsciiEncoded(
3447                            line.substring(SSID_STR.length()));
3448                } else if (line.startsWith(IE_STR)) {
3449                    infoElements = line;
3450                } else if (SupplicantBridge.isAnqpAttribute(line)) {
3451                    if (anqpLines == null) {
3452                        anqpLines = new ArrayList<>();
3453                    }
3454                    anqpLines.add(line);
3455                } else if (line.startsWith(DELIMITER_STR) || line.startsWith(END_STR)) {
3456                    if (bssid != null) {
3457                        try {
3458                            NetworkDetail networkDetail =
3459                                    new NetworkDetail(bssid, infoElements, anqpLines, freq);
3460
3461                            String xssid = (wifiSsid != null) ? wifiSsid.toString() : WifiSsid.NONE;
3462                            if (!xssid.equals(networkDetail.getSSID())) {
3463                                Log.d("HS2J", "Inconsistency: SSID: '" + xssid +
3464                                        "' vs '" + networkDetail.getSSID() + "': " + infoElements);
3465                            }
3466
3467                            if (networkDetail.hasInterworking()) {
3468                                Log.d("HS2J", "HSNwk: '" + networkDetail);
3469                            }
3470
3471                            ScanDetail scanDetail = mScanResultCache.get(networkDetail);
3472                            if (scanDetail != null) {
3473                                scanDetail.updateResults(networkDetail, level, wifiSsid, xssid,
3474                                        flags, freq, tsf);
3475                            } else {
3476                                scanDetail = new ScanDetail(networkDetail, wifiSsid, bssid,
3477                                        flags, level, freq, tsf);
3478                                mScanResultCache.put(networkDetail, scanDetail);
3479                            }
3480
3481                            mNumScanResultsReturned++; // Keep track of how many scan results we got
3482                            // as part of this scan's processing
3483                            mScanResults.add(scanDetail);
3484                        }
3485                        catch (IllegalArgumentException iae) {
3486                            Log.d("HS2J", "Failed to parse information elements: " + iae);
3487                        }
3488                    }
3489                    bssid = null;
3490                    level = 0;
3491                    freq = 0;
3492                    tsf = 0;
3493                    flags = "";
3494                    wifiSsid = null;
3495                    infoElements = null;
3496                    anqpLines = null;
3497                }
3498            }
3499        }
3500
3501        boolean attemptAutoJoin = true;
3502        SupplicantState state = mWifiInfo.getSupplicantState();
3503        String selection = mWifiConfigStore.getLastSelectedConfiguration();
3504        if (getCurrentState() == mRoamingState
3505                || getCurrentState() == mObtainingIpState
3506                || getCurrentState() == mScanModeState
3507                || getCurrentState() == mDisconnectingState
3508                || (getCurrentState() == mConnectedState
3509                && !mWifiConfigStore.enableAutoJoinWhenAssociated.get())
3510                || linkDebouncing
3511                || state == SupplicantState.ASSOCIATING
3512                || state == SupplicantState.AUTHENTICATING
3513                || state == SupplicantState.FOUR_WAY_HANDSHAKE
3514                || state == SupplicantState.GROUP_HANDSHAKE
3515                || (/* keep autojoin enabled if user has manually selected a wifi network,
3516                        so as to make sure we reliably remain connected to this network */
3517                        mConnectionRequests == 0 && selection == null)) {
3518            // Dont attempt auto-joining again while we are already attempting to join
3519            // and/or obtaining Ip address
3520            attemptAutoJoin = false;
3521        }
3522        if (DBG) {
3523            if (selection == null) {
3524                selection = "<none>";
3525            }
3526            loge("wifi setScanResults state" + getCurrentState()
3527                    + " sup_state=" + state
3528                    + " debouncing=" + linkDebouncing
3529                    + " mConnectionRequests=" + mConnectionRequests
3530                    + " selection=" + selection);
3531        }
3532        if (attemptAutoJoin) {
3533            messageHandlingStatus = MESSAGE_HANDLING_STATUS_PROCESSED;
3534        }
3535        // Loose last selected configuration if we have been disconnected for 5 minutes
3536        if (getDisconnectedTimeMilli() > mWifiConfigStore.wifiConfigLastSelectionHysteresis) {
3537            mWifiConfigStore.setLastSelectedConfiguration(WifiConfiguration.INVALID_NETWORK_ID);
3538        }
3539
3540        if (mWifiConfigStore.enableAutoJoinWhenAssociated.get()) {
3541            synchronized(mScanResultCache) {
3542                // AutoJoincontroller will directly acces the scan result list and update it with
3543                // ScanResult status
3544                mNumScanResultsKnown = mWifiAutoJoinController.newSupplicantResults(attemptAutoJoin);
3545            }
3546        }
3547        if (linkDebouncing) {
3548            // If debouncing, we dont re-select a SSID or BSSID hence
3549            // there is no need to call the network selection code
3550            // in WifiAutoJoinController, instead,
3551            // just try to reconnect to the same SSID by triggering a roam
3552            sendMessage(CMD_AUTO_ROAM, mLastNetworkId, 1, null);
3553        }
3554    }
3555
3556    /*
3557     * Fetch RSSI, linkspeed, and frequency on current connection
3558     */
3559    private void fetchRssiLinkSpeedAndFrequencyNative() {
3560        int newRssi = -1;
3561        int newLinkSpeed = -1;
3562        int newFrequency = -1;
3563
3564        String signalPoll = mWifiNative.signalPoll();
3565
3566        if (signalPoll != null) {
3567            String[] lines = signalPoll.split("\n");
3568            for (String line : lines) {
3569                String[] prop = line.split("=");
3570                if (prop.length < 2) continue;
3571                try {
3572                    if (prop[0].equals("RSSI")) {
3573                        newRssi = Integer.parseInt(prop[1]);
3574                    } else if (prop[0].equals("LINKSPEED")) {
3575                        newLinkSpeed = Integer.parseInt(prop[1]);
3576                    } else if (prop[0].equals("FREQUENCY")) {
3577                        newFrequency = Integer.parseInt(prop[1]);
3578                    }
3579                } catch (NumberFormatException e) {
3580                    //Ignore, defaults on rssi and linkspeed are assigned
3581                }
3582            }
3583        }
3584
3585        if (PDBG) {
3586            loge("fetchRssiLinkSpeedAndFrequencyNative rssi="
3587                    + Integer.toString(newRssi) + " linkspeed="
3588                    + Integer.toString(newLinkSpeed));
3589        }
3590
3591        if (newRssi > WifiInfo.INVALID_RSSI && newRssi < WifiInfo.MAX_RSSI) {
3592        // screen out invalid values
3593            /* some implementations avoid negative values by adding 256
3594             * so we need to adjust for that here.
3595             */
3596            if (newRssi > 0) newRssi -= 256;
3597            mWifiInfo.setRssi(newRssi);
3598            /*
3599             * Rather then sending the raw RSSI out every time it
3600             * changes, we precalculate the signal level that would
3601             * be displayed in the status bar, and only send the
3602             * broadcast if that much more coarse-grained number
3603             * changes. This cuts down greatly on the number of
3604             * broadcasts, at the cost of not informing others
3605             * interested in RSSI of all the changes in signal
3606             * level.
3607             */
3608            int newSignalLevel = WifiManager.calculateSignalLevel(newRssi, WifiManager.RSSI_LEVELS);
3609            if (newSignalLevel != mLastSignalLevel) {
3610                sendRssiChangeBroadcast(newRssi);
3611            }
3612            mLastSignalLevel = newSignalLevel;
3613        } else {
3614            mWifiInfo.setRssi(WifiInfo.INVALID_RSSI);
3615        }
3616
3617        if (newLinkSpeed != -1) {
3618            mWifiInfo.setLinkSpeed(newLinkSpeed);
3619        }
3620        if (newFrequency > 0) {
3621            if (ScanResult.is5GHz(newFrequency)) {
3622                mWifiConnectionStatistics.num5GhzConnected++;
3623            }
3624            if (ScanResult.is24GHz(newFrequency)) {
3625                mWifiConnectionStatistics.num24GhzConnected++;
3626            }
3627            mWifiInfo.setFrequency(newFrequency);
3628        }
3629        mWifiConfigStore.updateConfiguration(mWifiInfo);
3630    }
3631
3632    /**
3633     *  Determine if we need to switch network:
3634     * - the delta determine the urgency to switch and/or or the expected evilness of the disruption
3635     * - match the uregncy of the switch versus the packet usage at the interface
3636     */
3637    boolean shouldSwitchNetwork(int networkDelta) {
3638        int delta;
3639        if (networkDelta <= 0) {
3640            return false;
3641        }
3642        delta = networkDelta;
3643        if (mWifiInfo != null) {
3644            if (!mWifiConfigStore.enableAutoJoinWhenAssociated.get()
3645                    && mWifiInfo.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID) {
3646                // If AutoJoin while associated is not enabled,
3647                // we should never switch network when already associated
3648                delta = -1000;
3649            } else {
3650                // TODO: Look at per AC packet count, do not switch if VO/VI traffic is present
3651                // TODO: at the interface. We should also discriminate between ucast and mcast,
3652                // TODO: since the rxSuccessRate include all the bonjour and Ipv6
3653                // TODO: broadcasts
3654                if ((mWifiInfo.txSuccessRate > 20) || (mWifiInfo.rxSuccessRate > 80)) {
3655                    delta -= 999;
3656                } else if ((mWifiInfo.txSuccessRate > 5) || (mWifiInfo.rxSuccessRate > 30)) {
3657                    delta -= 6;
3658                }
3659                loge("WifiStateMachine shouldSwitchNetwork "
3660                        + " txSuccessRate=" + String.format("%.2f", mWifiInfo.txSuccessRate)
3661                        + " rxSuccessRate=" + String.format("%.2f", mWifiInfo.rxSuccessRate)
3662                        + " delta " + networkDelta + " -> " + delta);
3663            }
3664        } else {
3665            loge("WifiStateMachine shouldSwitchNetwork "
3666                    + " delta " + networkDelta + " -> " + delta);
3667        }
3668        if (delta > 0) {
3669            return true;
3670        }
3671        return false;
3672    }
3673
3674    // Polling has completed, hence we wont have a score anymore
3675    private void cleanWifiScore() {
3676        mWifiInfo.txBadRate = 0;
3677        mWifiInfo.txSuccessRate = 0;
3678        mWifiInfo.txRetriesRate = 0;
3679        mWifiInfo.rxSuccessRate = 0;
3680    }
3681
3682    int mBadLinkspeedcount = 0;
3683
3684    // For debug, provide information about the last scoring operation
3685    String wifiScoringReport = null;
3686    private void calculateWifiScore(WifiLinkLayerStats stats) {
3687        StringBuilder sb = new StringBuilder();
3688
3689        int score = 56; // Starting score, temporarily hardcoded in between 50 and 60
3690        boolean isBadLinkspeed = (mWifiInfo.is24GHz()
3691                && mWifiInfo.getLinkSpeed() < mWifiConfigStore.badLinkSpeed24)
3692                || (mWifiInfo.is5GHz() && mWifiInfo.getLinkSpeed()
3693                < mWifiConfigStore.badLinkSpeed5);
3694        boolean isGoodLinkspeed = (mWifiInfo.is24GHz()
3695                && mWifiInfo.getLinkSpeed() >= mWifiConfigStore.goodLinkSpeed24)
3696                || (mWifiInfo.is5GHz() && mWifiInfo.getLinkSpeed()
3697                >= mWifiConfigStore.goodLinkSpeed5);
3698
3699        if (isBadLinkspeed) {
3700            if (mBadLinkspeedcount < 6)
3701                mBadLinkspeedcount++;
3702        } else {
3703            if (mBadLinkspeedcount > 0)
3704                mBadLinkspeedcount--;
3705        }
3706
3707        if (isBadLinkspeed) sb.append(" bl(").append(mBadLinkspeedcount).append(")");
3708        if (isGoodLinkspeed) sb.append(" gl");
3709
3710        /**
3711         * We want to make sure that we use the 24GHz RSSI thresholds if
3712         * there are 2.4GHz scan results
3713         * otherwise we end up lowering the score based on 5GHz values
3714         * which may cause a switch to LTE before roaming has a chance to try 2.4GHz
3715         * We also might unblacklist the configuation based on 2.4GHz
3716         * thresholds but joining 5GHz anyhow, and failing over to 2.4GHz because 5GHz is not good
3717         */
3718        boolean use24Thresholds = false;
3719        boolean homeNetworkBoost = false;
3720        WifiConfiguration currentConfiguration = getCurrentWifiConfiguration();
3721        if (currentConfiguration != null
3722                && currentConfiguration.scanResultCache != null) {
3723            currentConfiguration.setVisibility(12000);
3724            if (currentConfiguration.visibility != null) {
3725                if (currentConfiguration.visibility.rssi24 != WifiConfiguration.INVALID_RSSI
3726                        && currentConfiguration.visibility.rssi24
3727                        >= (currentConfiguration.visibility.rssi5-2)) {
3728                    use24Thresholds = true;
3729                }
3730            }
3731            if (currentConfiguration.scanResultCache.size() <= 6
3732                && currentConfiguration.allowedKeyManagement.cardinality() == 1
3733                && currentConfiguration.allowedKeyManagement.
3734                    get(WifiConfiguration.KeyMgmt.WPA_PSK) == true) {
3735                // A PSK network with less than 6 known BSSIDs
3736                // This is most likely a home network and thus we want to stick to wifi more
3737                homeNetworkBoost = true;
3738            }
3739        }
3740        if (homeNetworkBoost) sb.append(" hn");
3741        if (use24Thresholds) sb.append(" u24");
3742
3743        int rssi = mWifiInfo.getRssi() - 6 * mAggressiveHandover
3744                + (homeNetworkBoost ? WifiConfiguration.HOME_NETWORK_RSSI_BOOST : 0);
3745        sb.append(String.format(" rssi=%d ag=%d", rssi, mAggressiveHandover));
3746
3747        boolean is24GHz = use24Thresholds || mWifiInfo.is24GHz();
3748
3749        boolean isBadRSSI = (is24GHz && rssi < mWifiConfigStore.thresholdBadRssi24.get())
3750                || (!is24GHz && rssi < mWifiConfigStore.thresholdBadRssi5.get());
3751        boolean isLowRSSI = (is24GHz && rssi < mWifiConfigStore.thresholdLowRssi24.get())
3752                || (!is24GHz && mWifiInfo.getRssi() < mWifiConfigStore.thresholdLowRssi5.get());
3753        boolean isHighRSSI = (is24GHz && rssi >= mWifiConfigStore.thresholdGoodRssi24.get())
3754                || (!is24GHz && mWifiInfo.getRssi() >= mWifiConfigStore.thresholdGoodRssi5.get());
3755
3756        if (isBadRSSI) sb.append(" br");
3757        if (isLowRSSI) sb.append(" lr");
3758        if (isHighRSSI) sb.append(" hr");
3759
3760        int penalizedDueToUserTriggeredDisconnect = 0;        // For debug information
3761        if (currentConfiguration!= null &&
3762                (mWifiInfo.txSuccessRate > 5 || mWifiInfo.rxSuccessRate > 5)) {
3763            if (isBadRSSI) {
3764                currentConfiguration.numTicksAtBadRSSI++;
3765                if (currentConfiguration.numTicksAtBadRSSI > 1000) {
3766                    // We remained associated for a compound amount of time while passing
3767                    // traffic, hence loose the corresponding user triggered disabled stats
3768                    if (currentConfiguration.numUserTriggeredWifiDisableBadRSSI > 0) {
3769                        currentConfiguration.numUserTriggeredWifiDisableBadRSSI--;
3770                    }
3771                    if (currentConfiguration.numUserTriggeredWifiDisableLowRSSI > 0) {
3772                        currentConfiguration.numUserTriggeredWifiDisableLowRSSI--;
3773                    }
3774                    if (currentConfiguration.numUserTriggeredWifiDisableNotHighRSSI > 0) {
3775                        currentConfiguration.numUserTriggeredWifiDisableNotHighRSSI--;
3776                    }
3777                    currentConfiguration.numTicksAtBadRSSI = 0;
3778                }
3779                if (mWifiConfigStore.enableWifiCellularHandoverUserTriggeredAdjustment &&
3780                        (currentConfiguration.numUserTriggeredWifiDisableBadRSSI > 0
3781                        || currentConfiguration.numUserTriggeredWifiDisableLowRSSI > 0
3782                        || currentConfiguration.numUserTriggeredWifiDisableNotHighRSSI > 0)) {
3783                    score = score -5;
3784                    penalizedDueToUserTriggeredDisconnect = 1;
3785                    sb.append(" p1");
3786                }
3787            } else if (isLowRSSI) {
3788                currentConfiguration.numTicksAtLowRSSI++;
3789                if (currentConfiguration.numTicksAtLowRSSI > 1000) {
3790                    // We remained associated for a compound amount of time while passing
3791                    // traffic, hence loose the corresponding user triggered disabled stats
3792                    if (currentConfiguration.numUserTriggeredWifiDisableLowRSSI > 0) {
3793                        currentConfiguration.numUserTriggeredWifiDisableLowRSSI--;
3794                    }
3795                    if (currentConfiguration.numUserTriggeredWifiDisableNotHighRSSI > 0) {
3796                        currentConfiguration.numUserTriggeredWifiDisableNotHighRSSI--;
3797                    }
3798                    currentConfiguration.numTicksAtLowRSSI = 0;
3799                }
3800                if (mWifiConfigStore.enableWifiCellularHandoverUserTriggeredAdjustment &&
3801                        (currentConfiguration.numUserTriggeredWifiDisableLowRSSI > 0
3802                        || currentConfiguration.numUserTriggeredWifiDisableNotHighRSSI > 0)) {
3803                    score = score -5;
3804                    penalizedDueToUserTriggeredDisconnect = 2;
3805                    sb.append(" p2");
3806                }
3807            } else if (!isHighRSSI) {
3808                currentConfiguration.numTicksAtNotHighRSSI++;
3809                if (currentConfiguration.numTicksAtNotHighRSSI > 1000) {
3810                    // We remained associated for a compound amount of time while passing
3811                    // traffic, hence loose the corresponding user triggered disabled stats
3812                    if (currentConfiguration.numUserTriggeredWifiDisableNotHighRSSI > 0) {
3813                        currentConfiguration.numUserTriggeredWifiDisableNotHighRSSI--;
3814                    }
3815                    currentConfiguration.numTicksAtNotHighRSSI = 0;
3816                }
3817                if (mWifiConfigStore.enableWifiCellularHandoverUserTriggeredAdjustment &&
3818                        currentConfiguration.numUserTriggeredWifiDisableNotHighRSSI > 0) {
3819                    score = score -5;
3820                    penalizedDueToUserTriggeredDisconnect = 3;
3821                    sb.append(" p3");
3822                }
3823            }
3824            sb.append(String.format(" ticks %d,%d,%d", currentConfiguration.numTicksAtBadRSSI,
3825                    currentConfiguration.numTicksAtLowRSSI,
3826                    currentConfiguration.numTicksAtNotHighRSSI));
3827        }
3828
3829        if (PDBG) {
3830            String rssiStatus = "";
3831            if (isBadRSSI) rssiStatus += " badRSSI ";
3832            else if (isHighRSSI) rssiStatus += " highRSSI ";
3833            else if (isLowRSSI) rssiStatus += " lowRSSI ";
3834            if (isBadLinkspeed) rssiStatus += " lowSpeed ";
3835            loge("calculateWifiScore freq=" + Integer.toString(mWifiInfo.getFrequency())
3836                            + " speed=" + Integer.toString(mWifiInfo.getLinkSpeed())
3837                            + " score=" + Integer.toString(mWifiInfo.score)
3838                            + rssiStatus
3839                            + " -> txbadrate=" + String.format( "%.2f", mWifiInfo.txBadRate )
3840                            + " txgoodrate=" + String.format("%.2f", mWifiInfo.txSuccessRate)
3841                            + " txretriesrate=" + String.format("%.2f", mWifiInfo.txRetriesRate)
3842                            + " rxrate=" + String.format("%.2f", mWifiInfo.rxSuccessRate)
3843                            + " userTriggerdPenalty" + penalizedDueToUserTriggeredDisconnect);
3844        }
3845
3846        if ((mWifiInfo.txBadRate >= 1) && (mWifiInfo.txSuccessRate < 3)
3847                && (isBadRSSI || isLowRSSI)) {
3848            // Link is stuck
3849            if (mWifiInfo.linkStuckCount < 5)
3850                mWifiInfo.linkStuckCount += 1;
3851            sb.append(String.format(" ls+=%d", mWifiInfo.linkStuckCount));
3852            if (PDBG) loge(" bad link -> stuck count ="
3853                    + Integer.toString(mWifiInfo.linkStuckCount));
3854        } else if (mWifiInfo.txSuccessRate > 2 || mWifiInfo.txBadRate < 0.1) {
3855            if (mWifiInfo.linkStuckCount > 0)
3856                mWifiInfo.linkStuckCount -= 1;
3857            sb.append(String.format(" ls-=%d", mWifiInfo.linkStuckCount));
3858            if (PDBG) loge(" good link -> stuck count ="
3859                    + Integer.toString(mWifiInfo.linkStuckCount));
3860        }
3861
3862        sb.append(String.format(" [%d", score));
3863
3864        if (mWifiInfo.linkStuckCount > 1) {
3865            // Once link gets stuck for more than 3 seconds, start reducing the score
3866            score = score - 2 * (mWifiInfo.linkStuckCount - 1);
3867        }
3868        sb.append(String.format(",%d", score));
3869
3870        if (isBadLinkspeed) {
3871            score -= 4 ;
3872            if (PDBG) {
3873                loge(" isBadLinkspeed   ---> count=" + mBadLinkspeedcount
3874                        + " score=" + Integer.toString(score));
3875            }
3876        } else if ((isGoodLinkspeed) && (mWifiInfo.txSuccessRate > 5)) {
3877            score += 4; // So as bad rssi alone dont kill us
3878        }
3879        sb.append(String.format(",%d", score));
3880
3881        if (isBadRSSI) {
3882            if (mWifiInfo.badRssiCount < 7)
3883                mWifiInfo.badRssiCount += 1;
3884        } else if (isLowRSSI) {
3885            mWifiInfo.lowRssiCount = 1; // Dont increment the lowRssi count above 1
3886            if (mWifiInfo.badRssiCount > 0) {
3887                // Decrement bad Rssi count
3888                mWifiInfo.badRssiCount -= 1;
3889            }
3890        } else {
3891            mWifiInfo.badRssiCount = 0;
3892            mWifiInfo.lowRssiCount = 0;
3893        }
3894
3895        score -= mWifiInfo.badRssiCount * 2 +  mWifiInfo.lowRssiCount ;
3896        sb.append(String.format(",%d", score));
3897
3898        if (PDBG) loge(" badRSSI count" + Integer.toString(mWifiInfo.badRssiCount)
3899                     + " lowRSSI count" + Integer.toString(mWifiInfo.lowRssiCount)
3900                        + " --> score " + Integer.toString(score));
3901
3902
3903        if (isHighRSSI) {
3904            score += 5;
3905            if (PDBG) loge(" isHighRSSI       ---> score=" + Integer.toString(score));
3906        }
3907        sb.append(String.format(",%d]", score));
3908
3909        sb.append(String.format(" brc=%d lrc=%d", mWifiInfo.badRssiCount, mWifiInfo.lowRssiCount));
3910
3911        //sanitize boundaries
3912        if (score > NetworkAgent.WIFI_BASE_SCORE)
3913            score = NetworkAgent.WIFI_BASE_SCORE;
3914        if (score < 0)
3915            score = 0;
3916
3917        //report score
3918        if (score != mWifiInfo.score) {
3919            if (DBG) {
3920                loge("calculateWifiScore() report new score " + Integer.toString(score));
3921            }
3922            mWifiInfo.score = score;
3923            if (mNetworkAgent != null) {
3924                mNetworkAgent.sendNetworkScore(score);
3925            }
3926        }
3927        wifiScoringReport = sb.toString();
3928    }
3929
3930    public double getTxPacketRate() {
3931        if (mWifiInfo != null) {
3932            return mWifiInfo.txSuccessRate;
3933        }
3934        return -1;
3935    }
3936
3937    public double getRxPacketRate() {
3938        if (mWifiInfo != null) {
3939            return mWifiInfo.rxSuccessRate;
3940        }
3941        return -1;
3942    }
3943
3944    /**
3945     * Fetch TX packet counters on current connection
3946     */
3947    private void fetchPktcntNative(RssiPacketCountInfo info) {
3948        String pktcntPoll = mWifiNative.pktcntPoll();
3949
3950        if (pktcntPoll != null) {
3951            String[] lines = pktcntPoll.split("\n");
3952            for (String line : lines) {
3953                String[] prop = line.split("=");
3954                if (prop.length < 2) continue;
3955                try {
3956                    if (prop[0].equals("TXGOOD")) {
3957                        info.txgood = Integer.parseInt(prop[1]);
3958                    } else if (prop[0].equals("TXBAD")) {
3959                        info.txbad = Integer.parseInt(prop[1]);
3960                    }
3961                } catch (NumberFormatException e) {
3962                    // Ignore
3963                }
3964            }
3965        }
3966    }
3967
3968    private boolean clearIPv4Address(String iface) {
3969        try {
3970            InterfaceConfiguration ifcg = new InterfaceConfiguration();
3971            ifcg.setLinkAddress(new LinkAddress("0.0.0.0/0"));
3972            mNwService.setInterfaceConfig(iface, ifcg);
3973            return true;
3974        } catch (RemoteException e) {
3975            return false;
3976        }
3977    }
3978
3979    private boolean isProvisioned(LinkProperties lp) {
3980        return lp.isProvisioned() ||
3981               (mWifiConfigStore.isUsingStaticIp(mLastNetworkId) && lp.hasIPv4Address());
3982    }
3983
3984    /**
3985     * Updates mLinkProperties by merging information from various sources.
3986     *
3987     * This is needed because the information in mLinkProperties comes from multiple sources (DHCP,
3988     * netlink, static configuration, ...). When one of these sources of information has updated
3989     * link properties, we can't just assign them to mLinkProperties or we'd lose track of the
3990     * information that came from other sources. Instead, when one of those sources has new
3991     * information, we update the object that tracks the information from that source and then
3992     * call this method to apply the change to mLinkProperties.
3993     *
3994     * The information in mLinkProperties is currently obtained as follows:
3995     * - Interface name: set in the constructor.
3996     * - IPv4 and IPv6 addresses: netlink, passed in by mNetlinkTracker.
3997     * - IPv4 routes, DNS servers, and domains: DHCP.
3998     * - IPv6 routes and DNS servers: netlink, passed in by mNetlinkTracker.
3999     * - HTTP proxy: the wifi config store.
4000     */
4001    private void updateLinkProperties(int reason) {
4002        LinkProperties newLp = new LinkProperties();
4003
4004        // Interface name and proxy are locally configured.
4005        newLp.setInterfaceName(mInterfaceName);
4006        newLp.setHttpProxy(mWifiConfigStore.getProxyProperties(mLastNetworkId));
4007
4008        // IPv4/v6 addresses, IPv6 routes and IPv6 DNS servers come from netlink.
4009        LinkProperties netlinkLinkProperties = mNetlinkTracker.getLinkProperties();
4010        newLp.setLinkAddresses(netlinkLinkProperties.getLinkAddresses());
4011        for (RouteInfo route : netlinkLinkProperties.getRoutes()) {
4012            newLp.addRoute(route);
4013        }
4014        for (InetAddress dns : netlinkLinkProperties.getDnsServers()) {
4015            newLp.addDnsServer(dns);
4016        }
4017
4018        // IPv4 routes, DNS servers and domains come from mDhcpResults.
4019        synchronized (mDhcpResultsLock) {
4020            // Even when we're using static configuration, we don't need to look at the config
4021            // store, because static IP configuration also populates mDhcpResults.
4022            if ((mDhcpResults != null)) {
4023                for (RouteInfo route : mDhcpResults.getRoutes(mInterfaceName)) {
4024                    newLp.addRoute(route);
4025                }
4026                for (InetAddress dns : mDhcpResults.dnsServers) {
4027                    newLp.addDnsServer(dns);
4028                }
4029                newLp.setDomains(mDhcpResults.domains);
4030            }
4031        }
4032
4033        final boolean linkChanged = !newLp.equals(mLinkProperties);
4034        final boolean wasProvisioned = isProvisioned(mLinkProperties);
4035        final boolean isProvisioned = isProvisioned(newLp);
4036        final boolean lostIPv4Provisioning =
4037            mLinkProperties.hasIPv4Address() && !newLp.hasIPv4Address();
4038        final DetailedState detailedState = getNetworkDetailedState();
4039
4040        if (linkChanged) {
4041            if (DBG) {
4042                log("Link configuration changed for netId: " + mLastNetworkId
4043                        + " old: " + mLinkProperties + " new: " + newLp);
4044            }
4045            mLinkProperties = newLp;
4046            if (TextUtils.isEmpty(mTcpBufferSizes) == false) {
4047                mLinkProperties.setTcpBufferSizes(mTcpBufferSizes);
4048            }
4049            if (mNetworkAgent != null) mNetworkAgent.sendLinkProperties(mLinkProperties);
4050        }
4051
4052        if (DBG) {
4053            StringBuilder sb = new StringBuilder();
4054            sb.append("updateLinkProperties nid: " + mLastNetworkId);
4055            sb.append(" state: " + detailedState);
4056            sb.append(" reason: " + smToString(reason));
4057
4058            if (mLinkProperties != null) {
4059                if (mLinkProperties.hasIPv4Address()) {
4060                    sb.append(" v4");
4061                }
4062                if (mLinkProperties.hasGlobalIPv6Address()) {
4063                    sb.append(" v6");
4064                }
4065                if (mLinkProperties.hasIPv4DefaultRoute()) {
4066                    sb.append(" v4r");
4067                }
4068                if (mLinkProperties.hasIPv6DefaultRoute()) {
4069                    sb.append(" v6r");
4070                }
4071                if (mLinkProperties.hasIPv4DnsServer()) {
4072                    sb.append(" v4dns");
4073                }
4074                if (mLinkProperties.hasIPv6DnsServer()) {
4075                    sb.append(" v6dns");
4076                }
4077                if (isProvisioned) {
4078                    sb.append(" isprov");
4079                }
4080            }
4081            loge(sb.toString());
4082        }
4083
4084        // If we just configured or lost IP configuration, do the needful.
4085        // We don't just call handleSuccessfulIpConfiguration() or handleIpConfigurationLost()
4086        // here because those should only be called if we're attempting to connect or already
4087        // connected, whereas updateLinkProperties can be called at any time.
4088        switch (reason) {
4089            case DhcpStateMachine.DHCP_SUCCESS:
4090            case CMD_STATIC_IP_SUCCESS:
4091                // IPv4 provisioning succeded. Advance to connected state.
4092                sendMessage(CMD_IP_CONFIGURATION_SUCCESSFUL);
4093                if (!isProvisioned) {
4094                    // Can never happen unless DHCP reports success but isProvisioned thinks the
4095                    // resulting configuration is invalid (e.g., no IPv4 address, or the state in
4096                    // mLinkProperties is out of sync with reality, or there's a bug in this code).
4097                    // TODO: disconnect here instead. If our configuration is not usable, there's no
4098                    // point in staying connected, and if mLinkProperties is out of sync with
4099                    // reality, that will cause problems in the future.
4100                    loge("IPv4 config succeeded, but not provisioned");
4101                }
4102                break;
4103
4104            case DhcpStateMachine.DHCP_FAILURE:
4105                // DHCP failed. If we're not already provisioned, or we had IPv4 and now lost it,
4106                // give up and disconnect.
4107                // If we're already provisioned (e.g., IPv6-only network), stay connected.
4108                if (!isProvisioned || lostIPv4Provisioning) {
4109                    sendMessage(CMD_IP_CONFIGURATION_LOST);
4110                } else {
4111                    // DHCP failed, but we're provisioned (e.g., if we're on an IPv6-only network).
4112                    sendMessage(CMD_IP_CONFIGURATION_SUCCESSFUL);
4113
4114                    // To be sure we don't get stuck with a non-working network if all we had is
4115                    // IPv4, remove the IPv4 address from the interface (since we're using DHCP,
4116                    // and DHCP failed). If we had an IPv4 address before, the deletion of the
4117                    // address  will cause a CMD_UPDATE_LINKPROPERTIES. If the IPv4 address was
4118                    // necessary for provisioning, its deletion will cause us to disconnect.
4119                    //
4120                    // This shouldn't be needed, because on an IPv4-only network a DHCP failure will
4121                    // have empty DhcpResults and thus empty LinkProperties, and isProvisioned will
4122                    // not return true if we're using DHCP and don't have an IPv4 default route. So
4123                    // for now it's only here for extra redundancy. However, it will increase
4124                    // robustness if we move to getting IPv4 routes from netlink as well.
4125                    loge("DHCP failure: provisioned, clearing IPv4 address.");
4126                    if (!clearIPv4Address(mInterfaceName)) {
4127                        sendMessage(CMD_IP_CONFIGURATION_LOST);
4128                    }
4129                }
4130                break;
4131
4132            case CMD_STATIC_IP_FAILURE:
4133                // Static configuration was invalid, or an error occurred in applying it. Give up.
4134                sendMessage(CMD_IP_CONFIGURATION_LOST);
4135                break;
4136
4137            case CMD_UPDATE_LINKPROPERTIES:
4138                // IP addresses, DNS servers, etc. changed. Act accordingly.
4139                if (wasProvisioned && !isProvisioned) {
4140                    // We no longer have a usable network configuration. Disconnect.
4141                    sendMessage(CMD_IP_CONFIGURATION_LOST);
4142                } else if (!wasProvisioned && isProvisioned) {
4143                    // We have a usable IPv6-only config. Advance to connected state.
4144                    sendMessage(CMD_IP_CONFIGURATION_SUCCESSFUL);
4145                }
4146                if (linkChanged && getNetworkDetailedState() == DetailedState.CONNECTED) {
4147                    // If anything has changed and we're already connected, send out a notification.
4148                    sendLinkConfigurationChangedBroadcast();
4149                }
4150                break;
4151        }
4152    }
4153
4154    /**
4155     * Clears all our link properties.
4156     */
4157     private void clearLinkProperties() {
4158         // Clear the link properties obtained from DHCP and netlink.
4159         synchronized (mDhcpResultsLock) {
4160             if (mDhcpResults != null) {
4161                 mDhcpResults.clear();
4162             }
4163         }
4164         mNetlinkTracker.clearLinkProperties();
4165
4166         // Now clear the merged link properties.
4167         mLinkProperties.clear();
4168         if (mNetworkAgent != null) mNetworkAgent.sendLinkProperties(mLinkProperties);
4169     }
4170
4171     /**
4172      * try to update default route MAC address.
4173      */
4174      private String updateDefaultRouteMacAddress(int timeout) {
4175          String address = null;
4176          for (RouteInfo route : mLinkProperties.getRoutes()) {
4177              if (route.isDefaultRoute() && route.hasGateway()) {
4178                  InetAddress gateway = route.getGateway();
4179                  if (gateway instanceof Inet4Address) {
4180                      if (PDBG) {
4181                          loge("updateDefaultRouteMacAddress found Ipv4 default :"
4182                                  + gateway.getHostAddress());
4183                      }
4184                      address = macAddressFromRoute(gateway.getHostAddress());
4185                     /* The gateway's MAC address is known */
4186                      if ((address == null) && (timeout > 0)) {
4187                          boolean reachable = false;
4188                          try {
4189                              reachable = gateway.isReachable(timeout);
4190                          } catch (Exception e) {
4191                              loge("updateDefaultRouteMacAddress exception reaching :"
4192                                      + gateway.getHostAddress());
4193
4194                          } finally {
4195                              if (reachable == true) {
4196
4197                                  address = macAddressFromRoute(gateway.getHostAddress());
4198                                  if (PDBG) {
4199                                      loge("updateDefaultRouteMacAddress reachable (tried again) :"
4200                                              + gateway.getHostAddress() + " found " + address);
4201                                  }
4202                              }
4203                          }
4204                      }
4205                      if (address != null) {
4206                          mWifiConfigStore.setDefaultGwMacAddress(mLastNetworkId, address);
4207                      }
4208                  }
4209              }
4210          }
4211          return address;
4212      }
4213
4214    private void sendScanResultsAvailableBroadcast() {
4215        Intent intent = new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
4216        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
4217        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
4218    }
4219
4220    private void sendRssiChangeBroadcast(final int newRssi) {
4221        try {
4222            mBatteryStats.noteWifiRssiChanged(newRssi);
4223        } catch (RemoteException e) {
4224            // Won't happen.
4225        }
4226        Intent intent = new Intent(WifiManager.RSSI_CHANGED_ACTION);
4227        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
4228        intent.putExtra(WifiManager.EXTRA_NEW_RSSI, newRssi);
4229        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
4230    }
4231
4232    private void sendNetworkStateChangeBroadcast(String bssid) {
4233        Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
4234        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
4235        intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, new NetworkInfo(mNetworkInfo));
4236        intent.putExtra(WifiManager.EXTRA_LINK_PROPERTIES, new LinkProperties (mLinkProperties));
4237        if (bssid != null)
4238            intent.putExtra(WifiManager.EXTRA_BSSID, bssid);
4239        if (mNetworkInfo.getDetailedState() == DetailedState.VERIFYING_POOR_LINK ||
4240                mNetworkInfo.getDetailedState() == DetailedState.CONNECTED) {
4241            intent.putExtra(WifiManager.EXTRA_WIFI_INFO, new WifiInfo(mWifiInfo));
4242        }
4243        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
4244    }
4245
4246    private void sendLinkConfigurationChangedBroadcast() {
4247        Intent intent = new Intent(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION);
4248        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
4249        intent.putExtra(WifiManager.EXTRA_LINK_PROPERTIES, new LinkProperties(mLinkProperties));
4250        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
4251    }
4252
4253    private void sendSupplicantConnectionChangedBroadcast(boolean connected) {
4254        Intent intent = new Intent(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
4255        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
4256        intent.putExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, connected);
4257        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
4258    }
4259
4260    /**
4261     * Record the detailed state of a network.
4262     * @param state the new {@code DetailedState}
4263     */
4264    private boolean setNetworkDetailedState(NetworkInfo.DetailedState state) {
4265        boolean hidden = false;
4266
4267        if (linkDebouncing || isRoaming()) {
4268            // There is generally a confusion in the system about colluding
4269            // WiFi Layer 2 state (as reported by supplicant) and the Network state
4270            // which leads to multiple confusion.
4271            //
4272            // If link is de-bouncing or roaming, we already have an IP address
4273            // as well we were connected and are doing L2 cycles of
4274            // reconnecting or renewing IP address to check that we still have it
4275            // This L2 link flapping should ne be reflected into the Network state
4276            // which is the state of the WiFi Network visible to Layer 3 and applications
4277            // Note that once debouncing and roaming are completed, we will
4278            // set the Network state to where it should be, or leave it as unchanged
4279            //
4280            hidden = true;
4281        }
4282        if (DBG) {
4283            log("setDetailed state, old ="
4284                    + mNetworkInfo.getDetailedState() + " and new state=" + state
4285                    + " hidden=" + hidden);
4286        }
4287        if (mNetworkInfo.getExtraInfo() != null && mWifiInfo.getSSID() != null) {
4288            // Always indicate that SSID has changed
4289            if (!mNetworkInfo.getExtraInfo().equals(mWifiInfo.getSSID())) {
4290                if (DBG) {
4291                    log("setDetailed state send new extra info"  + mWifiInfo.getSSID());
4292                }
4293                mNetworkInfo.setExtraInfo(mWifiInfo.getSSID());
4294                sendNetworkStateChangeBroadcast(null);
4295            }
4296        }
4297        if (hidden == true) {
4298            return false;
4299        }
4300
4301        if (state != mNetworkInfo.getDetailedState()) {
4302            mNetworkInfo.setDetailedState(state, null, mWifiInfo.getSSID());
4303            if (mNetworkAgent != null) {
4304                mNetworkAgent.sendNetworkInfo(mNetworkInfo);
4305            }
4306            sendNetworkStateChangeBroadcast(null);
4307            return true;
4308        }
4309        return false;
4310    }
4311
4312    private DetailedState getNetworkDetailedState() {
4313        return mNetworkInfo.getDetailedState();
4314    }
4315
4316
4317    private SupplicantState handleSupplicantStateChange(Message message) {
4318        StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
4319        SupplicantState state = stateChangeResult.state;
4320        // Supplicant state change
4321        // [31-13] Reserved for future use
4322        // [8 - 0] Supplicant state (as defined in SupplicantState.java)
4323        // 50023 supplicant_state_changed (custom|1|5)
4324        mWifiInfo.setSupplicantState(state);
4325        // Network id is only valid when we start connecting
4326        if (SupplicantState.isConnecting(state)) {
4327            mWifiInfo.setNetworkId(stateChangeResult.networkId);
4328        } else {
4329            mWifiInfo.setNetworkId(WifiConfiguration.INVALID_NETWORK_ID);
4330        }
4331
4332        mWifiInfo.setBSSID(stateChangeResult.BSSID);
4333        mWifiInfo.setSSID(stateChangeResult.wifiSsid);
4334
4335        mSupplicantStateTracker.sendMessage(Message.obtain(message));
4336
4337        return state;
4338    }
4339
4340    /**
4341     * Resets the Wi-Fi Connections by clearing any state, resetting any sockets
4342     * using the interface, stopping DHCP & disabling interface
4343     */
4344    private void handleNetworkDisconnect() {
4345        if (DBG) log("handleNetworkDisconnect: Stopping DHCP and clearing IP"
4346                + " stack:" + Thread.currentThread().getStackTrace()[2].getMethodName()
4347                +" - "+ Thread.currentThread().getStackTrace()[3].getMethodName()
4348                +" - "+ Thread.currentThread().getStackTrace()[4].getMethodName()
4349                +" - "+ Thread.currentThread().getStackTrace()[5].getMethodName());
4350
4351
4352        clearCurrentConfigBSSID("handleNetworkDisconnect");
4353
4354        stopDhcp();
4355
4356        try {
4357            mNwService.clearInterfaceAddresses(mInterfaceName);
4358            mNwService.disableIpv6(mInterfaceName);
4359        } catch (Exception e) {
4360            loge("Failed to clear addresses or disable ipv6" + e);
4361        }
4362
4363        /* Reset data structures */
4364        mBadLinkspeedcount = 0;
4365        mWifiInfo.reset();
4366        linkDebouncing = false;
4367        /* Reset roaming parameters */
4368        mAutoRoaming = WifiAutoJoinController.AUTO_JOIN_IDLE;
4369
4370        /**
4371         *  fullBandConnectedTimeIntervalMilli:
4372         *  - start scans at mWifiConfigStore.associatedPartialScanPeriodMilli seconds interval
4373         *  - exponentially increase to mWifiConfigStore.associatedFullScanMaxIntervalMilli
4374         *  Initialize to sane value = 20 seconds
4375         */
4376        fullBandConnectedTimeIntervalMilli = 20 * 1000;
4377
4378        setNetworkDetailedState(DetailedState.DISCONNECTED);
4379        if (mNetworkAgent != null) {
4380            mNetworkAgent.sendNetworkInfo(mNetworkInfo);
4381            mNetworkAgent = null;
4382        }
4383        mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.DISCONNECTED);
4384
4385        /* Clear network properties */
4386        clearLinkProperties();
4387
4388        /* Cend event to CM & network change broadcast */
4389        sendNetworkStateChangeBroadcast(mLastBssid);
4390
4391        /* Cancel auto roam requests */
4392        autoRoamSetBSSID(mLastNetworkId, "any");
4393
4394        mLastBssid= null;
4395        registerDisconnected();
4396        mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
4397    }
4398
4399    private void handleSupplicantConnectionLoss() {
4400        /* Socket connection can be lost when we do a graceful shutdown
4401        * or when the driver is hung. Ensure supplicant is stopped here.
4402        */
4403        mWifiMonitor.killSupplicant(mP2pSupported);
4404        mWifiNative.closeSupplicantConnection();
4405        sendSupplicantConnectionChangedBroadcast(false);
4406        setWifiState(WIFI_STATE_DISABLED);
4407    }
4408
4409    void handlePreDhcpSetup() {
4410        mDhcpActive = true;
4411        if (!mBluetoothConnectionActive) {
4412            /*
4413             * There are problems setting the Wi-Fi driver's power
4414             * mode to active when bluetooth coexistence mode is
4415             * enabled or sense.
4416             * <p>
4417             * We set Wi-Fi to active mode when
4418             * obtaining an IP address because we've found
4419             * compatibility issues with some routers with low power
4420             * mode.
4421             * <p>
4422             * In order for this active power mode to properly be set,
4423             * we disable coexistence mode until we're done with
4424             * obtaining an IP address.  One exception is if we
4425             * are currently connected to a headset, since disabling
4426             * coexistence would interrupt that connection.
4427             */
4428            // Disable the coexistence mode
4429            mWifiNative.setBluetoothCoexistenceMode(
4430                    mWifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED);
4431        }
4432
4433        // Disable power save and suspend optimizations during DHCP
4434        // Note: The order here is important for now. Brcm driver changes
4435        // power settings when we control suspend mode optimizations.
4436        // TODO: Remove this comment when the driver is fixed.
4437        setSuspendOptimizationsNative(SUSPEND_DUE_TO_DHCP, false);
4438        mWifiNative.setPowerSave(false);
4439
4440        // stopBatchedScan();
4441        WifiNative.pauseScan();
4442
4443        // Update link layer stats
4444        getWifiLinkLayerStats(false);
4445
4446        /* P2p discovery breaks dhcp, shut it down in order to get through this */
4447        Message msg = new Message();
4448        msg.what = WifiP2pServiceImpl.BLOCK_DISCOVERY;
4449        msg.arg1 = WifiP2pServiceImpl.ENABLED;
4450        msg.arg2 = DhcpStateMachine.CMD_PRE_DHCP_ACTION_COMPLETE;
4451        msg.obj = mDhcpStateMachine;
4452        mWifiP2pChannel.sendMessage(msg);
4453    }
4454
4455
4456    private boolean useLegacyDhcpClient() {
4457        return Settings.Global.getInt(
4458                mContext.getContentResolver(),
4459                Settings.Global.LEGACY_DHCP_CLIENT, 0) == 1;
4460    }
4461
4462    private void maybeInitDhcpStateMachine() {
4463        if (mDhcpStateMachine == null) {
4464            if (useLegacyDhcpClient()) {
4465                mDhcpStateMachine = DhcpStateMachine.makeDhcpStateMachine(
4466                        mContext, WifiStateMachine.this, mInterfaceName);
4467            } else {
4468                mDhcpStateMachine = DhcpClient.makeDhcpStateMachine(
4469                        mContext, WifiStateMachine.this, mInterfaceName);
4470            }
4471        }
4472    }
4473
4474    void startDhcp() {
4475        maybeInitDhcpStateMachine();
4476        mDhcpStateMachine.registerForPreDhcpNotification();
4477        mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_START_DHCP);
4478    }
4479
4480    void renewDhcp() {
4481        maybeInitDhcpStateMachine();
4482        mDhcpStateMachine.registerForPreDhcpNotification();
4483        mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_RENEW_DHCP);
4484    }
4485
4486    void stopDhcp() {
4487        if (mDhcpStateMachine != null) {
4488            /* In case we were in middle of DHCP operation restore back powermode */
4489            handlePostDhcpSetup();
4490            mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_STOP_DHCP);
4491        }
4492    }
4493
4494    void handlePostDhcpSetup() {
4495        /* Restore power save and suspend optimizations */
4496        setSuspendOptimizationsNative(SUSPEND_DUE_TO_DHCP, true);
4497        mWifiNative.setPowerSave(true);
4498
4499        mWifiP2pChannel.sendMessage(WifiP2pServiceImpl.BLOCK_DISCOVERY, WifiP2pServiceImpl.DISABLED);
4500
4501        // Set the coexistence mode back to its default value
4502        mWifiNative.setBluetoothCoexistenceMode(
4503                mWifiNative.BLUETOOTH_COEXISTENCE_MODE_SENSE);
4504
4505        mDhcpActive = false;
4506
4507        // startBatchedScan();
4508        WifiNative.restartScan();
4509    }
4510
4511    private void handleIPv4Success(DhcpResults dhcpResults, int reason) {
4512
4513        if (PDBG) {
4514            loge("wifistatemachine handleIPv4Success <" + dhcpResults.toString() + ">");
4515            loge("link address " + dhcpResults.ipAddress);
4516        }
4517
4518        synchronized (mDhcpResultsLock) {
4519            mDhcpResults = dhcpResults;
4520        }
4521
4522        Inet4Address addr = (Inet4Address) dhcpResults.ipAddress.getAddress();
4523        if (isRoaming()) {
4524            if (addr instanceof Inet4Address) {
4525                int previousAddress = mWifiInfo.getIpAddress();
4526                int newAddress = NetworkUtils.inetAddressToInt(addr);
4527                if (previousAddress != newAddress) {
4528                    loge("handleIPv4Success, roaming and address changed" +
4529                            mWifiInfo + " got: " + addr);
4530                } else {
4531
4532                }
4533            } else {
4534                loge("handleIPv4Success, roaming and didnt get an IPv4 address" +
4535                        addr.toString());
4536            }
4537        }
4538        mWifiInfo.setInetAddress(addr);
4539        mWifiInfo.setMeteredHint(dhcpResults.hasMeteredHint());
4540        updateLinkProperties(reason);
4541    }
4542
4543    private void handleSuccessfulIpConfiguration() {
4544        mLastSignalLevel = -1; // Force update of signal strength
4545        WifiConfiguration c = getCurrentWifiConfiguration();
4546        if (c != null) {
4547            // Reset IP failure tracking
4548            c.numConnectionFailures = 0;
4549
4550            // Tell the framework whether the newly connected network is trusted or untrusted.
4551            updateCapabilities(c);
4552        }
4553        if (c != null) {
4554            ScanResult result = getCurrentScanResult();
4555            if (result == null) {
4556                loge("WifiStateMachine: handleSuccessfulIpConfiguration and no scan results" +
4557                        c.configKey());
4558            } else {
4559                // Clear the per BSSID failure count
4560                result.numIpConfigFailures = 0;
4561                // Clear the WHOLE BSSID blacklist, which means supplicant is free to retry
4562                // any BSSID, even though it may already have a non zero ip failure count,
4563                // this will typically happen if the user walks away and come back to his arrea
4564                // TODO: implement blacklisting based on a timer, i.e. keep BSSID blacklisted
4565                // in supplicant for a couple of hours or a day
4566                mWifiNative.clearBlacklist();
4567            }
4568        }
4569    }
4570
4571    private void handleIPv4Failure(int reason) {
4572        synchronized(mDhcpResultsLock) {
4573             if (mDhcpResults != null) {
4574                 mDhcpResults.clear();
4575             }
4576        }
4577        if (PDBG) {
4578            loge("wifistatemachine handleIPv4Failure");
4579        }
4580        updateLinkProperties(reason);
4581    }
4582
4583    private void handleIpConfigurationLost() {
4584        mWifiInfo.setInetAddress(null);
4585        mWifiInfo.setMeteredHint(false);
4586
4587        mWifiConfigStore.handleSSIDStateChange(mLastNetworkId, false,
4588                "DHCP FAILURE", mWifiInfo.getBSSID());
4589
4590        /* DHCP times out after about 30 seconds, we do a
4591         * disconnect thru supplicant, we will let autojoin retry connecting to the network
4592         */
4593        mWifiNative.disconnect();
4594    }
4595
4596    private int convertFrequencyToChannelNumber(int frequency) {
4597        if (frequency >= 2412 && frequency <= 2484) {
4598            return (frequency -2412) / 5 + 1;
4599        } else if (frequency >= 5170  &&  frequency <=5825) {
4600            //DFS is included
4601            return (frequency -5170) / 5 + 34;
4602        } else {
4603            return 0;
4604        }
4605    }
4606
4607    private int chooseApChannel(int apBand) {
4608        int apChannel;
4609        int[] channel;
4610
4611        if (apBand == 0)  {
4612            //for 2.4GHz, we only set the AP at channel 1,6,11
4613            apChannel = 5 * mRandom.nextInt(3) + 1;
4614        } else {
4615            //5G without DFS
4616            channel = mWifiNative.getChannelsForBand(2);
4617            if (channel != null && channel.length > 0) {
4618                apChannel = channel[mRandom.nextInt(channel.length)];
4619                apChannel = convertFrequencyToChannelNumber(apChannel);
4620            } else {
4621                Log.e(TAG, "SoftAp do not get available channel list");
4622                apChannel = 0;
4623            }
4624        }
4625
4626        if(DBG) {
4627            Log.d(TAG, "SoftAp set on channel " + apChannel);
4628        }
4629
4630        return apChannel;
4631    }
4632
4633
4634    /* Current design is to not set the config on a running hostapd but instead
4635     * stop and start tethering when user changes config on a running access point
4636     *
4637     * TODO: Add control channel setup through hostapd that allows changing config
4638     * on a running daemon
4639     */
4640    private void startSoftApWithConfig(final WifiConfiguration configuration) {
4641        // set channel
4642        final WifiConfiguration config = new WifiConfiguration(configuration);
4643
4644        if (DBG) {
4645            Log.d(TAG, "SoftAp config channel is: " + config.apChannel);
4646        }
4647        //set country code through HAL Here
4648        if (mSetCountryCode != null) {
4649            if(!mWifiNative.setCountryCodeHal(mSetCountryCode.toUpperCase(Locale.ROOT))) {
4650                if (config.apBand != 0) {
4651                    Log.e(TAG, "Fail to set country code. Can not setup Softap on 5GHz");
4652                    //countrycode is mandatory for 5GHz
4653                    sendMessage(CMD_START_AP_FAILURE);
4654                    return;
4655                }
4656            }
4657        } else {
4658            if (config.apBand != 0) {
4659                //countrycode is mandatory for 5GHz
4660                Log.e(TAG, "Can not setup softAp on 5GHz without country code!");
4661                sendMessage(CMD_START_AP_FAILURE);
4662                return;
4663            }
4664        }
4665
4666        if (config.apChannel == 0) {
4667            config.apChannel = chooseApChannel(config.apBand);
4668            if (config.apChannel == 0) {
4669                //fail to get available channel
4670                sendMessage(CMD_START_AP_FAILURE);
4671                return;
4672            }
4673        }
4674        //turn off interface
4675        if (!mWifiNative.toggleInterface(0)) {
4676            sendMessage(CMD_START_AP_FAILURE);
4677            return;
4678        }
4679        // Start hostapd on a separate thread
4680        new Thread(new Runnable() {
4681            public void run() {
4682                try {
4683                    mNwService.startAccessPoint(config, mInterfaceName);
4684                } catch (Exception e) {
4685                    loge("Exception in softap start " + e);
4686                    try {
4687                        mNwService.stopAccessPoint(mInterfaceName);
4688                        mNwService.startAccessPoint(config, mInterfaceName);
4689                    } catch (Exception e1) {
4690                        loge("Exception in softap re-start " + e1);
4691                        sendMessage(CMD_START_AP_FAILURE);
4692                        return;
4693                    }
4694                }
4695                if (DBG) log("Soft AP start successful");
4696                sendMessage(CMD_START_AP_SUCCESS);
4697            }
4698        }).start();
4699    }
4700
4701    /*
4702     * Read a MAC address in /proc/arp/table, used by WifistateMachine
4703     * so as to record MAC address of default gateway.
4704     **/
4705    private String macAddressFromRoute(String ipAddress) {
4706        String macAddress = null;
4707        BufferedReader reader = null;
4708        try {
4709            reader = new BufferedReader(new FileReader("/proc/net/arp"));
4710
4711            // Skip over the line bearing colum titles
4712            String line = reader.readLine();
4713
4714            while ((line = reader.readLine()) != null) {
4715                String[] tokens = line.split("[ ]+");
4716                if (tokens.length < 6) {
4717                    continue;
4718                }
4719
4720                // ARP column format is
4721                // Address HWType HWAddress Flags Mask IFace
4722                String ip = tokens[0];
4723                String mac = tokens[3];
4724
4725                if (ipAddress.equals(ip)) {
4726                    macAddress = mac;
4727                    break;
4728                }
4729            }
4730
4731            if (macAddress == null) {
4732                loge("Did not find remoteAddress {" + ipAddress + "} in " +
4733                        "/proc/net/arp");
4734            }
4735
4736        } catch (FileNotFoundException e) {
4737            loge("Could not open /proc/net/arp to lookup mac address");
4738        } catch (IOException e) {
4739            loge("Could not read /proc/net/arp to lookup mac address");
4740        } finally {
4741            try {
4742                if (reader != null) {
4743                    reader.close();
4744                }
4745            } catch (IOException e) {
4746                // Do nothing
4747            }
4748        }
4749        return macAddress;
4750
4751    }
4752
4753    private class WifiNetworkFactory extends NetworkFactory {
4754        public WifiNetworkFactory(Looper l, Context c, String TAG, NetworkCapabilities f) {
4755            super(l, c, TAG, f);
4756        }
4757
4758        @Override
4759        protected void needNetworkFor(NetworkRequest networkRequest, int score) {
4760            ++mConnectionRequests;
4761        }
4762
4763        @Override
4764        protected void releaseNetworkFor(NetworkRequest networkRequest) {
4765            --mConnectionRequests;
4766        }
4767
4768        public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
4769            pw.println("mConnectionRequests " + mConnectionRequests);
4770        }
4771
4772    }
4773
4774    private class UntrustedWifiNetworkFactory extends NetworkFactory {
4775        private int mUntrustedReqCount;
4776
4777        public UntrustedWifiNetworkFactory(Looper l, Context c, String tag, NetworkCapabilities f) {
4778            super(l, c, tag, f);
4779        }
4780
4781        @Override
4782        protected void needNetworkFor(NetworkRequest networkRequest, int score) {
4783            if (!networkRequest.networkCapabilities.hasCapability(
4784                    NetworkCapabilities.NET_CAPABILITY_TRUSTED)) {
4785                if (++mUntrustedReqCount == 1) {
4786                    mWifiAutoJoinController.setAllowUntrustedConnections(true);
4787                }
4788            }
4789        }
4790
4791        @Override
4792        protected void releaseNetworkFor(NetworkRequest networkRequest) {
4793            if (!networkRequest.networkCapabilities.hasCapability(
4794                    NetworkCapabilities.NET_CAPABILITY_TRUSTED)) {
4795                if (--mUntrustedReqCount == 0) {
4796                    mWifiAutoJoinController.setAllowUntrustedConnections(false);
4797                }
4798            }
4799        }
4800
4801        public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
4802            pw.println("mUntrustedReqCount " + mUntrustedReqCount);
4803        }
4804    }
4805
4806    void maybeRegisterNetworkFactory() {
4807        if (mNetworkFactory == null) {
4808            checkAndSetConnectivityInstance();
4809            if (mCm != null) {
4810                mNetworkFactory = new WifiNetworkFactory(getHandler().getLooper(), mContext,
4811                        NETWORKTYPE, mNetworkCapabilitiesFilter);
4812                mNetworkFactory.setScoreFilter(60);
4813                mNetworkFactory.register();
4814
4815                // We can't filter untrusted network in the capabilities filter because a trusted
4816                // network would still satisfy a request that accepts untrusted ones.
4817                mUntrustedNetworkFactory = new UntrustedWifiNetworkFactory(getHandler().getLooper(),
4818                        mContext, NETWORKTYPE_UNTRUSTED, mNetworkCapabilitiesFilter);
4819                mUntrustedNetworkFactory.setScoreFilter(Integer.MAX_VALUE);
4820                mUntrustedNetworkFactory.register();
4821            }
4822        }
4823    }
4824
4825    /********************************************************
4826     * HSM states
4827     *******************************************************/
4828
4829    class DefaultState extends State {
4830        @Override
4831        public boolean processMessage(Message message) {
4832            logStateAndMessage(message, getClass().getSimpleName());
4833
4834            switch (message.what) {
4835                case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: {
4836                    AsyncChannel ac = (AsyncChannel) message.obj;
4837                    if (ac == mWifiP2pChannel) {
4838                        if (message.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
4839                            mWifiP2pChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
4840                        } else {
4841                            loge("WifiP2pService connection failure, error=" + message.arg1);
4842                        }
4843                    } else {
4844                        loge("got HALF_CONNECTED for unknown channel");
4845                    }
4846                    break;
4847                }
4848                case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
4849                    AsyncChannel ac = (AsyncChannel) message.obj;
4850                    if (ac == mWifiP2pChannel) {
4851                        loge("WifiP2pService channel lost, message.arg1 =" + message.arg1);
4852                        //TODO: Re-establish connection to state machine after a delay
4853                        // mWifiP2pChannel.connect(mContext, getHandler(),
4854                        // mWifiP2pManager.getMessenger());
4855                    }
4856                    break;
4857                }
4858                case CMD_BLUETOOTH_ADAPTER_STATE_CHANGE:
4859                    mBluetoothConnectionActive = (message.arg1 !=
4860                            BluetoothAdapter.STATE_DISCONNECTED);
4861                    break;
4862                    /* Synchronous call returns */
4863                case CMD_PING_SUPPLICANT:
4864                case CMD_ENABLE_NETWORK:
4865                case CMD_ADD_OR_UPDATE_NETWORK:
4866                case CMD_REMOVE_NETWORK:
4867                case CMD_SAVE_CONFIG:
4868                    replyToMessage(message, message.what, FAILURE);
4869                    break;
4870                case CMD_GET_CAPABILITY_FREQ:
4871                    replyToMessage(message, message.what, null);
4872                    break;
4873                case CMD_GET_CONFIGURED_NETWORKS:
4874                    replyToMessage(message, message.what, (List<WifiConfiguration>) null);
4875                    break;
4876                case CMD_GET_PRIVILEGED_CONFIGURED_NETWORKS:
4877                    replyToMessage(message, message.what, (List<WifiConfiguration>) null);
4878                    break;
4879                case CMD_ENABLE_RSSI_POLL:
4880                    mEnableRssiPolling = (message.arg1 == 1);
4881                    break;
4882                case CMD_SET_HIGH_PERF_MODE:
4883                    if (message.arg1 == 1) {
4884                        setSuspendOptimizations(SUSPEND_DUE_TO_HIGH_PERF, false);
4885                    } else {
4886                        setSuspendOptimizations(SUSPEND_DUE_TO_HIGH_PERF, true);
4887                    }
4888                    break;
4889                case CMD_BOOT_COMPLETED:
4890                    maybeRegisterNetworkFactory();
4891                    break;
4892                case CMD_SET_BATCHED_SCAN:
4893                    recordBatchedScanSettings(message.arg1, message.arg2, (Bundle)message.obj);
4894                    break;
4895                case CMD_POLL_BATCHED_SCAN:
4896                    handleBatchedScanPollRequest();
4897                    break;
4898                case CMD_START_NEXT_BATCHED_SCAN:
4899                    startNextBatchedScan();
4900                    break;
4901                case CMD_SCREEN_STATE_CHANGED:
4902                    handleScreenStateChanged(message.arg1 != 0,
4903                            /* startBackgroundScanIfNeeded = */ false);
4904                    break;
4905                    /* Discard */
4906                case CMD_START_SCAN:
4907                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
4908                    break;
4909                case CMD_START_SUPPLICANT:
4910                case CMD_STOP_SUPPLICANT:
4911                case CMD_STOP_SUPPLICANT_FAILED:
4912                case CMD_START_DRIVER:
4913                case CMD_STOP_DRIVER:
4914                case CMD_DELAYED_STOP_DRIVER:
4915                case CMD_DRIVER_START_TIMED_OUT:
4916                case CMD_START_AP:
4917                case CMD_START_AP_SUCCESS:
4918                case CMD_START_AP_FAILURE:
4919                case CMD_STOP_AP:
4920                case CMD_TETHER_STATE_CHANGE:
4921                case CMD_TETHER_NOTIFICATION_TIMED_OUT:
4922                case CMD_DISCONNECT:
4923                case CMD_RECONNECT:
4924                case CMD_REASSOCIATE:
4925                case CMD_RELOAD_TLS_AND_RECONNECT:
4926                case WifiMonitor.SUP_CONNECTION_EVENT:
4927                case WifiMonitor.SUP_DISCONNECTION_EVENT:
4928                case WifiMonitor.NETWORK_CONNECTION_EVENT:
4929                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
4930                case WifiMonitor.SCAN_RESULTS_EVENT:
4931                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
4932                case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
4933                case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
4934                case WifiMonitor.WPS_OVERLAP_EVENT:
4935                case CMD_BLACKLIST_NETWORK:
4936                case CMD_CLEAR_BLACKLIST:
4937                case CMD_SET_OPERATIONAL_MODE:
4938                case CMD_SET_COUNTRY_CODE:
4939                case CMD_SET_FREQUENCY_BAND:
4940                case CMD_RSSI_POLL:
4941                case CMD_ENABLE_ALL_NETWORKS:
4942                case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
4943                case DhcpStateMachine.CMD_POST_DHCP_ACTION:
4944                /* Handled by WifiApConfigStore */
4945                case CMD_SET_AP_CONFIG:
4946                case CMD_SET_AP_CONFIG_COMPLETED:
4947                case CMD_REQUEST_AP_CONFIG:
4948                case CMD_RESPONSE_AP_CONFIG:
4949                case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
4950                case WifiWatchdogStateMachine.GOOD_LINK_DETECTED:
4951                case CMD_NO_NETWORKS_PERIODIC_SCAN:
4952                case CMD_DISABLE_P2P_RSP:
4953                case WifiMonitor.SUP_REQUEST_IDENTITY:
4954                case CMD_TEST_NETWORK_DISCONNECT:
4955                case CMD_OBTAINING_IP_ADDRESS_WATCHDOG_TIMER:
4956                case WifiMonitor.SUP_REQUEST_SIM_AUTH:
4957                case CMD_TARGET_BSSID:
4958                case CMD_AUTO_CONNECT:
4959                case CMD_AUTO_ROAM:
4960                case CMD_AUTO_SAVE_NETWORK:
4961                case CMD_ASSOCIATED_BSSID:
4962                case CMD_UNWANTED_NETWORK:
4963                case CMD_DISCONNECTING_WATCHDOG_TIMER:
4964                case CMD_ROAM_WATCHDOG_TIMER:
4965                case CMD_DISABLE_EPHEMERAL_NETWORK:
4966                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
4967                    break;
4968                case DhcpStateMachine.CMD_ON_QUIT:
4969                    mDhcpStateMachine = null;
4970                    break;
4971                case CMD_SET_SUSPEND_OPT_ENABLED:
4972                    if (message.arg1 == 1) {
4973                        mSuspendWakeLock.release();
4974                        setSuspendOptimizations(SUSPEND_DUE_TO_SCREEN, true);
4975                    } else {
4976                        setSuspendOptimizations(SUSPEND_DUE_TO_SCREEN, false);
4977                    }
4978                    break;
4979                case WifiMonitor.DRIVER_HUNG_EVENT:
4980                    setSupplicantRunning(false);
4981                    setSupplicantRunning(true);
4982                    break;
4983                case WifiManager.CONNECT_NETWORK:
4984                    replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
4985                            WifiManager.BUSY);
4986                    break;
4987                case WifiManager.FORGET_NETWORK:
4988                    replyToMessage(message, WifiManager.FORGET_NETWORK_FAILED,
4989                            WifiManager.BUSY);
4990                    break;
4991                case WifiManager.SAVE_NETWORK:
4992                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
4993                    replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED,
4994                            WifiManager.BUSY);
4995                    break;
4996                case WifiManager.START_WPS:
4997                    replyToMessage(message, WifiManager.WPS_FAILED,
4998                            WifiManager.BUSY);
4999                    break;
5000                case WifiManager.CANCEL_WPS:
5001                    replyToMessage(message, WifiManager.CANCEL_WPS_FAILED,
5002                            WifiManager.BUSY);
5003                    break;
5004                case WifiManager.DISABLE_NETWORK:
5005                    replyToMessage(message, WifiManager.DISABLE_NETWORK_FAILED,
5006                            WifiManager.BUSY);
5007                    break;
5008                case WifiManager.RSSI_PKTCNT_FETCH:
5009                    replyToMessage(message, WifiManager.RSSI_PKTCNT_FETCH_FAILED,
5010                            WifiManager.BUSY);
5011                    break;
5012                case CMD_GET_SUPPORTED_FEATURES:
5013                    if (WifiNative.startHal()) {
5014                        int featureSet = WifiNative.getSupportedFeatureSet();
5015                        replyToMessage(message, message.what, featureSet);
5016                    } else {
5017                        replyToMessage(message, message.what, 0);
5018                    }
5019                    break;
5020                case CMD_GET_LINK_LAYER_STATS:
5021                    // Not supported hence reply with error message
5022                    replyToMessage(message, message.what, null);
5023                    break;
5024                case WifiP2pServiceImpl.P2P_CONNECTION_CHANGED:
5025                    NetworkInfo info = (NetworkInfo) message.obj;
5026                    mP2pConnected.set(info.isConnected());
5027                    break;
5028                case WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST:
5029                    mTemporarilyDisconnectWifi = (message.arg1 == 1);
5030                    replyToMessage(message, WifiP2pServiceImpl.DISCONNECT_WIFI_RESPONSE);
5031                    break;
5032                /* Link configuration (IP address, DNS, ...) changes notified via netlink */
5033                case CMD_UPDATE_LINKPROPERTIES:
5034                    updateLinkProperties(CMD_UPDATE_LINKPROPERTIES);
5035                    break;
5036                case CMD_IP_CONFIGURATION_SUCCESSFUL:
5037                case CMD_IP_CONFIGURATION_LOST:
5038                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
5039                    break;
5040                case CMD_GET_CONNECTION_STATISTICS:
5041                    replyToMessage(message, message.what, mWifiConnectionStatistics);
5042                    break;
5043                default:
5044                    loge("Error! unhandled message" + message);
5045                    break;
5046            }
5047            return HANDLED;
5048        }
5049    }
5050
5051    class InitialState extends State {
5052        @Override
5053        public void enter() {
5054            mWifiNative.unloadDriver();
5055
5056            if (mWifiP2pChannel == null) {
5057                mWifiP2pChannel = new AsyncChannel();
5058                mWifiP2pChannel.connect(mContext, getHandler(),
5059                    mWifiP2pServiceImpl.getP2pStateMachineMessenger());
5060            }
5061
5062            if (mWifiApConfigChannel == null) {
5063                mWifiApConfigChannel = new AsyncChannel();
5064                WifiApConfigStore wifiApConfigStore = WifiApConfigStore.makeWifiApConfigStore(
5065                        mContext, getHandler());
5066                wifiApConfigStore.loadApConfiguration();
5067                mWifiApConfigChannel.connectSync(mContext, getHandler(),
5068                        wifiApConfigStore.getMessenger());
5069            }
5070        }
5071        @Override
5072        public boolean processMessage(Message message) {
5073            logStateAndMessage(message, getClass().getSimpleName());
5074            switch (message.what) {
5075                case CMD_START_SUPPLICANT:
5076                    if (mWifiNative.loadDriver()) {
5077                        try {
5078                            mNwService.wifiFirmwareReload(mInterfaceName, "STA");
5079                        } catch (Exception e) {
5080                            loge("Failed to reload STA firmware " + e);
5081                            // Continue
5082                        }
5083
5084                        try {
5085                            // A runtime crash can leave the interface up and
5086                            // IP addresses configured, and this affects
5087                            // connectivity when supplicant starts up.
5088                            // Ensure interface is down and we have no IP
5089                            // addresses before a supplicant start.
5090                            mNwService.setInterfaceDown(mInterfaceName);
5091                            mNwService.clearInterfaceAddresses(mInterfaceName);
5092
5093                            // Set privacy extensions
5094                            mNwService.setInterfaceIpv6PrivacyExtensions(mInterfaceName, true);
5095
5096                           // IPv6 is enabled only as long as access point is connected since:
5097                           // - IPv6 addresses and routes stick around after disconnection
5098                           // - kernel is unaware when connected and fails to start IPv6 negotiation
5099                           // - kernel can start autoconfiguration when 802.1x is not complete
5100                            mNwService.disableIpv6(mInterfaceName);
5101                        } catch (RemoteException re) {
5102                            loge("Unable to change interface settings: " + re);
5103                        } catch (IllegalStateException ie) {
5104                            loge("Unable to change interface settings: " + ie);
5105                        }
5106
5107                       /* Stop a running supplicant after a runtime restart
5108                        * Avoids issues with drivers that do not handle interface down
5109                        * on a running supplicant properly.
5110                        */
5111                        mWifiMonitor.killSupplicant(mP2pSupported);
5112                        if(mWifiNative.startSupplicant(mP2pSupported)) {
5113                            setWifiState(WIFI_STATE_ENABLING);
5114                            if (DBG) log("Supplicant start successful");
5115                            mWifiMonitor.startMonitoring();
5116                            transitionTo(mSupplicantStartingState);
5117                        } else {
5118                            loge("Failed to start supplicant!");
5119                        }
5120                    } else {
5121                        loge("Failed to load driver");
5122                    }
5123                    break;
5124                case CMD_START_AP:
5125                    if (mWifiNative.loadDriver()) {
5126                        setWifiApState(WIFI_AP_STATE_ENABLING);
5127                        transitionTo(mSoftApStartingState);
5128                    } else {
5129                        loge("Failed to load driver for softap");
5130                    }
5131                    break;
5132                default:
5133                    return NOT_HANDLED;
5134            }
5135            return HANDLED;
5136        }
5137    }
5138
5139    class SupplicantStartingState extends State {
5140        private void initializeWpsDetails() {
5141            String detail;
5142            detail = SystemProperties.get("ro.product.name", "");
5143            if (!mWifiNative.setDeviceName(detail)) {
5144                loge("Failed to set device name " +  detail);
5145            }
5146            detail = SystemProperties.get("ro.product.manufacturer", "");
5147            if (!mWifiNative.setManufacturer(detail)) {
5148                loge("Failed to set manufacturer " + detail);
5149            }
5150            detail = SystemProperties.get("ro.product.model", "");
5151            if (!mWifiNative.setModelName(detail)) {
5152                loge("Failed to set model name " + detail);
5153            }
5154            detail = SystemProperties.get("ro.product.model", "");
5155            if (!mWifiNative.setModelNumber(detail)) {
5156                loge("Failed to set model number " + detail);
5157            }
5158            detail = SystemProperties.get("ro.serialno", "");
5159            if (!mWifiNative.setSerialNumber(detail)) {
5160                loge("Failed to set serial number " + detail);
5161            }
5162            if (!mWifiNative.setConfigMethods("physical_display virtual_push_button")) {
5163                loge("Failed to set WPS config methods");
5164            }
5165            if (!mWifiNative.setDeviceType(mPrimaryDeviceType)) {
5166                loge("Failed to set primary device type " + mPrimaryDeviceType);
5167            }
5168        }
5169
5170        @Override
5171        public boolean processMessage(Message message) {
5172            logStateAndMessage(message, getClass().getSimpleName());
5173
5174            switch(message.what) {
5175                case WifiMonitor.SUP_CONNECTION_EVENT:
5176                    if (DBG) log("Supplicant connection established");
5177                    setWifiState(WIFI_STATE_ENABLED);
5178                    mSupplicantRestartCount = 0;
5179                    /* Reset the supplicant state to indicate the supplicant
5180                     * state is not known at this time */
5181                    mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
5182                    /* Initialize data structures */
5183                    mLastBssid = null;
5184                    mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
5185                    mLastSignalLevel = -1;
5186
5187                    mWifiInfo.setMacAddress(mWifiNative.getMacAddress());
5188                    mWifiNative.enableSaveConfig();
5189                    mWifiConfigStore.loadAndEnableAllNetworks();
5190                    if (mWifiConfigStore.enableVerboseLogging.get() > 0) {
5191                        enableVerboseLogging(mWifiConfigStore.enableVerboseLogging.get());
5192                    }
5193                    if (mWifiConfigStore.associatedPartialScanPeriodMilli.get() < 0) {
5194                        mWifiConfigStore.associatedPartialScanPeriodMilli.set(0);
5195                    }
5196                    initializeWpsDetails();
5197
5198                    sendSupplicantConnectionChangedBroadcast(true);
5199                    transitionTo(mDriverStartedState);
5200                    break;
5201                case WifiMonitor.SUP_DISCONNECTION_EVENT:
5202                    if (++mSupplicantRestartCount <= SUPPLICANT_RESTART_TRIES) {
5203                        loge("Failed to setup control channel, restart supplicant");
5204                        mWifiMonitor.killSupplicant(mP2pSupported);
5205                        transitionTo(mInitialState);
5206                        sendMessageDelayed(CMD_START_SUPPLICANT, SUPPLICANT_RESTART_INTERVAL_MSECS);
5207                    } else {
5208                        loge("Failed " + mSupplicantRestartCount +
5209                                " times to start supplicant, unload driver");
5210                        mSupplicantRestartCount = 0;
5211                        setWifiState(WIFI_STATE_UNKNOWN);
5212                        transitionTo(mInitialState);
5213                    }
5214                    break;
5215                case CMD_START_SUPPLICANT:
5216                case CMD_STOP_SUPPLICANT:
5217                case CMD_START_AP:
5218                case CMD_STOP_AP:
5219                case CMD_START_DRIVER:
5220                case CMD_STOP_DRIVER:
5221                case CMD_SET_OPERATIONAL_MODE:
5222                case CMD_SET_COUNTRY_CODE:
5223                case CMD_SET_FREQUENCY_BAND:
5224                case CMD_START_PACKET_FILTERING:
5225                case CMD_STOP_PACKET_FILTERING:
5226                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_DEFERRED;
5227                    deferMessage(message);
5228                    break;
5229                default:
5230                    return NOT_HANDLED;
5231            }
5232            return HANDLED;
5233        }
5234    }
5235
5236    class SupplicantStartedState extends State {
5237        @Override
5238        public void enter() {
5239            /* Wifi is available as long as we have a connection to supplicant */
5240            mNetworkInfo.setIsAvailable(true);
5241            if (mNetworkAgent != null) mNetworkAgent.sendNetworkInfo(mNetworkInfo);
5242
5243            int defaultInterval = mContext.getResources().getInteger(
5244                    R.integer.config_wifi_supplicant_scan_interval);
5245
5246            mSupplicantScanIntervalMs = Settings.Global.getLong(mContext.getContentResolver(),
5247                    Settings.Global.WIFI_SUPPLICANT_SCAN_INTERVAL_MS,
5248                    defaultInterval);
5249
5250            mWifiNative.setScanInterval((int)mSupplicantScanIntervalMs / 1000);
5251            mWifiNative.setExternalSim(true);
5252
5253            /* turn on use of DFS channels */
5254            WifiNative.setDfsFlag(true);
5255
5256            /* set country code */
5257            setCountryCode();
5258
5259            setRandomMacOui();
5260            mWifiNative.enableAutoConnect(false);
5261        }
5262
5263        @Override
5264        public boolean processMessage(Message message) {
5265            logStateAndMessage(message, getClass().getSimpleName());
5266
5267            switch(message.what) {
5268                case CMD_STOP_SUPPLICANT:   /* Supplicant stopped by user */
5269                    if (mP2pSupported) {
5270                        transitionTo(mWaitForP2pDisableState);
5271                    } else {
5272                        transitionTo(mSupplicantStoppingState);
5273                    }
5274                    break;
5275                case WifiMonitor.SUP_DISCONNECTION_EVENT:  /* Supplicant connection lost */
5276                    loge("Connection lost, restart supplicant");
5277                    handleSupplicantConnectionLoss();
5278                    handleNetworkDisconnect();
5279                    mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
5280                    if (mP2pSupported) {
5281                        transitionTo(mWaitForP2pDisableState);
5282                    } else {
5283                        transitionTo(mInitialState);
5284                    }
5285                    sendMessageDelayed(CMD_START_SUPPLICANT, SUPPLICANT_RESTART_INTERVAL_MSECS);
5286                    break;
5287                case WifiMonitor.SCAN_RESULTS_EVENT:
5288                    maybeRegisterNetworkFactory(); // Make sure our NetworkFactory is registered
5289                    closeRadioScanStats();
5290                    noteScanEnd();
5291                    setScanResults();
5292                    if (mIsFullScanOngoing || mSendScanResultsBroadcast) {
5293                        /* Just updated results from full scan, let apps know about this */
5294                        sendScanResultsAvailableBroadcast();
5295                    }
5296                    mSendScanResultsBroadcast = false;
5297                    mIsScanOngoing = false;
5298                    mIsFullScanOngoing = false;
5299                    if (mBufferedScanMsg.size() > 0)
5300                        sendMessage(mBufferedScanMsg.remove());
5301                    break;
5302                case CMD_PING_SUPPLICANT:
5303                    boolean ok = mWifiNative.ping();
5304                    replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
5305                    break;
5306                case CMD_GET_CAPABILITY_FREQ:
5307                    String freqs = mWifiNative.getFreqCapability();
5308                    replyToMessage(message, message.what, freqs);
5309                    break;
5310                case CMD_START_AP:
5311                    /* Cannot start soft AP while in client mode */
5312                    loge("Failed to start soft AP with a running supplicant");
5313                    setWifiApState(WIFI_AP_STATE_FAILED);
5314                    break;
5315                case CMD_SET_OPERATIONAL_MODE:
5316                    mOperationalMode = message.arg1;
5317                    mWifiConfigStore.
5318                            setLastSelectedConfiguration(WifiConfiguration.INVALID_NETWORK_ID);
5319                    break;
5320                case CMD_TARGET_BSSID:
5321                    // Trying to associate to this BSSID
5322                    if (message.obj != null) {
5323                        mTargetRoamBSSID = (String) message.obj;
5324                    }
5325                    break;
5326                case CMD_GET_LINK_LAYER_STATS:
5327                    WifiLinkLayerStats stats = getWifiLinkLayerStats(DBG);
5328                    if (stats == null) {
5329                        // When firmware doesnt support link layer stats, return an empty object
5330                        stats = new WifiLinkLayerStats();
5331                    }
5332                    replyToMessage(message, message.what, stats);
5333                    break;
5334                case CMD_SET_COUNTRY_CODE:
5335                    String country = (String) message.obj;
5336
5337                    final boolean persist = (message.arg2 == 1);
5338                    final int sequence = message.arg1;
5339
5340                    if (sequence != mCountryCodeSequence.get()) {
5341                        if (DBG) log("set country code ignored due to sequnce num");
5342                        break;
5343                    }
5344                    if (DBG) log("set country code " + country);
5345                    country = country.toUpperCase(Locale.ROOT);
5346
5347                    if (mDriverSetCountryCode == null || !mDriverSetCountryCode.equals(country)) {
5348                        if (mWifiNative.setCountryCode(country)) {
5349                            mDriverSetCountryCode = country;
5350                        } else {
5351                            loge("Failed to set country code " + country);
5352                        }
5353                    }
5354
5355                    mWifiP2pChannel.sendMessage(WifiP2pServiceImpl.SET_COUNTRY_CODE, country);
5356                    break;
5357                default:
5358                    return NOT_HANDLED;
5359            }
5360            return HANDLED;
5361        }
5362
5363        @Override
5364        public void exit() {
5365            mNetworkInfo.setIsAvailable(false);
5366            if (mNetworkAgent != null) mNetworkAgent.sendNetworkInfo(mNetworkInfo);
5367        }
5368    }
5369
5370    class SupplicantStoppingState extends State {
5371        @Override
5372        public void enter() {
5373            /* Send any reset commands to supplicant before shutting it down */
5374            handleNetworkDisconnect();
5375            if (mDhcpStateMachine != null) {
5376                mDhcpStateMachine.doQuit();
5377            }
5378
5379            String suppState = System.getProperty("init.svc.wpa_supplicant");
5380            if (suppState == null) suppState = "unknown";
5381            String p2pSuppState = System.getProperty("init.svc.p2p_supplicant");
5382            if (p2pSuppState == null) p2pSuppState = "unknown";
5383
5384            loge("SupplicantStoppingState: stopSupplicant "
5385                    + " init.svc.wpa_supplicant=" + suppState
5386                    + " init.svc.p2p_supplicant=" + p2pSuppState);
5387            mWifiMonitor.stopSupplicant();
5388
5389            /* Send ourselves a delayed message to indicate failure after a wait time */
5390            sendMessageDelayed(obtainMessage(CMD_STOP_SUPPLICANT_FAILED,
5391                    ++mSupplicantStopFailureToken, 0), SUPPLICANT_RESTART_INTERVAL_MSECS);
5392            setWifiState(WIFI_STATE_DISABLING);
5393            mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
5394        }
5395        @Override
5396        public boolean processMessage(Message message) {
5397            logStateAndMessage(message, getClass().getSimpleName());
5398
5399            switch(message.what) {
5400                case WifiMonitor.SUP_CONNECTION_EVENT:
5401                    loge("Supplicant connection received while stopping");
5402                    break;
5403                case WifiMonitor.SUP_DISCONNECTION_EVENT:
5404                    if (DBG) log("Supplicant connection lost");
5405                    handleSupplicantConnectionLoss();
5406                    transitionTo(mInitialState);
5407                    break;
5408                case CMD_STOP_SUPPLICANT_FAILED:
5409                    if (message.arg1 == mSupplicantStopFailureToken) {
5410                        loge("Timed out on a supplicant stop, kill and proceed");
5411                        handleSupplicantConnectionLoss();
5412                        transitionTo(mInitialState);
5413                    }
5414                    break;
5415                case CMD_START_SUPPLICANT:
5416                case CMD_STOP_SUPPLICANT:
5417                case CMD_START_AP:
5418                case CMD_STOP_AP:
5419                case CMD_START_DRIVER:
5420                case CMD_STOP_DRIVER:
5421                case CMD_SET_OPERATIONAL_MODE:
5422                case CMD_SET_COUNTRY_CODE:
5423                case CMD_SET_FREQUENCY_BAND:
5424                case CMD_START_PACKET_FILTERING:
5425                case CMD_STOP_PACKET_FILTERING:
5426                    deferMessage(message);
5427                    break;
5428                default:
5429                    return NOT_HANDLED;
5430            }
5431            return HANDLED;
5432        }
5433    }
5434
5435    class DriverStartingState extends State {
5436        private int mTries;
5437        @Override
5438        public void enter() {
5439            mTries = 1;
5440            /* Send ourselves a delayed message to start driver a second time */
5441            sendMessageDelayed(obtainMessage(CMD_DRIVER_START_TIMED_OUT,
5442                        ++mDriverStartToken, 0), DRIVER_START_TIME_OUT_MSECS);
5443        }
5444        @Override
5445        public boolean processMessage(Message message) {
5446            logStateAndMessage(message, getClass().getSimpleName());
5447
5448            switch(message.what) {
5449               case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
5450                    SupplicantState state = handleSupplicantStateChange(message);
5451                    /* If suplicant is exiting out of INTERFACE_DISABLED state into
5452                     * a state that indicates driver has started, it is ready to
5453                     * receive driver commands
5454                     */
5455                    if (SupplicantState.isDriverActive(state)) {
5456                        transitionTo(mDriverStartedState);
5457                    }
5458                    break;
5459                case CMD_DRIVER_START_TIMED_OUT:
5460                    if (message.arg1 == mDriverStartToken) {
5461                        if (mTries >= 2) {
5462                            loge("Failed to start driver after " + mTries);
5463                            transitionTo(mDriverStoppedState);
5464                        } else {
5465                            loge("Driver start failed, retrying");
5466                            mWakeLock.acquire();
5467                            mWifiNative.startDriver();
5468                            mWakeLock.release();
5469
5470                            ++mTries;
5471                            /* Send ourselves a delayed message to start driver again */
5472                            sendMessageDelayed(obtainMessage(CMD_DRIVER_START_TIMED_OUT,
5473                                        ++mDriverStartToken, 0), DRIVER_START_TIME_OUT_MSECS);
5474                        }
5475                    }
5476                    break;
5477                    /* Queue driver commands & connection events */
5478                case CMD_START_DRIVER:
5479                case CMD_STOP_DRIVER:
5480                case WifiMonitor.NETWORK_CONNECTION_EVENT:
5481                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
5482                case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
5483                case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
5484                case WifiMonitor.WPS_OVERLAP_EVENT:
5485                case CMD_SET_COUNTRY_CODE:
5486                case CMD_SET_FREQUENCY_BAND:
5487                case CMD_START_PACKET_FILTERING:
5488                case CMD_STOP_PACKET_FILTERING:
5489                case CMD_START_SCAN:
5490                case CMD_DISCONNECT:
5491                case CMD_REASSOCIATE:
5492                case CMD_RECONNECT:
5493                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_DEFERRED;
5494                    deferMessage(message);
5495                    break;
5496                case WifiMonitor.SCAN_RESULTS_EVENT:
5497                    // Loose scan results obtained in Driver Starting state, they can only confuse
5498                    // the state machine
5499                    break;
5500                default:
5501                    return NOT_HANDLED;
5502            }
5503            return HANDLED;
5504        }
5505    }
5506
5507    class DriverStartedState extends State {
5508        @Override
5509        public void enter() {
5510
5511            if (PDBG) {
5512                loge("DriverStartedState enter");
5513            }
5514            mIsRunning = true;
5515            mInDelayedStop = false;
5516            mDelayedStopCounter++;
5517            updateBatteryWorkSource(null);
5518            /**
5519             * Enable bluetooth coexistence scan mode when bluetooth connection is active.
5520             * When this mode is on, some of the low-level scan parameters used by the
5521             * driver are changed to reduce interference with bluetooth
5522             */
5523            mWifiNative.setBluetoothCoexistenceScanMode(mBluetoothConnectionActive);
5524            /* set frequency band of operation */
5525            setFrequencyBand();
5526            /* initialize network state */
5527            setNetworkDetailedState(DetailedState.DISCONNECTED);
5528
5529            /* Remove any filtering on Multicast v6 at start */
5530            mWifiNative.stopFilteringMulticastV6Packets();
5531
5532            /* Reset Multicast v4 filtering state */
5533            if (mFilteringMulticastV4Packets.get()) {
5534                mWifiNative.startFilteringMulticastV4Packets();
5535            } else {
5536                mWifiNative.stopFilteringMulticastV4Packets();
5537            }
5538
5539            mDhcpActive = false;
5540
5541            startBatchedScan();
5542
5543            if (mOperationalMode != CONNECT_MODE) {
5544                mWifiNative.disconnect();
5545                mWifiConfigStore.disableAllNetworks();
5546                if (mOperationalMode == SCAN_ONLY_WITH_WIFI_OFF_MODE) {
5547                    setWifiState(WIFI_STATE_DISABLED);
5548                }
5549                transitionTo(mScanModeState);
5550            } else {
5551
5552                // Status pulls in the current supplicant state and network connection state
5553                // events over the monitor connection. This helps framework sync up with
5554                // current supplicant state
5555                // TODO: actually check th supplicant status string and make sure the supplicant
5556                // is in disconnecte4d state.
5557                mWifiNative.status();
5558                // Transitioning to Disconnected state will trigger a scan and subsequently AutoJoin
5559                transitionTo(mDisconnectedState);
5560            }
5561
5562            // We may have missed screen update at boot
5563            if (mScreenBroadcastReceived.get() == false) {
5564                PowerManager powerManager = (PowerManager)mContext.getSystemService(
5565                        Context.POWER_SERVICE);
5566                handleScreenStateChanged(powerManager.isScreenOn(),
5567                        /* startBackgroundScanIfNeeded = */ false);
5568            } else {
5569                // Set the right suspend mode settings
5570                mWifiNative.setSuspendOptimizations(mSuspendOptNeedsDisabled == 0
5571                        && mUserWantsSuspendOpt.get());
5572            }
5573            mWifiNative.setPowerSave(true);
5574
5575            if (mP2pSupported) {
5576                if (mOperationalMode == CONNECT_MODE) {
5577                    mWifiP2pChannel.sendMessage(WifiStateMachine.CMD_ENABLE_P2P);
5578                } else {
5579                    // P2P statemachine starts in disabled state, and is not enabled until
5580                    // CMD_ENABLE_P2P is sent from here; so, nothing needs to be done to
5581                    // keep it disabled.
5582                }
5583            }
5584
5585            final Intent intent = new Intent(WifiManager.WIFI_SCAN_AVAILABLE);
5586            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
5587            intent.putExtra(WifiManager.EXTRA_SCAN_AVAILABLE, WIFI_STATE_ENABLED);
5588            mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
5589
5590            if (PDBG) {
5591                loge("Driverstarted State enter done");
5592            }
5593        }
5594
5595        @Override
5596        public boolean processMessage(Message message) {
5597            logStateAndMessage(message, getClass().getSimpleName());
5598
5599            switch(message.what) {
5600                case CMD_START_SCAN:
5601                    handleScanRequest(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, message);
5602                    break;
5603                case CMD_SET_BATCHED_SCAN:
5604                    if (recordBatchedScanSettings(message.arg1, message.arg2,
5605                            (Bundle) message.obj)) {
5606                        if (mBatchedScanSettings != null) {
5607                            startBatchedScan();
5608                        } else {
5609                            stopBatchedScan();
5610                        }
5611                    }
5612                    break;
5613                case CMD_SET_FREQUENCY_BAND:
5614                    int band =  message.arg1;
5615                    if (DBG) log("set frequency band " + band);
5616                    if (mWifiNative.setBand(band)) {
5617
5618                        if (PDBG)  loge("did set frequency band " + band);
5619
5620                        mFrequencyBand.set(band);
5621                        // Flush old data - like scan results
5622                        mWifiNative.bssFlush();
5623                        // Fetch the latest scan results when frequency band is set
5624                        startScanNative(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, null);
5625
5626                        if (PDBG)  loge("done set frequency band " + band);
5627
5628                    } else {
5629                        loge("Failed to set frequency band " + band);
5630                    }
5631                    break;
5632                case CMD_BLUETOOTH_ADAPTER_STATE_CHANGE:
5633                    mBluetoothConnectionActive = (message.arg1 !=
5634                            BluetoothAdapter.STATE_DISCONNECTED);
5635                    mWifiNative.setBluetoothCoexistenceScanMode(mBluetoothConnectionActive);
5636                    break;
5637                case CMD_STOP_DRIVER:
5638                    int mode = message.arg1;
5639
5640                    /* Already doing a delayed stop */
5641                    if (mInDelayedStop) {
5642                        if (DBG) log("Already in delayed stop");
5643                        break;
5644                    }
5645                    /* disconnect right now, but leave the driver running for a bit */
5646                    mWifiConfigStore.disableAllNetworks();
5647
5648                    mInDelayedStop = true;
5649                    mDelayedStopCounter++;
5650                    if (DBG) log("Delayed stop message " + mDelayedStopCounter);
5651
5652                    /* send regular delayed shut down */
5653                    Intent driverStopIntent = new Intent(ACTION_DELAYED_DRIVER_STOP, null);
5654                    driverStopIntent.setPackage(this.getClass().getPackage().getName());
5655                    driverStopIntent.putExtra(DELAYED_STOP_COUNTER, mDelayedStopCounter);
5656                    mDriverStopIntent = PendingIntent.getBroadcast(mContext,
5657                            DRIVER_STOP_REQUEST, driverStopIntent,
5658                            PendingIntent.FLAG_UPDATE_CURRENT);
5659
5660                    mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()
5661                            + mDriverStopDelayMs, mDriverStopIntent);
5662                    break;
5663                case CMD_START_DRIVER:
5664                    if (mInDelayedStop) {
5665                        mInDelayedStop = false;
5666                        mDelayedStopCounter++;
5667                        mAlarmManager.cancel(mDriverStopIntent);
5668                        if (DBG) log("Delayed stop ignored due to start");
5669                        if (mOperationalMode == CONNECT_MODE) {
5670                            mWifiConfigStore.enableAllNetworks();
5671                        }
5672                    }
5673                    break;
5674                case CMD_DELAYED_STOP_DRIVER:
5675                    if (DBG) log("delayed stop " + message.arg1 + " " + mDelayedStopCounter);
5676                    if (message.arg1 != mDelayedStopCounter) break;
5677                    if (getCurrentState() != mDisconnectedState) {
5678                        mWifiNative.disconnect();
5679                        handleNetworkDisconnect();
5680                    }
5681                    mWakeLock.acquire();
5682                    mWifiNative.stopDriver();
5683                    mWakeLock.release();
5684                    if (mP2pSupported) {
5685                        transitionTo(mWaitForP2pDisableState);
5686                    } else {
5687                        transitionTo(mDriverStoppingState);
5688                    }
5689                    break;
5690                case CMD_START_PACKET_FILTERING:
5691                    if (message.arg1 == MULTICAST_V6) {
5692                        mWifiNative.startFilteringMulticastV6Packets();
5693                    } else if (message.arg1 == MULTICAST_V4) {
5694                        mWifiNative.startFilteringMulticastV4Packets();
5695                    } else {
5696                        loge("Illegal arugments to CMD_START_PACKET_FILTERING");
5697                    }
5698                    break;
5699                case CMD_STOP_PACKET_FILTERING:
5700                    if (message.arg1 == MULTICAST_V6) {
5701                        mWifiNative.stopFilteringMulticastV6Packets();
5702                    } else if (message.arg1 == MULTICAST_V4) {
5703                        mWifiNative.stopFilteringMulticastV4Packets();
5704                    } else {
5705                        loge("Illegal arugments to CMD_STOP_PACKET_FILTERING");
5706                    }
5707                    break;
5708                case CMD_SET_SUSPEND_OPT_ENABLED:
5709                    if (message.arg1 == 1) {
5710                        setSuspendOptimizationsNative(SUSPEND_DUE_TO_SCREEN, true);
5711                        mSuspendWakeLock.release();
5712                    } else {
5713                        setSuspendOptimizationsNative(SUSPEND_DUE_TO_SCREEN, false);
5714                    }
5715                    break;
5716                case CMD_SET_HIGH_PERF_MODE:
5717                    if (message.arg1 == 1) {
5718                        setSuspendOptimizationsNative(SUSPEND_DUE_TO_HIGH_PERF, false);
5719                    } else {
5720                        setSuspendOptimizationsNative(SUSPEND_DUE_TO_HIGH_PERF, true);
5721                    }
5722                    break;
5723                case CMD_ENABLE_TDLS:
5724                    if (message.obj != null) {
5725                        String remoteAddress = (String) message.obj;
5726                        boolean enable = (message.arg1 == 1);
5727                        mWifiNative.startTdls(remoteAddress, enable);
5728                    }
5729                    break;
5730                default:
5731                    return NOT_HANDLED;
5732            }
5733            return HANDLED;
5734        }
5735        @Override
5736        public void exit() {
5737            mIsRunning = false;
5738            updateBatteryWorkSource(null);
5739            mScanResults = new ArrayList<>();
5740
5741            stopBatchedScan();
5742
5743            final Intent intent = new Intent(WifiManager.WIFI_SCAN_AVAILABLE);
5744            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
5745            intent.putExtra(WifiManager.EXTRA_SCAN_AVAILABLE, WIFI_STATE_DISABLED);
5746            mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
5747            noteScanEnd(); // wrap up any pending request.
5748            mBufferedScanMsg.clear();
5749        }
5750    }
5751
5752    class WaitForP2pDisableState extends State {
5753        private State mTransitionToState;
5754        @Override
5755        public void enter() {
5756            switch (getCurrentMessage().what) {
5757                case WifiMonitor.SUP_DISCONNECTION_EVENT:
5758                    mTransitionToState = mInitialState;
5759                    break;
5760                case CMD_DELAYED_STOP_DRIVER:
5761                    mTransitionToState = mDriverStoppingState;
5762                    break;
5763                case CMD_STOP_SUPPLICANT:
5764                    mTransitionToState = mSupplicantStoppingState;
5765                    break;
5766                default:
5767                    mTransitionToState = mDriverStoppingState;
5768                    break;
5769            }
5770            mWifiP2pChannel.sendMessage(WifiStateMachine.CMD_DISABLE_P2P_REQ);
5771        }
5772        @Override
5773        public boolean processMessage(Message message) {
5774            logStateAndMessage(message, getClass().getSimpleName());
5775
5776            switch(message.what) {
5777                case WifiStateMachine.CMD_DISABLE_P2P_RSP:
5778                    transitionTo(mTransitionToState);
5779                    break;
5780                /* Defer wifi start/shut and driver commands */
5781                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
5782                case CMD_START_SUPPLICANT:
5783                case CMD_STOP_SUPPLICANT:
5784                case CMD_START_AP:
5785                case CMD_STOP_AP:
5786                case CMD_START_DRIVER:
5787                case CMD_STOP_DRIVER:
5788                case CMD_SET_OPERATIONAL_MODE:
5789                case CMD_SET_COUNTRY_CODE:
5790                case CMD_SET_FREQUENCY_BAND:
5791                case CMD_START_PACKET_FILTERING:
5792                case CMD_STOP_PACKET_FILTERING:
5793                case CMD_START_SCAN:
5794                case CMD_DISCONNECT:
5795                case CMD_REASSOCIATE:
5796                case CMD_RECONNECT:
5797                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_DEFERRED;
5798                    deferMessage(message);
5799                    break;
5800                default:
5801                    return NOT_HANDLED;
5802            }
5803            return HANDLED;
5804        }
5805    }
5806
5807    class DriverStoppingState extends State {
5808        @Override
5809        public boolean processMessage(Message message) {
5810            logStateAndMessage(message, getClass().getSimpleName());
5811
5812            switch(message.what) {
5813                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
5814                    SupplicantState state = handleSupplicantStateChange(message);
5815                    if (state == SupplicantState.INTERFACE_DISABLED) {
5816                        transitionTo(mDriverStoppedState);
5817                    }
5818                    break;
5819                    /* Queue driver commands */
5820                case CMD_START_DRIVER:
5821                case CMD_STOP_DRIVER:
5822                case CMD_SET_COUNTRY_CODE:
5823                case CMD_SET_FREQUENCY_BAND:
5824                case CMD_START_PACKET_FILTERING:
5825                case CMD_STOP_PACKET_FILTERING:
5826                case CMD_START_SCAN:
5827                case CMD_DISCONNECT:
5828                case CMD_REASSOCIATE:
5829                case CMD_RECONNECT:
5830                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_DEFERRED;
5831                    deferMessage(message);
5832                    break;
5833                default:
5834                    return NOT_HANDLED;
5835            }
5836            return HANDLED;
5837        }
5838    }
5839
5840    class DriverStoppedState extends State {
5841        @Override
5842        public boolean processMessage(Message message) {
5843            logStateAndMessage(message, getClass().getSimpleName());
5844            switch (message.what) {
5845                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
5846                    StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
5847                    SupplicantState state = stateChangeResult.state;
5848                    // A WEXT bug means that we can be back to driver started state
5849                    // unexpectedly
5850                    if (SupplicantState.isDriverActive(state)) {
5851                        transitionTo(mDriverStartedState);
5852                    }
5853                    break;
5854                case CMD_START_DRIVER:
5855                    mWakeLock.acquire();
5856                    mWifiNative.startDriver();
5857                    mWakeLock.release();
5858                    transitionTo(mDriverStartingState);
5859                    break;
5860                default:
5861                    return NOT_HANDLED;
5862            }
5863            return HANDLED;
5864        }
5865    }
5866
5867    class ScanModeState extends State {
5868        private int mLastOperationMode;
5869        @Override
5870        public void enter() {
5871            mLastOperationMode = mOperationalMode;
5872        }
5873        @Override
5874        public boolean processMessage(Message message) {
5875            logStateAndMessage(message, getClass().getSimpleName());
5876
5877            switch(message.what) {
5878                case CMD_SET_OPERATIONAL_MODE:
5879                    if (message.arg1 == CONNECT_MODE) {
5880
5881                        if (mLastOperationMode == SCAN_ONLY_WITH_WIFI_OFF_MODE) {
5882                            setWifiState(WIFI_STATE_ENABLED);
5883                            // Load and re-enable networks when going back to enabled state
5884                            // This is essential for networks to show up after restore
5885                            mWifiConfigStore.loadAndEnableAllNetworks();
5886                            mWifiP2pChannel.sendMessage(CMD_ENABLE_P2P);
5887                        } else {
5888                            mWifiConfigStore.enableAllNetworks();
5889                        }
5890
5891                        // Try autojoining with recent network already present in the cache
5892                        // If none are found then trigger a scan which will trigger autojoin
5893                        // upon reception of scan results event
5894                        if (!mWifiAutoJoinController.attemptAutoJoin()) {
5895                            startScan(ENABLE_WIFI, 0, null, null);
5896                        }
5897
5898                        // Loose last selection choice since user toggled WiFi
5899                        mWifiConfigStore.
5900                                setLastSelectedConfiguration(WifiConfiguration.INVALID_NETWORK_ID);
5901
5902                        mOperationalMode = CONNECT_MODE;
5903                        transitionTo(mDisconnectedState);
5904                    } else {
5905                        // Nothing to do
5906                        return HANDLED;
5907                    }
5908                    break;
5909                // Handle scan. All the connection related commands are
5910                // handled only in ConnectModeState
5911                case CMD_START_SCAN:
5912                    handleScanRequest(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, message);
5913                    break;
5914                default:
5915                    return NOT_HANDLED;
5916            }
5917            return HANDLED;
5918        }
5919    }
5920
5921
5922    String smToString(Message message) {
5923        return smToString(message.what);
5924    }
5925
5926    String smToString(int what) {
5927        String s = "unknown";
5928        switch (what) {
5929            case WifiMonitor.DRIVER_HUNG_EVENT:
5930                s = "DRIVER_HUNG_EVENT";
5931                break;
5932            case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
5933                s = "AsyncChannel.CMD_CHANNEL_HALF_CONNECTED";
5934                break;
5935            case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
5936                s = "AsyncChannel.CMD_CHANNEL_DISCONNECTED";
5937                break;
5938            case CMD_SET_FREQUENCY_BAND:
5939                s = "CMD_SET_FREQUENCY_BAND";
5940                break;
5941            case CMD_DELAYED_NETWORK_DISCONNECT:
5942                s = "CMD_DELAYED_NETWORK_DISCONNECT";
5943                break;
5944            case CMD_TEST_NETWORK_DISCONNECT:
5945                s = "CMD_TEST_NETWORK_DISCONNECT";
5946                break;
5947            case CMD_OBTAINING_IP_ADDRESS_WATCHDOG_TIMER:
5948                s = "CMD_OBTAINING_IP_ADDRESS_WATCHDOG_TIMER";
5949                break;
5950            case CMD_DISABLE_EPHEMERAL_NETWORK:
5951                s = "CMD_DISABLE_EPHEMERAL_NETWORK";
5952                break;
5953            case CMD_START_DRIVER:
5954                s = "CMD_START_DRIVER";
5955                break;
5956            case CMD_STOP_DRIVER:
5957                s = "CMD_STOP_DRIVER";
5958                break;
5959            case CMD_STOP_SUPPLICANT:
5960                s = "CMD_STOP_SUPPLICANT";
5961                break;
5962            case CMD_STOP_SUPPLICANT_FAILED:
5963                s = "CMD_STOP_SUPPLICANT_FAILED";
5964                break;
5965            case CMD_START_SUPPLICANT:
5966                s = "CMD_START_SUPPLICANT";
5967                break;
5968            case CMD_REQUEST_AP_CONFIG:
5969                s = "CMD_REQUEST_AP_CONFIG";
5970                break;
5971            case CMD_RESPONSE_AP_CONFIG:
5972                s = "CMD_RESPONSE_AP_CONFIG";
5973                break;
5974            case CMD_TETHER_STATE_CHANGE:
5975                s = "CMD_TETHER_STATE_CHANGE";
5976                break;
5977            case CMD_TETHER_NOTIFICATION_TIMED_OUT:
5978                s = "CMD_TETHER_NOTIFICATION_TIMED_OUT";
5979                break;
5980            case CMD_BLUETOOTH_ADAPTER_STATE_CHANGE:
5981                s = "CMD_BLUETOOTH_ADAPTER_STATE_CHANGE";
5982                break;
5983            case CMD_ADD_OR_UPDATE_NETWORK:
5984                s = "CMD_ADD_OR_UPDATE_NETWORK";
5985                break;
5986            case CMD_REMOVE_NETWORK:
5987                s = "CMD_REMOVE_NETWORK";
5988                break;
5989            case CMD_ENABLE_NETWORK:
5990                s = "CMD_ENABLE_NETWORK";
5991                break;
5992            case CMD_ENABLE_ALL_NETWORKS:
5993                s = "CMD_ENABLE_ALL_NETWORKS";
5994                break;
5995            case CMD_AUTO_CONNECT:
5996                s = "CMD_AUTO_CONNECT";
5997                break;
5998            case CMD_AUTO_ROAM:
5999                s = "CMD_AUTO_ROAM";
6000                break;
6001            case CMD_AUTO_SAVE_NETWORK:
6002                s = "CMD_AUTO_SAVE_NETWORK";
6003                break;
6004            case CMD_BOOT_COMPLETED:
6005                s = "CMD_BOOT_COMPLETED";
6006                break;
6007            case DhcpStateMachine.CMD_START_DHCP:
6008                s = "CMD_START_DHCP";
6009                break;
6010            case DhcpStateMachine.CMD_STOP_DHCP:
6011                s = "CMD_STOP_DHCP";
6012                break;
6013            case DhcpStateMachine.CMD_RENEW_DHCP:
6014                s = "CMD_RENEW_DHCP";
6015                break;
6016            case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
6017                s = "CMD_PRE_DHCP_ACTION";
6018                break;
6019            case DhcpStateMachine.CMD_POST_DHCP_ACTION:
6020                s = "CMD_POST_DHCP_ACTION";
6021                break;
6022            case DhcpStateMachine.CMD_PRE_DHCP_ACTION_COMPLETE:
6023                s = "CMD_PRE_DHCP_ACTION_COMPLETE";
6024                break;
6025            case DhcpStateMachine.CMD_ON_QUIT:
6026                s = "CMD_ON_QUIT";
6027                break;
6028            case WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST:
6029                s = "WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST";
6030                break;
6031            case WifiManager.DISABLE_NETWORK:
6032                s = "WifiManager.DISABLE_NETWORK";
6033                break;
6034            case CMD_BLACKLIST_NETWORK:
6035                s = "CMD_BLACKLIST_NETWORK";
6036                break;
6037            case CMD_CLEAR_BLACKLIST:
6038                s = "CMD_CLEAR_BLACKLIST";
6039                break;
6040            case CMD_SAVE_CONFIG:
6041                s = "CMD_SAVE_CONFIG";
6042                break;
6043            case CMD_GET_CONFIGURED_NETWORKS:
6044                s = "CMD_GET_CONFIGURED_NETWORKS";
6045                break;
6046            case CMD_GET_SUPPORTED_FEATURES:
6047                s = "CMD_GET_SUPPORTED_FEATURES";
6048                break;
6049            case CMD_UNWANTED_NETWORK:
6050                s = "CMD_UNWANTED_NETWORK";
6051                break;
6052            case CMD_NETWORK_STATUS:
6053                s = "CMD_NETWORK_STATUS";
6054                break;
6055            case CMD_GET_LINK_LAYER_STATS:
6056                s = "CMD_GET_LINK_LAYER_STATS";
6057                break;
6058            case CMD_GET_PRIVILEGED_CONFIGURED_NETWORKS:
6059                s = "CMD_GET_PRIVILEGED_CONFIGURED_NETWORKS";
6060                break;
6061            case CMD_DISCONNECT:
6062                s = "CMD_DISCONNECT";
6063                break;
6064            case CMD_RECONNECT:
6065                s = "CMD_RECONNECT";
6066                break;
6067            case CMD_REASSOCIATE:
6068                s = "CMD_REASSOCIATE";
6069                break;
6070            case CMD_GET_CONNECTION_STATISTICS:
6071                s = "CMD_GET_CONNECTION_STATISTICS";
6072                break;
6073            case CMD_SET_HIGH_PERF_MODE:
6074                s = "CMD_SET_HIGH_PERF_MODE";
6075                break;
6076            case CMD_SET_COUNTRY_CODE:
6077                s = "CMD_SET_COUNTRY_CODE";
6078                break;
6079            case CMD_ENABLE_RSSI_POLL:
6080                s = "CMD_ENABLE_RSSI_POLL";
6081                break;
6082            case CMD_RSSI_POLL:
6083                s = "CMD_RSSI_POLL";
6084                break;
6085            case CMD_START_PACKET_FILTERING:
6086                s = "CMD_START_PACKET_FILTERING";
6087                break;
6088            case CMD_STOP_PACKET_FILTERING:
6089                s = "CMD_STOP_PACKET_FILTERING";
6090                break;
6091            case CMD_SET_SUSPEND_OPT_ENABLED:
6092                s = "CMD_SET_SUSPEND_OPT_ENABLED";
6093                break;
6094            case CMD_NO_NETWORKS_PERIODIC_SCAN:
6095                s = "CMD_NO_NETWORKS_PERIODIC_SCAN";
6096                break;
6097            case CMD_SET_BATCHED_SCAN:
6098                s = "CMD_SET_BATCHED_SCAN";
6099                break;
6100            case CMD_START_NEXT_BATCHED_SCAN:
6101                s = "CMD_START_NEXT_BATCHED_SCAN";
6102                break;
6103            case CMD_POLL_BATCHED_SCAN:
6104                s = "CMD_POLL_BATCHED_SCAN";
6105                break;
6106            case CMD_UPDATE_LINKPROPERTIES:
6107                s = "CMD_UPDATE_LINKPROPERTIES";
6108                break;
6109            case CMD_RELOAD_TLS_AND_RECONNECT:
6110                s = "CMD_RELOAD_TLS_AND_RECONNECT";
6111                break;
6112            case WifiManager.CONNECT_NETWORK:
6113                s = "CONNECT_NETWORK";
6114                break;
6115            case WifiManager.SAVE_NETWORK:
6116                s = "SAVE_NETWORK";
6117                break;
6118            case WifiManager.FORGET_NETWORK:
6119                s = "FORGET_NETWORK";
6120                break;
6121            case WifiMonitor.SUP_CONNECTION_EVENT:
6122                s = "SUP_CONNECTION_EVENT";
6123                break;
6124            case WifiMonitor.SUP_DISCONNECTION_EVENT:
6125                s = "SUP_DISCONNECTION_EVENT";
6126                break;
6127            case WifiMonitor.SCAN_RESULTS_EVENT:
6128                s = "SCAN_RESULTS_EVENT";
6129                break;
6130            case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
6131                s = "SUPPLICANT_STATE_CHANGE_EVENT";
6132                break;
6133            case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
6134                s = "AUTHENTICATION_FAILURE_EVENT";
6135                break;
6136            case WifiMonitor.SSID_TEMP_DISABLED:
6137                s = "SSID_TEMP_DISABLED";
6138                break;
6139            case WifiMonitor.SSID_REENABLED:
6140                s = "SSID_REENABLED";
6141                break;
6142            case WifiMonitor.WPS_SUCCESS_EVENT:
6143                s = "WPS_SUCCESS_EVENT";
6144                break;
6145            case WifiMonitor.WPS_FAIL_EVENT:
6146                s = "WPS_FAIL_EVENT";
6147                break;
6148            case WifiMonitor.SUP_REQUEST_IDENTITY:
6149                s = "SUP_REQUEST_IDENTITY";
6150                break;
6151            case WifiMonitor.NETWORK_CONNECTION_EVENT:
6152                s = "NETWORK_CONNECTION_EVENT";
6153                break;
6154            case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
6155                s = "NETWORK_DISCONNECTION_EVENT";
6156                break;
6157            case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
6158                s = "ASSOCIATION_REJECTION_EVENT";
6159                break;
6160            case CMD_SET_OPERATIONAL_MODE:
6161                s = "CMD_SET_OPERATIONAL_MODE";
6162                break;
6163            case CMD_START_SCAN:
6164                s = "CMD_START_SCAN";
6165                break;
6166            case CMD_DISABLE_P2P_RSP:
6167                s = "CMD_DISABLE_P2P_RSP";
6168                break;
6169            case CMD_DISABLE_P2P_REQ:
6170                s = "CMD_DISABLE_P2P_REQ";
6171                break;
6172            case WifiWatchdogStateMachine.GOOD_LINK_DETECTED:
6173                s = "GOOD_LINK_DETECTED";
6174                break;
6175            case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
6176                s = "POOR_LINK_DETECTED";
6177                break;
6178            case WifiP2pServiceImpl.GROUP_CREATING_TIMED_OUT:
6179                s = "GROUP_CREATING_TIMED_OUT";
6180                break;
6181            case WifiP2pServiceImpl.P2P_CONNECTION_CHANGED:
6182                s = "P2P_CONNECTION_CHANGED";
6183                break;
6184            case WifiP2pServiceImpl.DISCONNECT_WIFI_RESPONSE:
6185                s = "P2P.DISCONNECT_WIFI_RESPONSE";
6186                break;
6187            case WifiP2pServiceImpl.SET_MIRACAST_MODE:
6188                s = "P2P.SET_MIRACAST_MODE";
6189                break;
6190            case WifiP2pServiceImpl.BLOCK_DISCOVERY:
6191                s = "P2P.BLOCK_DISCOVERY";
6192                break;
6193            case WifiP2pServiceImpl.SET_COUNTRY_CODE:
6194                s = "P2P.SET_COUNTRY_CODE";
6195                break;
6196            case WifiManager.CANCEL_WPS:
6197                s = "CANCEL_WPS";
6198                break;
6199            case WifiManager.CANCEL_WPS_FAILED:
6200                s = "CANCEL_WPS_FAILED";
6201                break;
6202            case WifiManager.CANCEL_WPS_SUCCEDED:
6203                s = "CANCEL_WPS_SUCCEDED";
6204                break;
6205            case WifiManager.START_WPS:
6206                s = "START_WPS";
6207                break;
6208            case WifiManager.START_WPS_SUCCEEDED:
6209                s = "START_WPS_SUCCEEDED";
6210                break;
6211            case WifiManager.WPS_FAILED:
6212                s = "WPS_FAILED";
6213                break;
6214            case WifiManager.WPS_COMPLETED:
6215                s = "WPS_COMPLETED";
6216                break;
6217            case WifiManager.RSSI_PKTCNT_FETCH:
6218                s = "RSSI_PKTCNT_FETCH";
6219                break;
6220            case CMD_IP_CONFIGURATION_LOST:
6221                s = "CMD_IP_CONFIGURATION_LOST";
6222                break;
6223            case CMD_IP_CONFIGURATION_SUCCESSFUL:
6224                s = "CMD_IP_CONFIGURATION_SUCCESSFUL";
6225                break;
6226            case CMD_STATIC_IP_SUCCESS:
6227                s = "CMD_STATIC_IP_SUCCESSFUL";
6228                break;
6229            case CMD_STATIC_IP_FAILURE:
6230                s = "CMD_STATIC_IP_FAILURE";
6231                break;
6232            case DhcpStateMachine.DHCP_SUCCESS:
6233                s = "DHCP_SUCCESS";
6234                break;
6235            case DhcpStateMachine.DHCP_FAILURE:
6236                s = "DHCP_FAILURE";
6237                break;
6238            case CMD_TARGET_BSSID:
6239                s = "CMD_TARGET_BSSID";
6240                break;
6241            case CMD_ASSOCIATED_BSSID:
6242                s = "CMD_ASSOCIATED_BSSID";
6243                break;
6244            case CMD_ROAM_WATCHDOG_TIMER:
6245                s = "CMD_ROAM_WATCHDOG_TIMER";
6246                break;
6247            case CMD_SCREEN_STATE_CHANGED:
6248                s = "CMD_SCREEN_STATE_CHANGED";
6249                break;
6250            case CMD_DISCONNECTING_WATCHDOG_TIMER:
6251                s = "CMD_DISCONNECTING_WATCHDOG_TIMER";
6252                break;
6253            default:
6254                s = "what:" + Integer.toString(what);
6255                break;
6256        }
6257        return s;
6258    }
6259
6260    void registerConnected() {
6261       if (mLastNetworkId != WifiConfiguration.INVALID_NETWORK_ID) {
6262           long now_ms = System.currentTimeMillis();
6263           // We are switching away from this configuration,
6264           // hence record the time we were connected last
6265           WifiConfiguration config = mWifiConfigStore.getWifiConfiguration(mLastNetworkId);
6266           if (config != null) {
6267               config.lastConnected = System.currentTimeMillis();
6268               config.autoJoinBailedDueToLowRssi = false;
6269               config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED);
6270               config.numConnectionFailures = 0;
6271               config.numIpConfigFailures = 0;
6272               config.numAuthFailures = 0;
6273               config.numAssociation++;
6274           }
6275           mBadLinkspeedcount = 0;
6276       }
6277    }
6278
6279    void registerDisconnected() {
6280        if (mLastNetworkId != WifiConfiguration.INVALID_NETWORK_ID) {
6281            long now_ms = System.currentTimeMillis();
6282            // We are switching away from this configuration,
6283            // hence record the time we were connected last
6284            WifiConfiguration config = mWifiConfigStore.getWifiConfiguration(mLastNetworkId);
6285            if (config != null) {
6286                config.lastDisconnected = System.currentTimeMillis();
6287                if (config.ephemeral) {
6288                    // Remove ephemeral WifiConfigurations from file
6289                    mWifiConfigStore.forgetNetwork(mLastNetworkId);
6290                }
6291            }
6292        }
6293    }
6294
6295    void noteWifiDisabledWhileAssociated() {
6296        // We got disabled by user while we were associated, make note of it
6297        int rssi = mWifiInfo.getRssi();
6298        WifiConfiguration config = getCurrentWifiConfiguration();
6299        if (getCurrentState() == mConnectedState
6300                && rssi != WifiInfo.INVALID_RSSI
6301                && config != null) {
6302            boolean is24GHz = mWifiInfo.is24GHz();
6303            boolean isBadRSSI = (is24GHz && rssi < mWifiConfigStore.thresholdBadRssi24.get())
6304                    || (!is24GHz && rssi < mWifiConfigStore.thresholdBadRssi5.get());
6305            boolean isLowRSSI = (is24GHz && rssi < mWifiConfigStore.thresholdLowRssi24.get())
6306                    || (!is24GHz && mWifiInfo.getRssi() < mWifiConfigStore.thresholdLowRssi5.get());
6307            boolean isHighRSSI = (is24GHz && rssi >= mWifiConfigStore.thresholdGoodRssi24.get())
6308                    || (!is24GHz && mWifiInfo.getRssi() >= mWifiConfigStore.thresholdGoodRssi5.get());
6309            if (isBadRSSI) {
6310                // Take note that we got disabled while RSSI was Bad
6311                config.numUserTriggeredWifiDisableLowRSSI++;
6312            } else if (isLowRSSI) {
6313                // Take note that we got disabled while RSSI was Low
6314                config.numUserTriggeredWifiDisableBadRSSI++;
6315            } else if (!isHighRSSI) {
6316                // Take note that we got disabled while RSSI was Not high
6317                config.numUserTriggeredWifiDisableNotHighRSSI++;
6318            }
6319        }
6320    }
6321
6322    WifiConfiguration getCurrentWifiConfiguration() {
6323        if (mLastNetworkId == WifiConfiguration.INVALID_NETWORK_ID) {
6324            return null;
6325        }
6326        return mWifiConfigStore.getWifiConfiguration(mLastNetworkId);
6327    }
6328
6329    ScanResult getCurrentScanResult() {
6330        WifiConfiguration config = getCurrentWifiConfiguration();
6331        if (config == null) {
6332            return null;
6333        }
6334        String BSSID = mWifiInfo.getBSSID();
6335        if (BSSID == null) {
6336            BSSID = mTargetRoamBSSID;
6337        }
6338        if (config.scanResultCache == null) {
6339            return null;
6340        }
6341        return config.scanResultCache.get(BSSID);
6342    }
6343
6344    String getCurrentBSSID() {
6345        if (linkDebouncing) {
6346            return null;
6347        }
6348        return mLastBssid;
6349    }
6350
6351    class ConnectModeState extends State {
6352        @Override
6353        public boolean processMessage(Message message) {
6354            WifiConfiguration config;
6355            int netId;
6356            boolean ok;
6357            boolean didDisconnect;
6358            String bssid;
6359            String ssid;
6360            NetworkUpdateResult result;
6361            logStateAndMessage(message, getClass().getSimpleName());
6362
6363            switch (message.what) {
6364                case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
6365                    didBlackListBSSID = false;
6366                    bssid = (String) message.obj;
6367                    if (bssid == null || TextUtils.isEmpty(bssid)) {
6368                        // If BSSID is null, use the target roam BSSID
6369                        bssid = mTargetRoamBSSID;
6370                    }
6371                    if (bssid != null) {
6372                        // If we have a BSSID, tell configStore to black list it
6373                        synchronized(mScanResultCache) {
6374                            didBlackListBSSID = mWifiConfigStore.handleBSSIDBlackList
6375                                    (mLastNetworkId, bssid, false);
6376                        }
6377                    }
6378                    mSupplicantStateTracker.sendMessage(WifiMonitor.ASSOCIATION_REJECTION_EVENT);
6379                    break;
6380                case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
6381                    mSupplicantStateTracker.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT);
6382                    break;
6383                case WifiMonitor.SSID_TEMP_DISABLED:
6384                case WifiMonitor.SSID_REENABLED:
6385                    String substr = (String) message.obj;
6386                    String en = message.what == WifiMonitor.SSID_TEMP_DISABLED ?
6387                            "temp-disabled" : "re-enabled";
6388                    loge("ConnectModeState SSID state=" + en + " nid="
6389                            + Integer.toString(message.arg1) + " [" + substr + "]");
6390                    synchronized(mScanResultCache) {
6391                        mWifiConfigStore.handleSSIDStateChange(message.arg1, message.what ==
6392                                WifiMonitor.SSID_REENABLED, substr, mWifiInfo.getBSSID());
6393                    }
6394                    break;
6395                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
6396                    SupplicantState state = handleSupplicantStateChange(message);
6397                    // A driver/firmware hang can now put the interface in a down state.
6398                    // We detect the interface going down and recover from it
6399                    if (!SupplicantState.isDriverActive(state)) {
6400                        if (mNetworkInfo.getState() != NetworkInfo.State.DISCONNECTED) {
6401                            handleNetworkDisconnect();
6402                        }
6403                        log("Detected an interface down, restart driver");
6404                        transitionTo(mDriverStoppedState);
6405                        sendMessage(CMD_START_DRIVER);
6406                        break;
6407                    }
6408
6409                    // Supplicant can fail to report a NETWORK_DISCONNECTION_EVENT
6410                    // when authentication times out after a successful connection,
6411                    // we can figure this from the supplicant state. If supplicant
6412                    // state is DISCONNECTED, but the mNetworkInfo says we are not
6413                    // disconnected, we need to handle a disconnection
6414                    if (!linkDebouncing && state == SupplicantState.DISCONNECTED &&
6415                            mNetworkInfo.getState() != NetworkInfo.State.DISCONNECTED) {
6416                        if (DBG) log("Missed CTRL-EVENT-DISCONNECTED, disconnect");
6417                        handleNetworkDisconnect();
6418                        transitionTo(mDisconnectedState);
6419                    }
6420                    break;
6421                case WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST:
6422                    if (message.arg1 == 1) {
6423                        mWifiNative.disconnect();
6424                        mTemporarilyDisconnectWifi = true;
6425                    } else {
6426                        mWifiNative.reconnect();
6427                        mTemporarilyDisconnectWifi = false;
6428                    }
6429                    break;
6430                case CMD_ADD_OR_UPDATE_NETWORK:
6431                    config = (WifiConfiguration) message.obj;
6432                    int res = mWifiConfigStore.addOrUpdateNetwork(config, message.sendingUid);
6433                    if (res < 0) {
6434                        messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
6435                    } else {
6436                        WifiConfiguration curConfig = getCurrentWifiConfiguration();
6437                        if (curConfig != null && config != null) {
6438                            if (curConfig.priority < config.priority
6439                                    && config.status == WifiConfiguration.Status.ENABLED) {
6440                                // Interpret this as a connect attempt
6441                                // Set the last selected configuration so as to allow the system to
6442                                // stick the last user choice without persisting the choice
6443                                mWifiConfigStore.setLastSelectedConfiguration(res);
6444
6445                                // Remember time of last connection attempt
6446                                lastConnectAttempt = System.currentTimeMillis();
6447
6448                                mWifiConnectionStatistics.numWifiManagerJoinAttempt++;
6449
6450                                // As a courtesy to the caller, trigger a scan now
6451                                startScan(ADD_OR_UPDATE_SOURCE, 0, null, null);
6452                            }
6453                        }
6454                    }
6455                    replyToMessage(message, CMD_ADD_OR_UPDATE_NETWORK, res);
6456                    break;
6457                case CMD_REMOVE_NETWORK:
6458                    ok = mWifiConfigStore.removeNetwork(message.arg1);
6459                    if (!ok) {
6460                        messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
6461                    }
6462                    replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
6463                    break;
6464                case CMD_ENABLE_NETWORK:
6465                    boolean others = message.arg2 == 1;
6466                    // Tell autojoin the user did try to select to that network
6467                    // However, do NOT persist the choice by bumping the priority of the network
6468                    if (others) {
6469                        mWifiAutoJoinController.
6470                                updateConfigurationHistory(message.arg1, true, false);
6471                        // Set the last selected configuration so as to allow the system to
6472                        // stick the last user choice without persisting the choice
6473                        mWifiConfigStore.setLastSelectedConfiguration(message.arg1);
6474
6475                        // Remember time of last connection attempt
6476                        lastConnectAttempt = System.currentTimeMillis();
6477
6478                        mWifiConnectionStatistics.numWifiManagerJoinAttempt++;
6479                    }
6480                    // Cancel auto roam requests
6481                    autoRoamSetBSSID(message.arg1, "any");
6482
6483                    ok = mWifiConfigStore.enableNetwork(message.arg1, message.arg2 == 1);
6484                    if (!ok) {
6485                        messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
6486                    }
6487                    replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
6488                    break;
6489                case CMD_ENABLE_ALL_NETWORKS:
6490                    long time = android.os.SystemClock.elapsedRealtime();
6491                    if (time - mLastEnableAllNetworksTime > MIN_INTERVAL_ENABLE_ALL_NETWORKS_MS) {
6492                        mWifiConfigStore.enableAllNetworks();
6493                        mLastEnableAllNetworksTime = time;
6494                    }
6495                    break;
6496                case WifiManager.DISABLE_NETWORK:
6497                    if (mWifiConfigStore.disableNetwork(message.arg1,
6498                            WifiConfiguration.DISABLED_BY_WIFI_MANAGER) == true) {
6499                        replyToMessage(message, WifiManager.DISABLE_NETWORK_SUCCEEDED);
6500                    } else {
6501                        messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
6502                        replyToMessage(message, WifiManager.DISABLE_NETWORK_FAILED,
6503                                WifiManager.ERROR);
6504                    }
6505                    break;
6506                case CMD_DISABLE_EPHEMERAL_NETWORK:
6507                    config = mWifiConfigStore.disableEphemeralNetwork((String)message.obj);
6508                    if (config != null) {
6509                        if (config.networkId == mLastNetworkId) {
6510                            // Disconnect and let autojoin reselect a new network
6511                            sendMessage(CMD_DISCONNECT);
6512                        }
6513                    }
6514                    break;
6515                case CMD_BLACKLIST_NETWORK:
6516                    mWifiNative.addToBlacklist((String) message.obj);
6517                    break;
6518                case CMD_CLEAR_BLACKLIST:
6519                    mWifiNative.clearBlacklist();
6520                    break;
6521                case CMD_SAVE_CONFIG:
6522                    ok = mWifiConfigStore.saveConfig();
6523
6524                    if (DBG) loge("wifistatemachine did save config " + ok);
6525                    replyToMessage(message, CMD_SAVE_CONFIG, ok ? SUCCESS : FAILURE);
6526
6527                    // Inform the backup manager about a data change
6528                    IBackupManager ibm = IBackupManager.Stub.asInterface(
6529                            ServiceManager.getService(Context.BACKUP_SERVICE));
6530                    if (ibm != null) {
6531                        try {
6532                            ibm.dataChanged("com.android.providers.settings");
6533                        } catch (Exception e) {
6534                            // Try again later
6535                        }
6536                    }
6537                    break;
6538                case CMD_GET_CONFIGURED_NETWORKS:
6539                    replyToMessage(message, message.what,
6540                            mWifiConfigStore.getConfiguredNetworks());
6541                    break;
6542                case WifiMonitor.SUP_REQUEST_IDENTITY:
6543                    int networkId = message.arg2;
6544                    boolean identitySent = false;
6545                    int eapMethod = WifiEnterpriseConfig.Eap.NONE;
6546
6547                    if (targetWificonfiguration != null
6548                            && targetWificonfiguration.enterpriseConfig != null) {
6549                        eapMethod = targetWificonfiguration.enterpriseConfig.getEapMethod();
6550                    }
6551
6552                    // For SIM & AKA EAP method Only, get identity from ICC
6553                    if (targetWificonfiguration != null
6554                            && targetWificonfiguration.networkId == networkId
6555                            && targetWificonfiguration.allowedKeyManagement
6556                                    .get(WifiConfiguration.KeyMgmt.IEEE8021X)
6557                            &&  (eapMethod == WifiEnterpriseConfig.Eap.SIM
6558                            || eapMethod == WifiEnterpriseConfig.Eap.AKA)) {
6559                        TelephonyManager tm = (TelephonyManager)
6560                                mContext.getSystemService(Context.TELEPHONY_SERVICE);
6561                        if (tm != null) {
6562                            String imsi = tm.getSubscriberId();
6563                            String mccMnc = "";
6564
6565                            if (tm.getSimState() == TelephonyManager.SIM_STATE_READY)
6566                                 mccMnc = tm.getSimOperator();
6567
6568                            String identity = buildIdentity(eapMethod, imsi, mccMnc);
6569
6570                            if (!identity.isEmpty()) {
6571                                mWifiNative.simIdentityResponse(networkId, identity);
6572                                identitySent = true;
6573                            }
6574                        }
6575                    }
6576                    if (!identitySent) {
6577                        // Supplicant lacks credentials to connect to that network, hence black list
6578                        ssid = (String) message.obj;
6579                        if (targetWificonfiguration != null && ssid != null
6580                                && targetWificonfiguration.SSID != null
6581                                && targetWificonfiguration.SSID.equals("\"" + ssid + "\"")) {
6582                            mWifiConfigStore.handleSSIDStateChange(
6583                                    targetWificonfiguration.networkId, false,
6584                                    "AUTH_FAILED no identity", null);
6585                        }
6586                        // Disconnect now, as we don't have any way to fullfill
6587                        // the  supplicant request.
6588                        mWifiConfigStore.setLastSelectedConfiguration(
6589                                WifiConfiguration.INVALID_NETWORK_ID);
6590                        mWifiNative.disconnect();
6591                    }
6592                    break;
6593                case WifiMonitor.SUP_REQUEST_SIM_AUTH:
6594                    logd("Received SUP_REQUEST_SIM_AUTH");
6595                    SimAuthRequestData requestData = (SimAuthRequestData) message.obj;
6596                    if (requestData != null) {
6597                        if (requestData.protocol == WifiEnterpriseConfig.Eap.SIM) {
6598                            handleGsmAuthRequest(requestData);
6599                        } else if (requestData.protocol == WifiEnterpriseConfig.Eap.AKA) {
6600                            handle3GAuthRequest(requestData);
6601                        }
6602                    } else {
6603                        loge("Invalid sim auth request");
6604                    }
6605                    break;
6606                case CMD_GET_PRIVILEGED_CONFIGURED_NETWORKS:
6607                    replyToMessage(message, message.what,
6608                            mWifiConfigStore.getPrivilegedConfiguredNetworks());
6609                    break;
6610                    /* Do a redundant disconnect without transition */
6611                case CMD_DISCONNECT:
6612                    mWifiConfigStore.setLastSelectedConfiguration
6613                            (WifiConfiguration.INVALID_NETWORK_ID);
6614                    mWifiNative.disconnect();
6615                    break;
6616                case CMD_RECONNECT:
6617                    mWifiAutoJoinController.attemptAutoJoin();
6618                    break;
6619                case CMD_REASSOCIATE:
6620                    lastConnectAttempt = System.currentTimeMillis();
6621                    mWifiNative.reassociate();
6622                    break;
6623                case CMD_RELOAD_TLS_AND_RECONNECT:
6624                    if (mWifiConfigStore.needsUnlockedKeyStore()) {
6625                        logd("Reconnecting to give a chance to un-connected TLS networks");
6626                        mWifiNative.disconnect();
6627                        lastConnectAttempt = System.currentTimeMillis();
6628                        mWifiNative.reconnect();
6629                    }
6630                    break;
6631                case CMD_AUTO_ROAM:
6632                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
6633                    return HANDLED;
6634                case CMD_AUTO_CONNECT:
6635                    /* Work Around: wpa_supplicant can get in a bad state where it returns a non
6636                     * associated status to the STATUS command but somehow-someplace still thinks
6637                     * it is associated and thus will ignore select/reconnect command with
6638                     * following message:
6639                     * "Already associated with the selected network - do nothing"
6640                     *
6641                     * Hence, sends a disconnect to supplicant first.
6642                     */
6643                    didDisconnect = false;
6644                    if (getCurrentState() != mDisconnectedState) {
6645                        /** Supplicant will ignore the reconnect if we are currently associated,
6646                         * hence trigger a disconnect
6647                         */
6648                        didDisconnect = true;
6649                        mWifiNative.disconnect();
6650                    }
6651
6652                    /* connect command coming from auto-join */
6653                    config = (WifiConfiguration) message.obj;
6654                    netId = message.arg1;
6655                    int roam = message.arg2;
6656                    loge("CMD_AUTO_CONNECT sup state "
6657                            + mSupplicantStateTracker.getSupplicantStateName()
6658                            + " my state " + getCurrentState().getName()
6659                            + " nid=" + Integer.toString(netId)
6660                            + " roam=" + Integer.toString(roam));
6661                    if (config == null) {
6662                        loge("AUTO_CONNECT and no config, bail out...");
6663                        break;
6664                    }
6665
6666                    /* Make sure we cancel any previous roam request */
6667                    autoRoamSetBSSID(netId, config.BSSID);
6668
6669                    /* Save the network config */
6670                    loge("CMD_AUTO_CONNECT will save config -> " + config.SSID
6671                            + " nid=" + Integer.toString(netId));
6672                    result = mWifiConfigStore.saveNetwork(config, -1);
6673                    netId = result.getNetworkId();
6674                    loge("CMD_AUTO_CONNECT did save config -> "
6675                            + " nid=" + Integer.toString(netId));
6676
6677                    // Make sure the network is enabled, since supplicant will not reenable it
6678                    mWifiConfigStore.enableNetworkWithoutBroadcast(netId, false);
6679
6680                    if (mWifiConfigStore.selectNetwork(config, /* updatePriorities = */ false) &&
6681                            mWifiNative.reconnect()) {
6682                        lastConnectAttempt = System.currentTimeMillis();
6683                        targetWificonfiguration = mWifiConfigStore.getWifiConfiguration(netId);
6684                        config = mWifiConfigStore.getWifiConfiguration(netId);
6685                        if (config != null
6686                                && !mWifiConfigStore.isLastSelectedConfiguration(config)) {
6687                            // If we autojoined a different config than the user selected one,
6688                            // it means we could not see the last user selection,
6689                            // or that the last user selection was faulty and ended up blacklisted
6690                            // for some reason (in which case the user is notified with an error
6691                            // message in the Wifi picker), and thus we managed to auto-join away
6692                            // from the selected  config. -> in that case we need to forget
6693                            // the selection because we don't want to abruptly switch back to it.
6694                            //
6695                            // Note that the user selection is also forgotten after a period of time
6696                            // during which the device has been disconnected.
6697                            // The default value is 30 minutes : see the code path at bottom of
6698                            // setScanResults() function.
6699                            mWifiConfigStore.
6700                                 setLastSelectedConfiguration(WifiConfiguration.INVALID_NETWORK_ID);
6701                        }
6702                        mAutoRoaming = roam;
6703                        if (isRoaming() || linkDebouncing) {
6704                            transitionTo(mRoamingState);
6705                        } else if (didDisconnect) {
6706                            transitionTo(mDisconnectingState);
6707                        } else {
6708                            /* Already in disconnected state, nothing to change */
6709                        }
6710                    } else {
6711                        loge("Failed to connect config: " + config + " netId: " + netId);
6712                        replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
6713                                WifiManager.ERROR);
6714                        break;
6715                    }
6716                    break;
6717                case WifiManager.CONNECT_NETWORK:
6718                    /**
6719                     *  The connect message can contain a network id passed as arg1 on message or
6720                     * or a config passed as obj on message.
6721                     * For a new network, a config is passed to create and connect.
6722                     * For an existing network, a network id is passed
6723                     */
6724                    netId = message.arg1;
6725                    config = (WifiConfiguration) message.obj;
6726                    mWifiConnectionStatistics.numWifiManagerJoinAttempt++;
6727                    boolean updatedExisting = false;
6728
6729                    /* Save the network config */
6730                    if (config != null) {
6731                        String configKey = config.configKey(true /* allowCached */);
6732                        WifiConfiguration savedConfig =
6733                                mWifiConfigStore.getWifiConfiguration(configKey);
6734                        if (savedConfig != null) {
6735                            // There is an existing config with this netId, but it wasn't exposed
6736                            // (either AUTO_JOIN_DELETED or ephemeral; see WifiConfigStore#
6737                            // getConfiguredNetworks). Remove those bits and update the config.
6738                            config = savedConfig;
6739                            loge("CONNECT_NETWORK updating existing config with id=" +
6740                                    config.networkId + " configKey=" + configKey);
6741                            config.ephemeral = false;
6742                            config.autoJoinStatus = WifiConfiguration.AUTO_JOIN_ENABLED;
6743                            updatedExisting = true;
6744                        }
6745
6746                        result = mWifiConfigStore.saveNetwork(config, message.sendingUid);
6747                        netId = result.getNetworkId();
6748                    }
6749                    config = mWifiConfigStore.getWifiConfiguration(netId);
6750
6751                    if (config == null) {
6752                        loge("CONNECT_NETWORK no config for id=" + Integer.toString(netId) + " "
6753                                + mSupplicantStateTracker.getSupplicantStateName() + " my state "
6754                                + getCurrentState().getName());
6755                        replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
6756                                WifiManager.ERROR);
6757                        break;
6758                    } else {
6759                        String wasSkipped = config.autoJoinBailedDueToLowRssi ? " skipped" : "";
6760                        loge("CONNECT_NETWORK id=" + Integer.toString(netId)
6761                                + " config=" + config.SSID
6762                                + " cnid=" + config.networkId
6763                                + " supstate=" + mSupplicantStateTracker.getSupplicantStateName()
6764                                + " my state " + getCurrentState().getName()
6765                                + " uid = " + message.sendingUid
6766                                + wasSkipped);
6767                    }
6768
6769                    autoRoamSetBSSID(netId, "any");
6770
6771                    if (message.sendingUid == Process.WIFI_UID
6772                        || message.sendingUid == Process.SYSTEM_UID) {
6773                        // As a sanity measure, clear the BSSID in the supplicant network block.
6774                        // If system or Wifi Settings want to connect, they will not
6775                        // specify the BSSID.
6776                        // If an app however had added a BSSID to this configuration, and the BSSID
6777                        // was wrong, Then we would forever fail to connect until that BSSID
6778                        // is cleaned up.
6779                        clearConfigBSSID(config, "CONNECT_NETWORK");
6780                    }
6781
6782                    mAutoRoaming = WifiAutoJoinController.AUTO_JOIN_IDLE;
6783
6784                    /* Tell autojoin the user did try to connect to that network */
6785                    mWifiAutoJoinController.updateConfigurationHistory(netId, true, true);
6786
6787                    mWifiConfigStore.setLastSelectedConfiguration(netId);
6788
6789                    didDisconnect = false;
6790                    if (mLastNetworkId != WifiConfiguration.INVALID_NETWORK_ID
6791                            && mLastNetworkId != netId) {
6792                        /** Supplicant will ignore the reconnect if we are currently associated,
6793                         * hence trigger a disconnect
6794                         */
6795                        didDisconnect = true;
6796                        mWifiNative.disconnect();
6797                    }
6798
6799                    // Make sure the network is enabled, since supplicant will not reenable it
6800                    mWifiConfigStore.enableNetworkWithoutBroadcast(netId, false);
6801
6802                    if (mWifiConfigStore.selectNetwork(config, /* updatePriorities = */ true) &&
6803                            mWifiNative.reconnect()) {
6804                        lastConnectAttempt = System.currentTimeMillis();
6805                        targetWificonfiguration = mWifiConfigStore.getWifiConfiguration(netId);
6806
6807                        /* The state tracker handles enabling networks upon completion/failure */
6808                        mSupplicantStateTracker.sendMessage(WifiManager.CONNECT_NETWORK);
6809                        replyToMessage(message, WifiManager.CONNECT_NETWORK_SUCCEEDED);
6810                        if (didDisconnect) {
6811                            /* Expect a disconnection from the old connection */
6812                            transitionTo(mDisconnectingState);
6813                        } else if (updatedExisting && getCurrentState() == mConnectedState &&
6814                                getCurrentWifiConfiguration().networkId == netId) {
6815                            // Update the current set of network capabilities, but stay in the
6816                            // current state.
6817                            updateCapabilities(config);
6818                        } else {
6819                            /**
6820                             *  Directly go to disconnected state where we
6821                             * process the connection events from supplicant
6822                             **/
6823                            transitionTo(mDisconnectedState);
6824                        }
6825                    } else {
6826                        loge("Failed to connect config: " + config + " netId: " + netId);
6827                        replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
6828                                WifiManager.ERROR);
6829                        break;
6830                    }
6831                    break;
6832                case WifiManager.SAVE_NETWORK:
6833                    mWifiConnectionStatistics.numWifiManagerJoinAttempt++;
6834                    // Fall thru
6835                case WifiStateMachine.CMD_AUTO_SAVE_NETWORK:
6836                    lastSavedConfigurationAttempt = null; // Used for debug
6837                    config = (WifiConfiguration) message.obj;
6838                    if (config == null) {
6839                        loge("ERROR: SAVE_NETWORK with null configuration"
6840                                + mSupplicantStateTracker.getSupplicantStateName()
6841                                + " my state " + getCurrentState().getName());
6842                        messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
6843                        replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED,
6844                                WifiManager.ERROR);
6845                        break;
6846                    }
6847                    lastSavedConfigurationAttempt = new WifiConfiguration(config);
6848                    int nid = config.networkId;
6849                    loge("SAVE_NETWORK id=" + Integer.toString(nid)
6850                                + " config=" + config.SSID
6851                                + " nid=" + config.networkId
6852                                + " supstate=" + mSupplicantStateTracker.getSupplicantStateName()
6853                                + " my state " + getCurrentState().getName());
6854
6855                    result = mWifiConfigStore.saveNetwork(config, -1);
6856                    if (result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID) {
6857                        if (mWifiInfo.getNetworkId() == result.getNetworkId()) {
6858                            if (result.hasIpChanged()) {
6859                                // The currently connection configuration was changed
6860                                // We switched from DHCP to static or from static to DHCP, or the
6861                                // static IP address has changed.
6862                                log("Reconfiguring IP on connection");
6863                                // TODO: clear addresses and disable IPv6
6864                                // to simplify obtainingIpState.
6865                                transitionTo(mObtainingIpState);
6866                            }
6867                            if (result.hasProxyChanged()) {
6868                                log("Reconfiguring proxy on connection");
6869                                updateLinkProperties(CMD_UPDATE_LINKPROPERTIES);
6870                            }
6871                        }
6872                        replyToMessage(message, WifiManager.SAVE_NETWORK_SUCCEEDED);
6873                        if (VDBG) {
6874                           loge("Success save network nid="
6875                                        + Integer.toString(result.getNetworkId()));
6876                        }
6877
6878                        synchronized(mScanResultCache) {
6879                            /**
6880                             * If the command comes from WifiManager, then
6881                             * tell autojoin the user did try to modify and save that network,
6882                             * and interpret the SAVE_NETWORK as a request to connect
6883                             */
6884                            boolean user = message.what == WifiManager.SAVE_NETWORK;
6885                            mWifiAutoJoinController.updateConfigurationHistory(result.getNetworkId()
6886                                    , user, true);
6887                            mWifiAutoJoinController.attemptAutoJoin();
6888                        }
6889                    } else {
6890                        loge("Failed to save network");
6891                        messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
6892                        replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED,
6893                                WifiManager.ERROR);
6894                    }
6895                    break;
6896                case WifiManager.FORGET_NETWORK:
6897                    // Debug only, remember last configuration that was forgotten
6898                    WifiConfiguration toRemove
6899                            = mWifiConfigStore.getWifiConfiguration(message.arg1);
6900                    if (toRemove == null) {
6901                        lastForgetConfigurationAttempt = null;
6902                    } else {
6903                        lastForgetConfigurationAttempt = new WifiConfiguration(toRemove);
6904                    }
6905                    if (mWifiConfigStore.forgetNetwork(message.arg1)) {
6906                        replyToMessage(message, WifiManager.FORGET_NETWORK_SUCCEEDED);
6907                    } else {
6908                        loge("Failed to forget network");
6909                        replyToMessage(message, WifiManager.FORGET_NETWORK_FAILED,
6910                                WifiManager.ERROR);
6911                    }
6912                    break;
6913                case WifiManager.START_WPS:
6914                    WpsInfo wpsInfo = (WpsInfo) message.obj;
6915                    WpsResult wpsResult;
6916                    switch (wpsInfo.setup) {
6917                        case WpsInfo.PBC:
6918                            wpsResult = mWifiConfigStore.startWpsPbc(wpsInfo);
6919                            break;
6920                        case WpsInfo.KEYPAD:
6921                            wpsResult = mWifiConfigStore.startWpsWithPinFromAccessPoint(wpsInfo);
6922                            break;
6923                        case WpsInfo.DISPLAY:
6924                            wpsResult = mWifiConfigStore.startWpsWithPinFromDevice(wpsInfo);
6925                            break;
6926                        default:
6927                            wpsResult = new WpsResult(Status.FAILURE);
6928                            loge("Invalid setup for WPS");
6929                            break;
6930                    }
6931                    mWifiConfigStore.setLastSelectedConfiguration
6932                            (WifiConfiguration.INVALID_NETWORK_ID);
6933                    if (wpsResult.status == Status.SUCCESS) {
6934                        replyToMessage(message, WifiManager.START_WPS_SUCCEEDED, wpsResult);
6935                        transitionTo(mWpsRunningState);
6936                    } else {
6937                        loge("Failed to start WPS with config " + wpsInfo.toString());
6938                        replyToMessage(message, WifiManager.WPS_FAILED, WifiManager.ERROR);
6939                    }
6940                    break;
6941                case WifiMonitor.NETWORK_CONNECTION_EVENT:
6942                    if (DBG) log("Network connection established");
6943                    mLastNetworkId = message.arg1;
6944                    mLastBssid = (String) message.obj;
6945
6946                    mWifiInfo.setBSSID(mLastBssid);
6947                    mWifiInfo.setNetworkId(mLastNetworkId);
6948
6949                    sendNetworkStateChangeBroadcast(mLastBssid);
6950                    transitionTo(mObtainingIpState);
6951                    break;
6952                case WifiMonitor.ANQP_DONE_EVENT:
6953                    Log.d("HS2J", String.format("WFSM: ANQP for %016x %s",
6954                            (Long)message.obj, message.arg1 != 0 ? "success" : "fail"));
6955                    mWifiConfigStore.notifyANQPDone((Long) message.obj, message.arg1 != 0);
6956                    break;
6957                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
6958                    // Calling handleNetworkDisconnect here is redundant because we might already
6959                    // have called it when leaving L2ConnectedState to go to disconnecting state
6960                    // or thru other path
6961                    // We should normally check the mWifiInfo or mLastNetworkId so as to check
6962                    // if they are valid, and only in this case call handleNEtworkDisconnect,
6963                    // TODO: this should be fixed for a L MR release
6964                    // The side effect of calling handleNetworkDisconnect twice is that a bunch of
6965                    // idempotent commands are executed twice (stopping Dhcp, enabling the SPS mode
6966                    // at the chip etc...
6967                    if (DBG) log("ConnectModeState: Network connection lost ");
6968                    handleNetworkDisconnect();
6969                    transitionTo(mDisconnectedState);
6970                    break;
6971                default:
6972                    return NOT_HANDLED;
6973            }
6974            return HANDLED;
6975        }
6976    }
6977
6978    private void updateCapabilities(WifiConfiguration config) {
6979        if (config.ephemeral) {
6980            mNetworkCapabilities.removeCapability(
6981                    NetworkCapabilities.NET_CAPABILITY_TRUSTED);
6982        } else {
6983            mNetworkCapabilities.addCapability(
6984                    NetworkCapabilities.NET_CAPABILITY_TRUSTED);
6985        }
6986        mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
6987    }
6988
6989    private class WifiNetworkAgent extends NetworkAgent {
6990        public WifiNetworkAgent(Looper l, Context c, String TAG, NetworkInfo ni,
6991                NetworkCapabilities nc, LinkProperties lp, int score) {
6992            super(l, c, TAG, ni, nc, lp, score);
6993        }
6994        protected void unwanted() {
6995            // Ignore if we're not the current networkAgent.
6996            if (this != mNetworkAgent) return;
6997            if (DBG) log("WifiNetworkAgent -> Wifi unwanted score "
6998                    + Integer.toString(mWifiInfo.score));
6999            unwantedNetwork(network_status_unwanted_disconnect);
7000        }
7001
7002        protected void networkStatus(int status) {
7003            if (status == NetworkAgent.INVALID_NETWORK) {
7004                if (DBG) log("WifiNetworkAgent -> Wifi networkStatus invalid, score="
7005                        + Integer.toString(mWifiInfo.score));
7006                unwantedNetwork(network_status_unwanted_disable_autojoin);
7007            } else if (status == NetworkAgent.VALID_NETWORK) {
7008                if (DBG && mWifiInfo != null) log("WifiNetworkAgent -> Wifi networkStatus valid, score= "
7009                        + Integer.toString(mWifiInfo.score));
7010                doNetworkStatus(status);
7011            }
7012        }
7013    }
7014
7015    void unwantedNetwork(int reason) {
7016        sendMessage(CMD_UNWANTED_NETWORK, reason);
7017    }
7018
7019    void doNetworkStatus(int status) {
7020        sendMessage(CMD_NETWORK_STATUS, status);
7021    }
7022
7023    // rfc4186 & rfc4187:
7024    // create Permanent Identity base on IMSI,
7025    // identity = usernam@realm
7026    // with username = prefix | IMSI
7027    // and realm is derived MMC/MNC tuple according 3GGP spec(TS23.003)
7028    private String buildIdentity(int eapMethod, String imsi, String mccMnc) {
7029        String mcc;
7030        String mnc;
7031        String prefix;
7032
7033        if (imsi == null || imsi.isEmpty())
7034            return "";
7035
7036        if (eapMethod == WifiEnterpriseConfig.Eap.SIM)
7037            prefix = "1";
7038        else if (eapMethod == WifiEnterpriseConfig.Eap.AKA)
7039            prefix = "0";
7040        else  // not a valide EapMethod
7041            return "";
7042
7043        /* extract mcc & mnc from mccMnc */
7044        if (mccMnc != null && !mccMnc.isEmpty()) {
7045            mcc = mccMnc.substring(0, 3);
7046            mnc = mccMnc.substring(3);
7047            if (mnc.length() == 2)
7048                mnc = "0" + mnc;
7049        } else {
7050            // extract mcc & mnc from IMSI, assume mnc size is 3
7051            mcc = imsi.substring(0, 3);
7052            mnc = imsi.substring(3, 6);
7053        }
7054
7055        return prefix + imsi + "@wlan.mnc" + mnc + ".mcc" + mcc + ".3gppnetwork.org";
7056    }
7057
7058    boolean startScanForConfiguration(WifiConfiguration config, boolean restrictChannelList) {
7059        if (config == null)
7060            return false;
7061
7062        // We are still seeing a fairly high power consumption triggered by autojoin scans
7063        // Hence do partial scans only for PSK configuration that are roamable since the
7064        // primary purpose of the partial scans is roaming.
7065        // Full badn scans with exponential backoff for the purpose or extended roaming and
7066        // network switching are performed unconditionally.
7067        if (config.scanResultCache == null
7068                || !config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)
7069                || config.scanResultCache.size() > 6) {
7070            //return true but to not trigger the scan
7071            return true;
7072        }
7073        HashSet<Integer> channels
7074                = mWifiConfigStore.makeChannelList(config,
7075                ONE_HOUR_MILLI, restrictChannelList);
7076        if (channels != null && channels.size() != 0) {
7077            StringBuilder freqs = new StringBuilder();
7078            boolean first = true;
7079            for (Integer channel : channels) {
7080                if (!first)
7081                    freqs.append(",");
7082                freqs.append(channel.toString());
7083                first = false;
7084            }
7085            //if (DBG) {
7086            loge("WifiStateMachine starting scan for " + config.configKey() + " with " + freqs);
7087            //}
7088            // Call wifi native to start the scan
7089            if (startScanNative(
7090                    WifiNative.SCAN_WITHOUT_CONNECTION_SETUP,
7091                    freqs.toString())) {
7092                // Only count battery consumption if scan request is accepted
7093                noteScanStart(SCAN_ALARM_SOURCE, null);
7094                messageHandlingStatus = MESSAGE_HANDLING_STATUS_OK;
7095            } else {
7096                // used for debug only, mark scan as failed
7097                messageHandlingStatus = MESSAGE_HANDLING_STATUS_HANDLING_ERROR;
7098            }
7099            return true;
7100        } else {
7101            if (DBG) loge("WifiStateMachine no channels for " + config.configKey());
7102            return false;
7103        }
7104    }
7105
7106    void clearCurrentConfigBSSID(String dbg) {
7107        // Clear the bssid in the current config's network block
7108        WifiConfiguration config = getCurrentWifiConfiguration();
7109        if (config == null)
7110            return;
7111        clearConfigBSSID(config, dbg);
7112    }
7113    void clearConfigBSSID(WifiConfiguration config, String dbg) {
7114        if (config == null)
7115            return;
7116        if (DBG) {
7117            loge(dbg + " " + mTargetRoamBSSID + " config " + config.configKey()
7118                    + " config.bssid " + config.BSSID);
7119        }
7120        config.autoJoinBSSID = "any";
7121        config.BSSID = "any";
7122        if (DBG) {
7123           loge(dbg + " " + config.SSID
7124                    + " nid=" + Integer.toString(config.networkId));
7125        }
7126        mWifiConfigStore.saveWifiConfigBSSID(config);
7127    }
7128
7129    class L2ConnectedState extends State {
7130        @Override
7131        public void enter() {
7132            mRssiPollToken++;
7133            if (mEnableRssiPolling) {
7134                sendMessage(CMD_RSSI_POLL, mRssiPollToken, 0);
7135            }
7136            if (mNetworkAgent != null) {
7137                loge("Have NetworkAgent when entering L2Connected");
7138                setNetworkDetailedState(DetailedState.DISCONNECTED);
7139            }
7140            setNetworkDetailedState(DetailedState.CONNECTING);
7141
7142            if (TextUtils.isEmpty(mTcpBufferSizes) == false) {
7143                mLinkProperties.setTcpBufferSizes(mTcpBufferSizes);
7144            }
7145            mNetworkAgent = new WifiNetworkAgent(getHandler().getLooper(), mContext,
7146                    "WifiNetworkAgent", mNetworkInfo, mNetworkCapabilitiesFilter,
7147                    mLinkProperties, 60);
7148
7149            // We must clear the config BSSID, as the wifi chipset may decide to roam
7150            // from this point on and having the BSSID specified in the network block would
7151            // cause the roam to faile and the device to disconnect
7152            clearCurrentConfigBSSID("L2ConnectedState");
7153        }
7154
7155        @Override
7156        public void exit() {
7157            // This is handled by receiving a NETWORK_DISCONNECTION_EVENT in ConnectModeState
7158            // Bug: 15347363
7159            // For paranoia's sake, call handleNetworkDisconnect
7160            // only if BSSID is null or last networkId
7161            // is not invalid.
7162            if (DBG) {
7163                StringBuilder sb = new StringBuilder();
7164                sb.append("leaving L2ConnectedState state nid=" + Integer.toString(mLastNetworkId));
7165                if (mLastBssid !=null) {
7166                    sb.append(" ").append(mLastBssid);
7167                }
7168            }
7169            if (mLastBssid != null || mLastNetworkId != WifiConfiguration.INVALID_NETWORK_ID) {
7170                handleNetworkDisconnect();
7171            }
7172        }
7173
7174        @Override
7175        public boolean processMessage(Message message) {
7176            logStateAndMessage(message, getClass().getSimpleName());
7177
7178            switch (message.what) {
7179              case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
7180                  handlePreDhcpSetup();
7181                  break;
7182              case DhcpStateMachine.CMD_POST_DHCP_ACTION:
7183                  handlePostDhcpSetup();
7184                  if (message.arg1 == DhcpStateMachine.DHCP_SUCCESS) {
7185                      if (DBG) log("WifiStateMachine DHCP successful");
7186                      handleIPv4Success((DhcpResults) message.obj, DhcpStateMachine.DHCP_SUCCESS);
7187                      // We advance to mVerifyingLinkState because handleIPv4Success will call
7188                      // updateLinkProperties, which then sends CMD_IP_CONFIGURATION_SUCCESSFUL.
7189                  } else if (message.arg1 == DhcpStateMachine.DHCP_FAILURE) {
7190                      if (DBG) {
7191                          int count = -1;
7192                          WifiConfiguration config = getCurrentWifiConfiguration();
7193                          if (config != null) {
7194                              count = config.numConnectionFailures;
7195                          }
7196                          log("WifiStateMachine DHCP failure count=" + count);
7197                      }
7198                      handleIPv4Failure(DhcpStateMachine.DHCP_FAILURE);
7199                      // As above, we transition to mDisconnectingState via updateLinkProperties.
7200                  }
7201                  break;
7202                case CMD_IP_CONFIGURATION_SUCCESSFUL:
7203                    handleSuccessfulIpConfiguration();
7204                    sendConnectedState();
7205                    transitionTo(mConnectedState);
7206                    break;
7207                case CMD_IP_CONFIGURATION_LOST:
7208                    // Get Link layer stats so as we get fresh tx packet counters
7209                    getWifiLinkLayerStats(true);
7210                    handleIpConfigurationLost();
7211                    transitionTo(mDisconnectingState);
7212                    break;
7213                case CMD_DISCONNECT:
7214                    mWifiNative.disconnect();
7215                    transitionTo(mDisconnectingState);
7216                    break;
7217                case WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST:
7218                    if (message.arg1 == 1) {
7219                        mWifiNative.disconnect();
7220                        mTemporarilyDisconnectWifi = true;
7221                        transitionTo(mDisconnectingState);
7222                    }
7223                    break;
7224                case CMD_SET_OPERATIONAL_MODE:
7225                    if (message.arg1 != CONNECT_MODE) {
7226                        sendMessage(CMD_DISCONNECT);
7227                        deferMessage(message);
7228                        if (message.arg1 == SCAN_ONLY_WITH_WIFI_OFF_MODE) {
7229                            noteWifiDisabledWhileAssociated();
7230                        }
7231                    }
7232                    mWifiConfigStore.
7233                                setLastSelectedConfiguration(WifiConfiguration.INVALID_NETWORK_ID);
7234                    break;
7235                case CMD_SET_COUNTRY_CODE:
7236                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_DEFERRED;
7237                    deferMessage(message);
7238                    break;
7239                case CMD_START_SCAN:
7240                    //if (DBG) {
7241                        loge("WifiStateMachine CMD_START_SCAN source " + message.arg1
7242                              + " txSuccessRate="+String.format( "%.2f", mWifiInfo.txSuccessRate)
7243                              + " rxSuccessRate="+String.format( "%.2f", mWifiInfo.rxSuccessRate)
7244                              + " targetRoamBSSID=" + mTargetRoamBSSID
7245                              + " RSSI=" + mWifiInfo.getRssi());
7246                    //}
7247                    if (message.arg1 == SCAN_ALARM_SOURCE) {
7248                        // Check if the CMD_START_SCAN message is obsolete (and thus if it should
7249                        // not be processed) and restart the scan if needed
7250                        boolean shouldScan =
7251                                mScreenOn && mWifiConfigStore.enableAutoJoinScanWhenAssociated.get();
7252                        if (!checkAndRestartDelayedScan(message.arg2,
7253                                shouldScan,
7254                                mWifiConfigStore.associatedPartialScanPeriodMilli.get(), null, null)) {
7255                            messageHandlingStatus = MESSAGE_HANDLING_STATUS_OBSOLETE;
7256                            loge("WifiStateMachine L2Connected CMD_START_SCAN source "
7257                                    + message.arg1
7258                                    + " " + message.arg2 + ", " + mDelayedScanCounter
7259                                    + " -> obsolete");
7260                            return HANDLED;
7261                        }
7262                        if (mP2pConnected.get()) {
7263                            loge("WifiStateMachine L2Connected CMD_START_SCAN source "
7264                                    + message.arg1
7265                                    + " " + message.arg2 + ", " + mDelayedScanCounter
7266                                    + " ignore because P2P is connected");
7267                            messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
7268                            return HANDLED;
7269                        }
7270                        boolean tryFullBandScan = false;
7271                        boolean restrictChannelList = false;
7272                        long now_ms = System.currentTimeMillis();
7273                        if (DBG) {
7274                            loge("WifiStateMachine CMD_START_SCAN with age="
7275                                    + Long.toString(now_ms - lastFullBandConnectedTimeMilli)
7276                                    + " interval=" + fullBandConnectedTimeIntervalMilli
7277                                    + " maxinterval=" + maxFullBandConnectedTimeIntervalMilli);
7278                        }
7279                        if (mWifiInfo != null) {
7280                            if (mWifiConfigStore.enableFullBandScanWhenAssociated.get() &&
7281                                    (now_ms - lastFullBandConnectedTimeMilli)
7282                                    > fullBandConnectedTimeIntervalMilli) {
7283                                if (DBG) {
7284                                    loge("WifiStateMachine CMD_START_SCAN try full band scan age="
7285                                         + Long.toString(now_ms - lastFullBandConnectedTimeMilli)
7286                                         + " interval=" + fullBandConnectedTimeIntervalMilli
7287                                         + " maxinterval=" + maxFullBandConnectedTimeIntervalMilli);
7288                                }
7289                                tryFullBandScan = true;
7290                            }
7291
7292                            if (mWifiInfo.txSuccessRate >
7293                                    mWifiConfigStore.maxTxPacketForFullScans
7294                                    || mWifiInfo.rxSuccessRate >
7295                                    mWifiConfigStore.maxRxPacketForFullScans) {
7296                                // Too much traffic at the interface, hence no full band scan
7297                                if (DBG) {
7298                                    loge("WifiStateMachine CMD_START_SCAN " +
7299                                            "prevent full band scan due to pkt rate");
7300                                }
7301                                tryFullBandScan = false;
7302                            }
7303
7304                            if (mWifiInfo.txSuccessRate >
7305                                    mWifiConfigStore.maxTxPacketForPartialScans
7306                                    || mWifiInfo.rxSuccessRate >
7307                                    mWifiConfigStore.maxRxPacketForPartialScans) {
7308                                // Don't scan if lots of packets are being sent
7309                                restrictChannelList = true;
7310                                if (mWifiConfigStore.alwaysEnableScansWhileAssociated.get() == 0) {
7311                                    if (DBG) {
7312                                     loge("WifiStateMachine CMD_START_SCAN source " + message.arg1
7313                                        + " ...and ignore scans"
7314                                        + " tx=" + String.format("%.2f", mWifiInfo.txSuccessRate)
7315                                        + " rx=" + String.format("%.2f", mWifiInfo.rxSuccessRate));
7316                                    }
7317                                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_REFUSED;
7318                                    return HANDLED;
7319                                }
7320                            }
7321                        }
7322
7323                        WifiConfiguration currentConfiguration = getCurrentWifiConfiguration();
7324                        if (DBG) {
7325                            loge("WifiStateMachine CMD_START_SCAN full=" +
7326                                    tryFullBandScan);
7327                        }
7328                        if (currentConfiguration != null) {
7329                            if (fullBandConnectedTimeIntervalMilli
7330                                    < mWifiConfigStore.associatedPartialScanPeriodMilli.get()) {
7331                                // Sanity
7332                                fullBandConnectedTimeIntervalMilli
7333                                        = mWifiConfigStore.associatedPartialScanPeriodMilli.get();
7334                            }
7335                            if (tryFullBandScan) {
7336                                lastFullBandConnectedTimeMilli = now_ms;
7337                                if (fullBandConnectedTimeIntervalMilli
7338                                        < mWifiConfigStore.associatedFullScanMaxIntervalMilli) {
7339                                    // Increase the interval
7340                                    fullBandConnectedTimeIntervalMilli
7341                                            = fullBandConnectedTimeIntervalMilli
7342                                            * mWifiConfigStore.associatedFullScanBackoff.get() / 8;
7343
7344                                    if (DBG) {
7345                                        loge("WifiStateMachine CMD_START_SCAN bump interval ="
7346                                        + fullBandConnectedTimeIntervalMilli);
7347                                    }
7348                                }
7349                                handleScanRequest(
7350                                        WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, message);
7351                            } else {
7352                                if (!startScanForConfiguration(
7353                                        currentConfiguration, restrictChannelList)) {
7354                                    if (DBG) {
7355                                        loge("WifiStateMachine starting scan, " +
7356                                                " did not find channels -> full");
7357                                    }
7358                                    lastFullBandConnectedTimeMilli = now_ms;
7359                                    if (fullBandConnectedTimeIntervalMilli
7360                                            < mWifiConfigStore.associatedFullScanMaxIntervalMilli) {
7361                                        // Increase the interval
7362                                        fullBandConnectedTimeIntervalMilli
7363                                                = fullBandConnectedTimeIntervalMilli
7364                                                * mWifiConfigStore.associatedFullScanBackoff.get() / 8;
7365
7366                                        if (DBG) {
7367                                            loge("WifiStateMachine CMD_START_SCAN bump interval ="
7368                                                    + fullBandConnectedTimeIntervalMilli);
7369                                        }
7370                                    }
7371                                    handleScanRequest(
7372                                                WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, message);
7373                                }
7374                            }
7375
7376                        } else {
7377                            loge("CMD_START_SCAN : connected mode and no configuration");
7378                            messageHandlingStatus = MESSAGE_HANDLING_STATUS_HANDLING_ERROR;
7379                        }
7380                    } else {
7381                        // Not scan alarm source
7382                        return NOT_HANDLED;
7383                    }
7384                    break;
7385                    /* Ignore connection to same network */
7386                case WifiManager.CONNECT_NETWORK:
7387                    int netId = message.arg1;
7388                    if (mWifiInfo.getNetworkId() == netId) {
7389                        break;
7390                    }
7391                    return NOT_HANDLED;
7392                    /* Ignore */
7393                case WifiMonitor.NETWORK_CONNECTION_EVENT:
7394                    break;
7395                case CMD_RSSI_POLL:
7396                    if (message.arg1 == mRssiPollToken) {
7397                        if (mWifiConfigStore.enableChipWakeUpWhenAssociated.get()) {
7398                            if (VVDBG) log(" get link layer stats " + mWifiLinkLayerStatsSupported);
7399                            WifiLinkLayerStats stats = getWifiLinkLayerStats(VDBG);
7400                            if (stats != null) {
7401                                // Sanity check the results provided by driver
7402                                if (mWifiInfo.getRssi() != WifiInfo.INVALID_RSSI
7403                                        && (stats.rssi_mgmt == 0
7404                                        || stats.beacon_rx == 0)) {
7405                                    stats = null;
7406                                }
7407                            }
7408                            // Get Info and continue polling
7409                            fetchRssiLinkSpeedAndFrequencyNative();
7410                            calculateWifiScore(stats);
7411                        }
7412                        sendMessageDelayed(obtainMessage(CMD_RSSI_POLL,
7413                                mRssiPollToken, 0), POLL_RSSI_INTERVAL_MSECS);
7414
7415                        if (DBG) sendRssiChangeBroadcast(mWifiInfo.getRssi());
7416                    } else {
7417                        // Polling has completed
7418                    }
7419                    break;
7420                case CMD_ENABLE_RSSI_POLL:
7421                    if (mWifiConfigStore.enableRssiPollWhenAssociated.get()) {
7422                        mEnableRssiPolling = (message.arg1 == 1);
7423                    } else {
7424                        mEnableRssiPolling = false;
7425                    }
7426                    mRssiPollToken++;
7427                    if (mEnableRssiPolling) {
7428                        // First poll
7429                        fetchRssiLinkSpeedAndFrequencyNative();
7430                        sendMessageDelayed(obtainMessage(CMD_RSSI_POLL,
7431                                mRssiPollToken, 0), POLL_RSSI_INTERVAL_MSECS);
7432                    } else {
7433                        cleanWifiScore();
7434                    }
7435                    break;
7436                case WifiManager.RSSI_PKTCNT_FETCH:
7437                    RssiPacketCountInfo info = new RssiPacketCountInfo();
7438                    fetchRssiLinkSpeedAndFrequencyNative();
7439                    info.rssi = mWifiInfo.getRssi();
7440                    fetchPktcntNative(info);
7441                    replyToMessage(message, WifiManager.RSSI_PKTCNT_FETCH_SUCCEEDED, info);
7442                    break;
7443                case CMD_DELAYED_NETWORK_DISCONNECT:
7444                    if (!linkDebouncing && mWifiConfigStore.enableLinkDebouncing) {
7445
7446                        // Ignore if we are not debouncing
7447                        loge("CMD_DELAYED_NETWORK_DISCONNECT and not debouncing - ignore "
7448                                + message.arg1);
7449                        return HANDLED;
7450                    } else {
7451                        loge("CMD_DELAYED_NETWORK_DISCONNECT and debouncing - disconnect "
7452                                + message.arg1);
7453
7454                        linkDebouncing = false;
7455                        // If we are still debouncing while this message comes,
7456                        // it means we were not able to reconnect within the alloted time
7457                        // = LINK_FLAPPING_DEBOUNCE_MSEC
7458                        // and thus, trigger a real disconnect
7459                        handleNetworkDisconnect();
7460                        transitionTo(mDisconnectedState);
7461                    }
7462                    break;
7463                case CMD_ASSOCIATED_BSSID:
7464                    if ((String) message.obj == null) {
7465                        loge("Associated command w/o BSSID");
7466                        break;
7467                    }
7468                    mLastBssid = (String) message.obj;
7469                    if (mLastBssid != null
7470                            && (mWifiInfo.getBSSID() == null
7471                            || !mLastBssid.equals(mWifiInfo.getBSSID()))) {
7472                        mWifiInfo.setBSSID((String) message.obj);
7473                        sendNetworkStateChangeBroadcast(mLastBssid);
7474                    }
7475                    break;
7476                default:
7477                    return NOT_HANDLED;
7478            }
7479
7480            return HANDLED;
7481        }
7482    }
7483
7484    class ObtainingIpState extends State {
7485        @Override
7486        public void enter() {
7487            if (DBG) {
7488                String key = "";
7489                if (getCurrentWifiConfiguration() != null) {
7490                    key = getCurrentWifiConfiguration().configKey();
7491                }
7492                log("enter ObtainingIpState netId=" + Integer.toString(mLastNetworkId)
7493                        + " " + key + " "
7494                        + " roam=" + mAutoRoaming
7495                        + " static=" + mWifiConfigStore.isUsingStaticIp(mLastNetworkId)
7496                        + " watchdog= " + obtainingIpWatchdogCount);
7497            }
7498
7499            // Reset link Debouncing, indicating we have successfully re-connected to the AP
7500            // We might still be roaming
7501            linkDebouncing = false;
7502
7503            // Send event to CM & network change broadcast
7504            setNetworkDetailedState(DetailedState.OBTAINING_IPADDR);
7505
7506            // We must clear the config BSSID, as the wifi chipset may decide to roam
7507            // from this point on and having the BSSID specified in the network block would
7508            // cause the roam to faile and the device to disconnect
7509            clearCurrentConfigBSSID("ObtainingIpAddress");
7510
7511            try {
7512                mNwService.enableIpv6(mInterfaceName);
7513            } catch (RemoteException re) {
7514                loge("Failed to enable IPv6: " + re);
7515            } catch (IllegalStateException e) {
7516                loge("Failed to enable IPv6: " + e);
7517            }
7518
7519            if (!mWifiConfigStore.isUsingStaticIp(mLastNetworkId)) {
7520                if (isRoaming()) {
7521                    renewDhcp();
7522                } else {
7523                    // Remove any IP address on the interface in case we're switching from static
7524                    // IP configuration to DHCP. This is safe because if we get here when not
7525                    // roaming, we don't have a usable address.
7526                    clearIPv4Address(mInterfaceName);
7527                    startDhcp();
7528                }
7529                obtainingIpWatchdogCount++;
7530                loge("Start Dhcp Watchdog " + obtainingIpWatchdogCount);
7531                // Get Link layer stats so as we get fresh tx packet counters
7532                getWifiLinkLayerStats(true);
7533                sendMessageDelayed(obtainMessage(CMD_OBTAINING_IP_ADDRESS_WATCHDOG_TIMER,
7534                        obtainingIpWatchdogCount, 0), OBTAINING_IP_ADDRESS_GUARD_TIMER_MSEC);
7535            } else {
7536                // stop any running dhcp before assigning static IP
7537                stopDhcp();
7538                StaticIpConfiguration config = mWifiConfigStore.getStaticIpConfiguration(
7539                        mLastNetworkId);
7540                if (config.ipAddress == null) {
7541                    loge("Static IP lacks address");
7542                    sendMessage(CMD_STATIC_IP_FAILURE);
7543                } else {
7544                    InterfaceConfiguration ifcg = new InterfaceConfiguration();
7545                    ifcg.setLinkAddress(config.ipAddress);
7546                    ifcg.setInterfaceUp();
7547                    try {
7548                        mNwService.setInterfaceConfig(mInterfaceName, ifcg);
7549                        if (DBG) log("Static IP configuration succeeded");
7550                        DhcpResults dhcpResults = new DhcpResults(config);
7551                        sendMessage(CMD_STATIC_IP_SUCCESS, dhcpResults);
7552                    } catch (RemoteException re) {
7553                        loge("Static IP configuration failed: " + re);
7554                        sendMessage(CMD_STATIC_IP_FAILURE);
7555                    } catch (IllegalStateException e) {
7556                        loge("Static IP configuration failed: " + e);
7557                        sendMessage(CMD_STATIC_IP_FAILURE);
7558                    }
7559                }
7560            }
7561        }
7562      @Override
7563      public boolean processMessage(Message message) {
7564          logStateAndMessage(message, getClass().getSimpleName());
7565
7566          switch(message.what) {
7567              case CMD_STATIC_IP_SUCCESS:
7568                  handleIPv4Success((DhcpResults) message.obj, CMD_STATIC_IP_SUCCESS);
7569                  break;
7570              case CMD_STATIC_IP_FAILURE:
7571                  handleIPv4Failure(CMD_STATIC_IP_FAILURE);
7572                  break;
7573              case CMD_AUTO_CONNECT:
7574              case CMD_AUTO_ROAM:
7575                  messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
7576                  break;
7577              case WifiManager.SAVE_NETWORK:
7578              case WifiStateMachine.CMD_AUTO_SAVE_NETWORK:
7579                  messageHandlingStatus = MESSAGE_HANDLING_STATUS_DEFERRED;
7580                  deferMessage(message);
7581                  break;
7582                  /* Defer any power mode changes since we must keep active power mode at DHCP */
7583              case CMD_SET_HIGH_PERF_MODE:
7584                  messageHandlingStatus = MESSAGE_HANDLING_STATUS_DEFERRED;
7585                  deferMessage(message);
7586                  break;
7587                  /* Defer scan request since we should not switch to other channels at DHCP */
7588              case CMD_START_SCAN:
7589                  messageHandlingStatus = MESSAGE_HANDLING_STATUS_DEFERRED;
7590                  deferMessage(message);
7591                  break;
7592              case CMD_OBTAINING_IP_ADDRESS_WATCHDOG_TIMER:
7593                  if (message.arg1 == obtainingIpWatchdogCount) {
7594                      loge("ObtainingIpAddress: Watchdog Triggered, count="
7595                              + obtainingIpWatchdogCount);
7596                      handleIpConfigurationLost();
7597                      transitionTo(mDisconnectingState);
7598                      break;
7599                  }
7600                  messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
7601                  break;
7602              default:
7603                  return NOT_HANDLED;
7604          }
7605          return HANDLED;
7606      }
7607    }
7608
7609    class VerifyingLinkState extends State {
7610        @Override
7611        public void enter() {
7612            log(getName() + " enter");
7613            setNetworkDetailedState(DetailedState.VERIFYING_POOR_LINK);
7614            mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.VERIFYING_POOR_LINK);
7615            sendNetworkStateChangeBroadcast(mLastBssid);
7616            // End roaming
7617            mAutoRoaming = WifiAutoJoinController.AUTO_JOIN_IDLE;
7618        }
7619        @Override
7620        public boolean processMessage(Message message) {
7621            logStateAndMessage(message, getClass().getSimpleName());
7622
7623            switch (message.what) {
7624                case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
7625                    // Stay here
7626                    log(getName() + " POOR_LINK_DETECTED: no transition");
7627                    break;
7628                case WifiWatchdogStateMachine.GOOD_LINK_DETECTED:
7629                    log(getName() + " GOOD_LINK_DETECTED: transition to captive portal check");
7630
7631                    log(getName() + " GOOD_LINK_DETECTED: transition to CONNECTED");
7632                    sendConnectedState();
7633                    transitionTo(mConnectedState);
7634                    break;
7635                default:
7636                    if (DBG) log(getName() + " what=" + message.what + " NOT_HANDLED");
7637                    return NOT_HANDLED;
7638            }
7639            return HANDLED;
7640        }
7641    }
7642
7643    private void sendConnectedState() {
7644        // Send out a broadcast with the CAPTIVE_PORTAL_CHECK to preserve
7645        // existing behaviour. The captive portal check really happens after we
7646        // transition into DetailedState.CONNECTED.
7647        setNetworkDetailedState(DetailedState.CAPTIVE_PORTAL_CHECK);
7648        mWifiConfigStore.updateStatus(mLastNetworkId,
7649        DetailedState.CAPTIVE_PORTAL_CHECK);
7650        sendNetworkStateChangeBroadcast(mLastBssid);
7651
7652        if (mWifiConfigStore.getLastSelectedConfiguration() != null) {
7653            if (mNetworkAgent != null) mNetworkAgent.explicitlySelected();
7654        }
7655
7656        setNetworkDetailedState(DetailedState.CONNECTED);
7657        mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.CONNECTED);
7658        sendNetworkStateChangeBroadcast(mLastBssid);
7659    }
7660
7661    class RoamingState extends State {
7662        boolean mAssociated;
7663        @Override
7664        public void enter() {
7665            if (DBG) {
7666                log("RoamingState Enter"
7667                        + " mScreenOn=" + mScreenOn );
7668            }
7669            setScanAlarm(false);
7670
7671            // Make sure we disconnect if roaming fails
7672            roamWatchdogCount++;
7673            loge("Start Roam Watchdog " + roamWatchdogCount);
7674            sendMessageDelayed(obtainMessage(CMD_ROAM_WATCHDOG_TIMER,
7675                    roamWatchdogCount, 0), ROAM_GUARD_TIMER_MSEC);
7676            mAssociated = false;
7677        }
7678        @Override
7679        public boolean processMessage(Message message) {
7680            logStateAndMessage(message, getClass().getSimpleName());
7681            WifiConfiguration config;
7682            switch (message.what) {
7683                case CMD_IP_CONFIGURATION_LOST:
7684                    config = getCurrentWifiConfiguration();
7685                    if (config != null) {
7686                        mWifiConfigStore.noteRoamingFailure(config,
7687                                WifiConfiguration.ROAMING_FAILURE_IP_CONFIG);
7688                    }
7689                    return NOT_HANDLED;
7690               case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
7691                    if (DBG) log("Roaming and Watchdog reports poor link -> ignore");
7692                    return HANDLED;
7693               case CMD_UNWANTED_NETWORK:
7694                    if (DBG) log("Roaming and CS doesnt want the network -> ignore");
7695                    return HANDLED;
7696               case CMD_SET_OPERATIONAL_MODE:
7697                    if (message.arg1 != CONNECT_MODE) {
7698                        deferMessage(message);
7699                    }
7700                    break;
7701               case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
7702                    /**
7703                     * If we get a SUPPLICANT_STATE_CHANGE_EVENT indicating a DISCONNECT
7704                     * before NETWORK_DISCONNECTION_EVENT
7705                     * And there is an associated BSSID corresponding to our target BSSID, then
7706                     * we have missed the network disconnection, transition to mDisconnectedState
7707                     * and handle the rest of the events there.
7708                     */
7709                    StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
7710                    if (stateChangeResult.state == SupplicantState.DISCONNECTED
7711                            || stateChangeResult.state == SupplicantState.INACTIVE
7712                            || stateChangeResult.state == SupplicantState.INTERFACE_DISABLED) {
7713                        if (DBG) {
7714                            log("STATE_CHANGE_EVENT in roaming state "
7715                                    + stateChangeResult.toString() );
7716                        }
7717                        if (stateChangeResult.BSSID != null
7718                                && stateChangeResult.BSSID.equals(mTargetRoamBSSID)) {
7719                            handleNetworkDisconnect();
7720                            transitionTo(mDisconnectedState);
7721                        }
7722                    }
7723                    if (stateChangeResult.state == SupplicantState.ASSOCIATED) {
7724                        // We completed the layer2 roaming part
7725                        mAssociated = true;
7726                        if (stateChangeResult.BSSID != null) {
7727                            mTargetRoamBSSID = (String) stateChangeResult.BSSID;
7728                        }
7729                    }
7730                    break;
7731                case CMD_ROAM_WATCHDOG_TIMER:
7732                    if (roamWatchdogCount == message.arg1) {
7733                        if (DBG) log("roaming watchdog! -> disconnect");
7734                        mRoamFailCount++;
7735                        handleNetworkDisconnect();
7736                        mWifiNative.disconnect();
7737                        transitionTo(mDisconnectedState);
7738                    }
7739                    break;
7740               case WifiMonitor.NETWORK_CONNECTION_EVENT:
7741                   if (mAssociated) {
7742                       if (DBG) log("roaming and Network connection established");
7743                       mLastNetworkId = message.arg1;
7744                       mLastBssid = (String) message.obj;
7745                       mWifiInfo.setBSSID(mLastBssid);
7746                       mWifiInfo.setNetworkId(mLastNetworkId);
7747                       mWifiConfigStore.handleBSSIDBlackList(mLastNetworkId, mLastBssid, true);
7748                       sendNetworkStateChangeBroadcast(mLastBssid);
7749                       transitionTo(mObtainingIpState);
7750                   } else {
7751                       messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
7752                   }
7753                   break;
7754               case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
7755                   // Throw away but only if it corresponds to the network we're roaming to
7756                   String bssid = (String)message.obj;
7757                   if (true) {
7758                       String target = "";
7759                       if (mTargetRoamBSSID != null) target = mTargetRoamBSSID;
7760                       log("NETWORK_DISCONNECTION_EVENT in roaming state"
7761                               + " BSSID=" + bssid
7762                               + " target=" + target);
7763                   }
7764                   if (bssid != null && bssid.equals(mTargetRoamBSSID)) {
7765                       handleNetworkDisconnect();
7766                       transitionTo(mDisconnectedState);
7767                   }
7768                   break;
7769                case WifiMonitor.SSID_TEMP_DISABLED:
7770                    // Auth error while roaming
7771                    loge("SSID_TEMP_DISABLED nid=" + Integer.toString(mLastNetworkId)
7772                            + " id=" + Integer.toString(message.arg1)
7773                            + " isRoaming=" + isRoaming()
7774                            + " roam=" + Integer.toString(mAutoRoaming));
7775                    if (message.arg1 == mLastNetworkId) {
7776                        config = getCurrentWifiConfiguration();
7777                        if (config != null) {
7778                            mWifiConfigStore.noteRoamingFailure(config,
7779                                    WifiConfiguration.ROAMING_FAILURE_AUTH_FAILURE);
7780                        }
7781                        handleNetworkDisconnect();
7782                        transitionTo(mDisconnectingState);
7783                    }
7784                    return NOT_HANDLED;
7785                case CMD_START_SCAN:
7786                    deferMessage(message);
7787                    break;
7788                default:
7789                    return NOT_HANDLED;
7790            }
7791            return HANDLED;
7792        }
7793
7794        @Override
7795        public void exit() {
7796            loge("WifiStateMachine: Leaving Roaming state");
7797        }
7798    }
7799
7800    class ConnectedState extends State {
7801        @Override
7802        public void enter() {
7803            String address;
7804            updateDefaultRouteMacAddress(1000);
7805            if (DBG) {
7806                log("ConnectedState Enter "
7807                        + " mScreenOn=" + mScreenOn
7808                        + " scanperiod="
7809                        + Integer.toString(mWifiConfigStore.associatedPartialScanPeriodMilli.get()) );
7810            }
7811            if (mScreenOn
7812                    && mWifiConfigStore.enableAutoJoinScanWhenAssociated.get()) {
7813                // restart scan alarm
7814                startDelayedScan(mWifiConfigStore.associatedPartialScanPeriodMilli.get(), null, null);
7815            }
7816            registerConnected();
7817            lastConnectAttempt = 0;
7818            targetWificonfiguration = null;
7819            // Paranoia
7820            linkDebouncing = false;
7821
7822            // Not roaming anymore
7823            mAutoRoaming = WifiAutoJoinController.AUTO_JOIN_IDLE;
7824
7825            if (testNetworkDisconnect) {
7826                testNetworkDisconnectCounter++;
7827                loge("ConnectedState Enter start disconnect test " +
7828                        testNetworkDisconnectCounter);
7829                sendMessageDelayed(obtainMessage(CMD_TEST_NETWORK_DISCONNECT,
7830                        testNetworkDisconnectCounter, 0), 15000);
7831            }
7832
7833            // Reenable all networks, allow for hidden networks to be scanned
7834            mWifiConfigStore.enableAllNetworks();
7835
7836            mLastDriverRoamAttempt = 0;
7837        }
7838        @Override
7839        public boolean processMessage(Message message) {
7840            WifiConfiguration config = null;
7841            logStateAndMessage(message, getClass().getSimpleName());
7842
7843            switch (message.what) {
7844                case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
7845                    if (DBG) log("Watchdog reports poor link");
7846                    transitionTo(mVerifyingLinkState);
7847                    break;
7848                case CMD_UNWANTED_NETWORK:
7849                    if (message.arg1 == network_status_unwanted_disconnect) {
7850                        mWifiConfigStore.handleBadNetworkDisconnectReport(mLastNetworkId, mWifiInfo);
7851                        mWifiNative.disconnect();
7852                        transitionTo(mDisconnectingState);
7853                    } else if (message.arg1 == network_status_unwanted_disable_autojoin) {
7854                        config = getCurrentWifiConfiguration();
7855                        if (config != null) {
7856                            // Disable autojoin
7857                            config.numNoInternetAccessReports += 1;
7858                        }
7859                    }
7860                    return HANDLED;
7861                case CMD_NETWORK_STATUS:
7862                    if (message.arg1 == NetworkAgent.VALID_NETWORK) {
7863                        config = getCurrentWifiConfiguration();
7864                        if (config != null) {
7865                            // re-enable autojoin
7866                            config.numNoInternetAccessReports = 0;
7867                            config.validatedInternetAccess = true;
7868                        }
7869                    }
7870                    return HANDLED;
7871                case CMD_TEST_NETWORK_DISCONNECT:
7872                    // Force a disconnect
7873                    if (message.arg1 == testNetworkDisconnectCounter) {
7874                        mWifiNative.disconnect();
7875                    }
7876                    break;
7877                case CMD_ASSOCIATED_BSSID:
7878                    // ASSOCIATING to a new BSSID while already connected, indicates
7879                    // that driver is roaming
7880                    mLastDriverRoamAttempt = System.currentTimeMillis();
7881                    String toBSSID = (String)message.obj;
7882                    if (toBSSID != null && !toBSSID.equals(mWifiInfo.getBSSID())) {
7883                        mWifiConfigStore.driverRoamedFrom(mWifiInfo);
7884                    }
7885                    return NOT_HANDLED;
7886                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
7887                    long lastRoam = 0;
7888                    if (mLastDriverRoamAttempt != 0) {
7889                        // Calculate time since last driver roam attempt
7890                        lastRoam = System.currentTimeMillis() - mLastDriverRoamAttempt;
7891                        mLastDriverRoamAttempt = 0;
7892                    }
7893                    config = getCurrentWifiConfiguration();
7894                    if (mScreenOn
7895                            && !linkDebouncing
7896                            && config != null
7897                            && config.autoJoinStatus == WifiConfiguration.AUTO_JOIN_ENABLED
7898                            && !mWifiConfigStore.isLastSelectedConfiguration(config)
7899                            && (message.arg2 != 3 /* reason cannot be 3, i.e. locally generated */
7900                                || (lastRoam > 0 && lastRoam < 2000) /* unless driver is roaming */)
7901                            && ((ScanResult.is24GHz(mWifiInfo.getFrequency())
7902                                    && mWifiInfo.getRssi() >
7903                                    WifiConfiguration.BAD_RSSI_24)
7904                                    || (ScanResult.is5GHz(mWifiInfo.getFrequency())
7905                                    && mWifiInfo.getRssi() >
7906                                    WifiConfiguration.BAD_RSSI_5))) {
7907                        // Start de-bouncing the L2 disconnection:
7908                        // this L2 disconnection might be spurious.
7909                        // Hence we allow 7 seconds for the state machine to try
7910                        // to reconnect, go thru the
7911                        // roaming cycle and enter Obtaining IP address
7912                        // before signalling the disconnect to ConnectivityService and L3
7913                        startScanForConfiguration(getCurrentWifiConfiguration(), false);
7914                        linkDebouncing = true;
7915
7916                        sendMessageDelayed(obtainMessage(CMD_DELAYED_NETWORK_DISCONNECT,
7917                                0, mLastNetworkId), LINK_FLAPPING_DEBOUNCE_MSEC);
7918                        if (DBG) {
7919                            log("NETWORK_DISCONNECTION_EVENT in connected state"
7920                                    + " BSSID=" + mWifiInfo.getBSSID()
7921                                    + " RSSI=" + mWifiInfo.getRssi()
7922                                    + " freq=" + mWifiInfo.getFrequency()
7923                                    + " reason=" + message.arg2
7924                                    + " -> debounce");
7925                        }
7926                        return HANDLED;
7927                    } else {
7928                        if (DBG) {
7929                            int ajst = -1;
7930                            if (config != null) ajst = config.autoJoinStatus;
7931                            log("NETWORK_DISCONNECTION_EVENT in connected state"
7932                                    + " BSSID=" + mWifiInfo.getBSSID()
7933                                    + " RSSI=" + mWifiInfo.getRssi()
7934                                    + " freq=" + mWifiInfo.getFrequency()
7935                                    + " was debouncing=" + linkDebouncing
7936                                    + " reason=" + message.arg2
7937                                    + " ajst=" + ajst);
7938                        }
7939                    }
7940                    break;
7941                case CMD_AUTO_ROAM:
7942                    // Clear the driver roam indication since we are attempting a framerwork roam
7943                    mLastDriverRoamAttempt = 0;
7944
7945                    /* Connect command coming from auto-join */
7946                    ScanResult candidate = (ScanResult)message.obj;
7947                    String bssid = "any";
7948                    if (candidate != null && candidate.is5GHz()) {
7949                        // Only lock BSSID for 5GHz networks
7950                        bssid = candidate.BSSID;
7951                    }
7952                    int netId = mLastNetworkId;
7953                    config = getCurrentWifiConfiguration();
7954
7955
7956                    if (config == null) {
7957                        loge("AUTO_ROAM and no config, bail out...");
7958                        break;
7959                    }
7960
7961                    loge("CMD_AUTO_ROAM sup state "
7962                            + mSupplicantStateTracker.getSupplicantStateName()
7963                            + " my state " + getCurrentState().getName()
7964                            + " nid=" + Integer.toString(netId)
7965                            + " config " + config.configKey()
7966                            + " roam=" + Integer.toString(message.arg2)
7967                            + " to " + bssid
7968                            + " targetRoamBSSID " + mTargetRoamBSSID);
7969
7970                    /* Save the BSSID so as to lock it @ firmware */
7971                    if (!autoRoamSetBSSID(config, bssid) && !linkDebouncing) {
7972                        loge("AUTO_ROAM nothing to do");
7973                        // Same BSSID, nothing to do
7974                        messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
7975                        break;
7976                    };
7977
7978                    // Make sure the network is enabled, since supplicant will not reenable it
7979                    mWifiConfigStore.enableNetworkWithoutBroadcast(netId, false);
7980
7981                    boolean ret = false;
7982                    if (mLastNetworkId != netId) {
7983                       if (mWifiConfigStore.selectNetwork(config, /* updatePriorities = */ false) &&
7984                           mWifiNative.reconnect()) {
7985                           ret = true;
7986                       }
7987                    } else {
7988                         ret = mWifiNative.reassociate();
7989                    }
7990                    if (ret) {
7991                        lastConnectAttempt = System.currentTimeMillis();
7992                        targetWificonfiguration = mWifiConfigStore.getWifiConfiguration(netId);
7993
7994                        // replyToMessage(message, WifiManager.CONNECT_NETWORK_SUCCEEDED);
7995                        mAutoRoaming = message.arg2;
7996                        transitionTo(mRoamingState);
7997
7998                    } else {
7999                        loge("Failed to connect config: " + config + " netId: " + netId);
8000                        replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
8001                                WifiManager.ERROR);
8002                        messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
8003                        break;
8004                    }
8005                    break;
8006                default:
8007                    return NOT_HANDLED;
8008            }
8009            return HANDLED;
8010        }
8011
8012        @Override
8013        public void exit() {
8014            loge("WifiStateMachine: Leaving Connected state");
8015            setScanAlarm(false);
8016            mLastDriverRoamAttempt = 0;
8017        }
8018    }
8019
8020    class DisconnectingState extends State {
8021
8022        @Override
8023        public void enter() {
8024
8025            if (PDBG) {
8026                loge(" Enter DisconnectingState State scan interval " + mFrameworkScanIntervalMs
8027                        + " mEnableBackgroundScan= " + mEnableBackgroundScan
8028                        + " screenOn=" + mScreenOn);
8029            }
8030
8031            // Make sure we disconnect: we enter this state prior connecting to a new
8032            // network, waiting for either a DISCONECT event or a SUPPLICANT_STATE_CHANGE
8033            // event which in this case will be indicating that supplicant started to associate.
8034            // In some cases supplicant doesn't ignore the connect requests (it might not
8035            // find the target SSID in its cache),
8036            // Therefore we end up stuck that state, hence the need for the watchdog.
8037            disconnectingWatchdogCount++;
8038            loge("Start Disconnecting Watchdog " + disconnectingWatchdogCount);
8039            sendMessageDelayed(obtainMessage(CMD_DISCONNECTING_WATCHDOG_TIMER,
8040                    disconnectingWatchdogCount, 0), DISCONNECTING_GUARD_TIMER_MSEC);
8041        }
8042
8043        @Override
8044        public boolean processMessage(Message message) {
8045            logStateAndMessage(message, getClass().getSimpleName());
8046            switch (message.what) {
8047                case CMD_SET_OPERATIONAL_MODE:
8048                    if (message.arg1 != CONNECT_MODE) {
8049                        deferMessage(message);
8050                    }
8051                    break;
8052                case CMD_START_SCAN:
8053                    deferMessage(message);
8054                    return HANDLED;
8055                case CMD_DISCONNECTING_WATCHDOG_TIMER:
8056                    if (disconnectingWatchdogCount == message.arg1) {
8057                        if (DBG) log("disconnecting watchdog! -> disconnect");
8058                        handleNetworkDisconnect();
8059                        transitionTo(mDisconnectedState);
8060                    }
8061                    break;
8062                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
8063                    /**
8064                     * If we get a SUPPLICANT_STATE_CHANGE_EVENT before NETWORK_DISCONNECTION_EVENT
8065                     * we have missed the network disconnection, transition to mDisconnectedState
8066                     * and handle the rest of the events there
8067                     */
8068                    deferMessage(message);
8069                    handleNetworkDisconnect();
8070                    transitionTo(mDisconnectedState);
8071                    break;
8072                default:
8073                    return NOT_HANDLED;
8074            }
8075            return HANDLED;
8076        }
8077    }
8078
8079    class DisconnectedState extends State {
8080        @Override
8081        public void enter() {
8082            // We dont scan frequently if this is a temporary disconnect
8083            // due to p2p
8084            if (mTemporarilyDisconnectWifi) {
8085                mWifiP2pChannel.sendMessage(WifiP2pServiceImpl.DISCONNECT_WIFI_RESPONSE);
8086                return;
8087            }
8088
8089            mFrameworkScanIntervalMs = Settings.Global.getLong(mContext.getContentResolver(),
8090                    Settings.Global.WIFI_FRAMEWORK_SCAN_INTERVAL_MS,
8091                    mDefaultFrameworkScanIntervalMs);
8092
8093            if (PDBG) {
8094                loge(" Enter disconnected State scan interval " + mFrameworkScanIntervalMs
8095                        + " mEnableBackgroundScan= " + mEnableBackgroundScan
8096                        + " screenOn=" + mScreenOn
8097                        + " mFrameworkScanIntervalMs=" + mFrameworkScanIntervalMs);
8098            }
8099
8100            /** clear the roaming state, if we were roaming, we failed */
8101            mAutoRoaming = WifiAutoJoinController.AUTO_JOIN_IDLE;
8102
8103            if (mScreenOn) {
8104                /**
8105                 * screen lit and => delayed timer
8106                 */
8107                startDelayedScan(mDisconnectedScanPeriodMs, null, null);
8108            } else {
8109                /**
8110                 * screen dark and PNO supported => scan alarm disabled
8111                 */
8112                if (mEnableBackgroundScan) {
8113                    /* If a regular scan result is pending, do not initiate background
8114                     * scan until the scan results are returned. This is needed because
8115                     * initiating a background scan will cancel the regular scan and
8116                     * scan results will not be returned until background scanning is
8117                     * cleared
8118                     */
8119                    if (!mIsScanOngoing) {
8120                        enableBackgroundScan(true);
8121                    }
8122                } else {
8123                    setScanAlarm(true);
8124                }
8125            }
8126
8127            /**
8128             * If we have no networks saved, the supplicant stops doing the periodic scan.
8129             * The scans are useful to notify the user of the presence of an open network.
8130             * Note that these are not wake up scans.
8131             */
8132            if (!mP2pConnected.get() && mWifiConfigStore.getConfiguredNetworks().size() == 0) {
8133                sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,
8134                        ++mPeriodicScanToken, 0), mSupplicantScanIntervalMs);
8135            }
8136
8137            mDisconnectedTimeStamp = System.currentTimeMillis();
8138
8139        }
8140        @Override
8141        public boolean processMessage(Message message) {
8142            boolean ret = HANDLED;
8143
8144            logStateAndMessage(message, getClass().getSimpleName());
8145
8146            switch (message.what) {
8147                case CMD_NO_NETWORKS_PERIODIC_SCAN:
8148                    if (mP2pConnected.get()) break;
8149                    if (message.arg1 == mPeriodicScanToken &&
8150                            mWifiConfigStore.getConfiguredNetworks().size() == 0) {
8151                        startScan(UNKNOWN_SCAN_SOURCE, -1, null, null);
8152                        sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,
8153                                    ++mPeriodicScanToken, 0), mSupplicantScanIntervalMs);
8154                    }
8155                    break;
8156                case WifiManager.FORGET_NETWORK:
8157                case CMD_REMOVE_NETWORK:
8158                    // Set up a delayed message here. After the forget/remove is handled
8159                    // the handled delayed message will determine if there is a need to
8160                    // scan and continue
8161                    sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,
8162                                ++mPeriodicScanToken, 0), mSupplicantScanIntervalMs);
8163                    ret = NOT_HANDLED;
8164                    break;
8165                case CMD_SET_OPERATIONAL_MODE:
8166                    if (message.arg1 != CONNECT_MODE) {
8167                        mOperationalMode = message.arg1;
8168
8169                        mWifiConfigStore.disableAllNetworks();
8170                        if (mOperationalMode == SCAN_ONLY_WITH_WIFI_OFF_MODE) {
8171                            mWifiP2pChannel.sendMessage(CMD_DISABLE_P2P_REQ);
8172                            setWifiState(WIFI_STATE_DISABLED);
8173                        }
8174                        transitionTo(mScanModeState);
8175                    }
8176                    mWifiConfigStore.
8177                            setLastSelectedConfiguration(WifiConfiguration.INVALID_NETWORK_ID);
8178                    break;
8179                    /* Ignore network disconnect */
8180                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
8181                    break;
8182                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
8183                    StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
8184                    if (DBG) {
8185                        loge("SUPPLICANT_STATE_CHANGE_EVENT state=" + stateChangeResult.state +
8186                                " -> state= " + WifiInfo.getDetailedStateOf(stateChangeResult.state)
8187                                + " debouncing=" + linkDebouncing);
8188                    }
8189                    setNetworkDetailedState(WifiInfo.getDetailedStateOf(stateChangeResult.state));
8190                    /* ConnectModeState does the rest of the handling */
8191                    ret = NOT_HANDLED;
8192                    break;
8193                case CMD_START_SCAN:
8194                    if (!checkOrDeferScanAllowed(message)) {
8195                        // The scan request was rescheduled
8196                        messageHandlingStatus = MESSAGE_HANDLING_STATUS_REFUSED;
8197                        return HANDLED;
8198                    }
8199                    /* Disable background scan temporarily during a regular scan */
8200                    if (mEnableBackgroundScan) {
8201                        enableBackgroundScan(false);
8202                    }
8203                    if (message.arg1 == SCAN_ALARM_SOURCE) {
8204                        // Check if the CMD_START_SCAN message is obsolete (and thus if it should
8205                        // not be processed) and restart the scan
8206                        int period =  mDisconnectedScanPeriodMs;
8207                        if (mP2pConnected.get()) {
8208                           period = (int)Settings.Global.getLong(mContext.getContentResolver(),
8209                                    Settings.Global.WIFI_SCAN_INTERVAL_WHEN_P2P_CONNECTED_MS,
8210                                    mDisconnectedScanPeriodMs);
8211                        }
8212                        if (!checkAndRestartDelayedScan(message.arg2,
8213                                true, period, null, null)) {
8214                            messageHandlingStatus = MESSAGE_HANDLING_STATUS_OBSOLETE;
8215                            loge("WifiStateMachine Disconnected CMD_START_SCAN source "
8216                                    + message.arg1
8217                                    + " " + message.arg2 + ", " + mDelayedScanCounter
8218                                    + " -> obsolete");
8219                            return HANDLED;
8220                        }
8221                        handleScanRequest(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, message);
8222                        ret = HANDLED;
8223                    } else {
8224                        ret = NOT_HANDLED;
8225                    }
8226                    break;
8227                case WifiMonitor.SCAN_RESULTS_EVENT:
8228                    /* Re-enable background scan when a pending scan result is received */
8229                    if (mEnableBackgroundScan && mIsScanOngoing) {
8230                        enableBackgroundScan(true);
8231                    }
8232                    /* Handled in parent state */
8233                    ret = NOT_HANDLED;
8234                    break;
8235                case WifiP2pServiceImpl.P2P_CONNECTION_CHANGED:
8236                    NetworkInfo info = (NetworkInfo) message.obj;
8237                    mP2pConnected.set(info.isConnected());
8238                    if (mP2pConnected.get()) {
8239                        int defaultInterval = mContext.getResources().getInteger(
8240                                R.integer.config_wifi_scan_interval_p2p_connected);
8241                        long scanIntervalMs = Settings.Global.getLong(mContext.getContentResolver(),
8242                                Settings.Global.WIFI_SCAN_INTERVAL_WHEN_P2P_CONNECTED_MS,
8243                                defaultInterval);
8244                        mWifiNative.setScanInterval((int) scanIntervalMs/1000);
8245                    } else if (mWifiConfigStore.getConfiguredNetworks().size() == 0) {
8246                        if (DBG) log("Turn on scanning after p2p disconnected");
8247                        sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,
8248                                    ++mPeriodicScanToken, 0), mSupplicantScanIntervalMs);
8249                    } else {
8250                        // If P2P is not connected and there are saved networks, then restart
8251                        // scanning at the normal period. This is necessary because scanning might
8252                        // have been disabled altogether if WIFI_SCAN_INTERVAL_WHEN_P2P_CONNECTED_MS
8253                        // was set to zero.
8254                        startDelayedScan(mDisconnectedScanPeriodMs, null, null);
8255                    }
8256                    break;
8257                case CMD_RECONNECT:
8258                case CMD_REASSOCIATE:
8259                    if (mTemporarilyDisconnectWifi) {
8260                        // Drop a third party reconnect/reassociate if STA is
8261                        // temporarily disconnected for p2p
8262                        break;
8263                    } else {
8264                        // ConnectModeState handles it
8265                        ret = NOT_HANDLED;
8266                    }
8267                    break;
8268                case CMD_SCREEN_STATE_CHANGED:
8269                    handleScreenStateChanged(message.arg1 != 0,
8270                            /* startBackgroundScanIfNeeded = */ true);
8271                    break;
8272                default:
8273                    ret = NOT_HANDLED;
8274            }
8275            return ret;
8276        }
8277
8278        @Override
8279        public void exit() {
8280            /* No need for a background scan upon exit from a disconnected state */
8281            if (mEnableBackgroundScan) {
8282                enableBackgroundScan(false);
8283            }
8284            setScanAlarm(false);
8285        }
8286    }
8287
8288    class WpsRunningState extends State {
8289        // Tracks the source to provide a reply
8290        private Message mSourceMessage;
8291        @Override
8292        public void enter() {
8293            mSourceMessage = Message.obtain(getCurrentMessage());
8294        }
8295        @Override
8296        public boolean processMessage(Message message) {
8297            logStateAndMessage(message, getClass().getSimpleName());
8298
8299            switch (message.what) {
8300                case WifiMonitor.WPS_SUCCESS_EVENT:
8301                    // Ignore intermediate success, wait for full connection
8302                    break;
8303                case WifiMonitor.NETWORK_CONNECTION_EVENT:
8304                    replyToMessage(mSourceMessage, WifiManager.WPS_COMPLETED);
8305                    mSourceMessage.recycle();
8306                    mSourceMessage = null;
8307                    deferMessage(message);
8308                    transitionTo(mDisconnectedState);
8309                    break;
8310                case WifiMonitor.WPS_OVERLAP_EVENT:
8311                    replyToMessage(mSourceMessage, WifiManager.WPS_FAILED,
8312                            WifiManager.WPS_OVERLAP_ERROR);
8313                    mSourceMessage.recycle();
8314                    mSourceMessage = null;
8315                    transitionTo(mDisconnectedState);
8316                    break;
8317                case WifiMonitor.WPS_FAIL_EVENT:
8318                    // Arg1 has the reason for the failure
8319                    if ((message.arg1 != WifiManager.ERROR) || (message.arg2 != 0)) {
8320                        replyToMessage(mSourceMessage, WifiManager.WPS_FAILED, message.arg1);
8321                        mSourceMessage.recycle();
8322                        mSourceMessage = null;
8323                        transitionTo(mDisconnectedState);
8324                    } else {
8325                        if (DBG) log("Ignore unspecified fail event during WPS connection");
8326                    }
8327                    break;
8328                case WifiMonitor.WPS_TIMEOUT_EVENT:
8329                    replyToMessage(mSourceMessage, WifiManager.WPS_FAILED,
8330                            WifiManager.WPS_TIMED_OUT);
8331                    mSourceMessage.recycle();
8332                    mSourceMessage = null;
8333                    transitionTo(mDisconnectedState);
8334                    break;
8335                case WifiManager.START_WPS:
8336                    replyToMessage(message, WifiManager.WPS_FAILED, WifiManager.IN_PROGRESS);
8337                    break;
8338                case WifiManager.CANCEL_WPS:
8339                    if (mWifiNative.cancelWps()) {
8340                        replyToMessage(message, WifiManager.CANCEL_WPS_SUCCEDED);
8341                    } else {
8342                        replyToMessage(message, WifiManager.CANCEL_WPS_FAILED, WifiManager.ERROR);
8343                    }
8344                    transitionTo(mDisconnectedState);
8345                    break;
8346                /**
8347                 * Defer all commands that can cause connections to a different network
8348                 * or put the state machine out of connect mode
8349                 */
8350                case CMD_STOP_DRIVER:
8351                case CMD_SET_OPERATIONAL_MODE:
8352                case WifiManager.CONNECT_NETWORK:
8353                case CMD_ENABLE_NETWORK:
8354                case CMD_RECONNECT:
8355                case CMD_REASSOCIATE:
8356                    deferMessage(message);
8357                    break;
8358                case CMD_AUTO_CONNECT:
8359                case CMD_AUTO_ROAM:
8360                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
8361                    return HANDLED;
8362                case CMD_START_SCAN:
8363                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
8364                    return HANDLED;
8365                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
8366                    if (DBG) log("Network connection lost");
8367                    handleNetworkDisconnect();
8368                    break;
8369                case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
8370                    if (DBG) log("Ignore Assoc reject event during WPS Connection");
8371                    break;
8372                case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
8373                    // Disregard auth failure events during WPS connection. The
8374                    // EAP sequence is retried several times, and there might be
8375                    // failures (especially for wps pin). We will get a WPS_XXX
8376                    // event at the end of the sequence anyway.
8377                    if (DBG) log("Ignore auth failure during WPS connection");
8378                    break;
8379                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
8380                    // Throw away supplicant state changes when WPS is running.
8381                    // We will start getting supplicant state changes once we get
8382                    // a WPS success or failure
8383                    break;
8384                default:
8385                    return NOT_HANDLED;
8386            }
8387            return HANDLED;
8388        }
8389
8390        @Override
8391        public void exit() {
8392            mWifiConfigStore.enableAllNetworks();
8393            mWifiConfigStore.loadConfiguredNetworks();
8394        }
8395    }
8396
8397    class SoftApStartingState extends State {
8398        @Override
8399        public void enter() {
8400            final Message message = getCurrentMessage();
8401            if (message.what == CMD_START_AP) {
8402                final WifiConfiguration config = (WifiConfiguration) message.obj;
8403
8404                if (config == null) {
8405                    mWifiApConfigChannel.sendMessage(CMD_REQUEST_AP_CONFIG);
8406                } else {
8407                    mWifiApConfigChannel.sendMessage(CMD_SET_AP_CONFIG, config);
8408                    startSoftApWithConfig(config);
8409                }
8410            } else {
8411                throw new RuntimeException("Illegal transition to SoftApStartingState: " + message);
8412            }
8413        }
8414        @Override
8415        public boolean processMessage(Message message) {
8416            logStateAndMessage(message, getClass().getSimpleName());
8417
8418            switch(message.what) {
8419                case CMD_START_SUPPLICANT:
8420                case CMD_STOP_SUPPLICANT:
8421                case CMD_START_AP:
8422                case CMD_STOP_AP:
8423                case CMD_START_DRIVER:
8424                case CMD_STOP_DRIVER:
8425                case CMD_SET_OPERATIONAL_MODE:
8426                case CMD_SET_COUNTRY_CODE:
8427                case CMD_SET_FREQUENCY_BAND:
8428                case CMD_START_PACKET_FILTERING:
8429                case CMD_STOP_PACKET_FILTERING:
8430                case CMD_TETHER_STATE_CHANGE:
8431                    deferMessage(message);
8432                    break;
8433                case WifiStateMachine.CMD_RESPONSE_AP_CONFIG:
8434                    WifiConfiguration config = (WifiConfiguration) message.obj;
8435                    if (config != null) {
8436                        startSoftApWithConfig(config);
8437                    } else {
8438                        loge("Softap config is null!");
8439                        sendMessage(CMD_START_AP_FAILURE);
8440                    }
8441                    break;
8442                case CMD_START_AP_SUCCESS:
8443                    setWifiApState(WIFI_AP_STATE_ENABLED);
8444                    transitionTo(mSoftApStartedState);
8445                    break;
8446                case CMD_START_AP_FAILURE:
8447                    setWifiApState(WIFI_AP_STATE_FAILED);
8448                    transitionTo(mInitialState);
8449                    break;
8450                default:
8451                    return NOT_HANDLED;
8452            }
8453            return HANDLED;
8454        }
8455    }
8456
8457    class SoftApStartedState extends State {
8458        @Override
8459        public boolean processMessage(Message message) {
8460            logStateAndMessage(message, getClass().getSimpleName());
8461
8462            switch(message.what) {
8463                case CMD_STOP_AP:
8464                    if (DBG) log("Stopping Soft AP");
8465                    /* We have not tethered at this point, so we just shutdown soft Ap */
8466                    try {
8467                        mNwService.stopAccessPoint(mInterfaceName);
8468                    } catch(Exception e) {
8469                        loge("Exception in stopAccessPoint()");
8470                    }
8471                    setWifiApState(WIFI_AP_STATE_DISABLED);
8472                    transitionTo(mInitialState);
8473                    break;
8474                case CMD_START_AP:
8475                    // Ignore a start on a running access point
8476                    break;
8477                    // Fail client mode operation when soft AP is enabled
8478                case CMD_START_SUPPLICANT:
8479                    loge("Cannot start supplicant with a running soft AP");
8480                    setWifiState(WIFI_STATE_UNKNOWN);
8481                    break;
8482                case CMD_TETHER_STATE_CHANGE:
8483                    TetherStateChange stateChange = (TetherStateChange) message.obj;
8484                    if (startTethering(stateChange.available)) {
8485                        transitionTo(mTetheringState);
8486                    }
8487                    break;
8488                default:
8489                    return NOT_HANDLED;
8490            }
8491            return HANDLED;
8492        }
8493    }
8494
8495    class TetheringState extends State {
8496        @Override
8497        public void enter() {
8498            /* Send ourselves a delayed message to shut down if tethering fails to notify */
8499            sendMessageDelayed(obtainMessage(CMD_TETHER_NOTIFICATION_TIMED_OUT,
8500                    ++mTetherToken, 0), TETHER_NOTIFICATION_TIME_OUT_MSECS);
8501        }
8502        @Override
8503        public boolean processMessage(Message message) {
8504            logStateAndMessage(message, getClass().getSimpleName());
8505
8506            switch(message.what) {
8507                case CMD_TETHER_STATE_CHANGE:
8508                    TetherStateChange stateChange = (TetherStateChange) message.obj;
8509                    if (isWifiTethered(stateChange.active)) {
8510                        transitionTo(mTetheredState);
8511                    }
8512                    return HANDLED;
8513                case CMD_TETHER_NOTIFICATION_TIMED_OUT:
8514                    if (message.arg1 == mTetherToken) {
8515                        loge("Failed to get tether update, shutdown soft access point");
8516                        transitionTo(mSoftApStartedState);
8517                        // Needs to be first thing handled
8518                        sendMessageAtFrontOfQueue(CMD_STOP_AP);
8519                    }
8520                    break;
8521                case CMD_START_SUPPLICANT:
8522                case CMD_STOP_SUPPLICANT:
8523                case CMD_START_AP:
8524                case CMD_STOP_AP:
8525                case CMD_START_DRIVER:
8526                case CMD_STOP_DRIVER:
8527                case CMD_SET_OPERATIONAL_MODE:
8528                case CMD_SET_COUNTRY_CODE:
8529                case CMD_SET_FREQUENCY_BAND:
8530                case CMD_START_PACKET_FILTERING:
8531                case CMD_STOP_PACKET_FILTERING:
8532                    deferMessage(message);
8533                    break;
8534                default:
8535                    return NOT_HANDLED;
8536            }
8537            return HANDLED;
8538        }
8539    }
8540
8541    class TetheredState extends State {
8542        @Override
8543        public boolean processMessage(Message message) {
8544            logStateAndMessage(message, getClass().getSimpleName());
8545
8546            switch(message.what) {
8547                case CMD_TETHER_STATE_CHANGE:
8548                    TetherStateChange stateChange = (TetherStateChange) message.obj;
8549                    if (!isWifiTethered(stateChange.active)) {
8550                        loge("Tethering reports wifi as untethered!, shut down soft Ap");
8551                        setHostApRunning(null, false);
8552                        setHostApRunning(null, true);
8553                    }
8554                    return HANDLED;
8555                case CMD_STOP_AP:
8556                    if (DBG) log("Untethering before stopping AP");
8557                    setWifiApState(WIFI_AP_STATE_DISABLING);
8558                    stopTethering();
8559                    transitionTo(mUntetheringState);
8560                    // More work to do after untethering
8561                    deferMessage(message);
8562                    break;
8563                default:
8564                    return NOT_HANDLED;
8565            }
8566            return HANDLED;
8567        }
8568    }
8569
8570    class UntetheringState extends State {
8571        @Override
8572        public void enter() {
8573            /* Send ourselves a delayed message to shut down if tethering fails to notify */
8574            sendMessageDelayed(obtainMessage(CMD_TETHER_NOTIFICATION_TIMED_OUT,
8575                    ++mTetherToken, 0), TETHER_NOTIFICATION_TIME_OUT_MSECS);
8576
8577        }
8578        @Override
8579        public boolean processMessage(Message message) {
8580            logStateAndMessage(message, getClass().getSimpleName());
8581
8582            switch(message.what) {
8583                case CMD_TETHER_STATE_CHANGE:
8584                    TetherStateChange stateChange = (TetherStateChange) message.obj;
8585
8586                    /* Wait till wifi is untethered */
8587                    if (isWifiTethered(stateChange.active)) break;
8588
8589                    transitionTo(mSoftApStartedState);
8590                    break;
8591                case CMD_TETHER_NOTIFICATION_TIMED_OUT:
8592                    if (message.arg1 == mTetherToken) {
8593                        loge("Failed to get tether update, force stop access point");
8594                        transitionTo(mSoftApStartedState);
8595                    }
8596                    break;
8597                case CMD_START_SUPPLICANT:
8598                case CMD_STOP_SUPPLICANT:
8599                case CMD_START_AP:
8600                case CMD_STOP_AP:
8601                case CMD_START_DRIVER:
8602                case CMD_STOP_DRIVER:
8603                case CMD_SET_OPERATIONAL_MODE:
8604                case CMD_SET_COUNTRY_CODE:
8605                case CMD_SET_FREQUENCY_BAND:
8606                case CMD_START_PACKET_FILTERING:
8607                case CMD_STOP_PACKET_FILTERING:
8608                    deferMessage(message);
8609                    break;
8610                default:
8611                    return NOT_HANDLED;
8612            }
8613            return HANDLED;
8614        }
8615    }
8616
8617    //State machine initiated requests can have replyTo set to null indicating
8618    //there are no recepients, we ignore those reply actions
8619    private void replyToMessage(Message msg, int what) {
8620        if (msg.replyTo == null) return;
8621        Message dstMsg = obtainMessageWithArg2(msg);
8622        dstMsg.what = what;
8623        mReplyChannel.replyToMessage(msg, dstMsg);
8624    }
8625
8626    private void replyToMessage(Message msg, int what, int arg1) {
8627        if (msg.replyTo == null) return;
8628        Message dstMsg = obtainMessageWithArg2(msg);
8629        dstMsg.what = what;
8630        dstMsg.arg1 = arg1;
8631        mReplyChannel.replyToMessage(msg, dstMsg);
8632    }
8633
8634    private void replyToMessage(Message msg, int what, Object obj) {
8635        if (msg.replyTo == null) return;
8636        Message dstMsg = obtainMessageWithArg2(msg);
8637        dstMsg.what = what;
8638        dstMsg.obj = obj;
8639        mReplyChannel.replyToMessage(msg, dstMsg);
8640    }
8641
8642    /**
8643     * arg2 on the source message has a unique id that needs to be retained in replies
8644     * to match the request
8645
8646     * see WifiManager for details
8647     */
8648    private Message obtainMessageWithArg2(Message srcMsg) {
8649        Message msg = Message.obtain();
8650        msg.arg2 = srcMsg.arg2;
8651        return msg;
8652    }
8653
8654    private static int parseHex(char ch) {
8655        if ('0' <= ch && ch <= '9') {
8656            return ch - '0';
8657        } else if ('a' <= ch && ch <= 'f') {
8658            return ch - 'a' + 10;
8659        } else if ('A' <= ch && ch <= 'F') {
8660            return ch - 'A' + 10;
8661        } else {
8662            throw new NumberFormatException("" + ch + " is not a valid hex digit");
8663        }
8664    }
8665
8666    private byte[] parseHex(String hex) {
8667        /* This only works for good input; don't throw bad data at it */
8668        if (hex == null) {
8669            return new byte[0];
8670        }
8671
8672        if (hex.length() % 2 != 0) {
8673            throw new NumberFormatException(hex + " is not a valid hex string");
8674        }
8675
8676        byte[] result = new byte[(hex.length())/2 + 1];
8677        result[0] = (byte) ((hex.length())/2);
8678        for (int i = 0, j = 1; i < hex.length(); i += 2, j++) {
8679            int val = parseHex(hex.charAt(i)) * 16 + parseHex(hex.charAt(i+1));
8680            byte b = (byte) (val & 0xFF);
8681            result[j] = b;
8682        }
8683
8684        return result;
8685    }
8686
8687    private static String makeHex(byte[] bytes) {
8688        StringBuilder sb = new StringBuilder();
8689        for (byte b : bytes) {
8690            sb.append(String.format("%02x", b));
8691        }
8692        return sb.toString();
8693    }
8694
8695    private static String makeHex(byte[] bytes, int from, int len) {
8696        StringBuilder sb = new StringBuilder();
8697        for (int i = 0; i < len; i++) {
8698            sb.append(String.format("%02x", bytes[from+i]));
8699        }
8700        return sb.toString();
8701    }
8702
8703    private static byte[] concat(byte[] array1, byte[] array2, byte[] array3) {
8704
8705        int len = array1.length + array2.length + array3.length;
8706
8707        if (array1.length != 0) {
8708            len++;                      /* add another byte for size */
8709        }
8710
8711        if (array2.length != 0) {
8712            len++;                      /* add another byte for size */
8713        }
8714
8715        if (array3.length != 0) {
8716            len++;                      /* add another byte for size */
8717        }
8718
8719        byte[] result = new byte[len];
8720
8721        int index = 0;
8722        if (array1.length != 0) {
8723            result[index] = (byte) (array1.length & 0xFF);
8724            index++;
8725            for (byte b : array1) {
8726                result[index] = b;
8727                index++;
8728            }
8729        }
8730
8731        if (array2.length != 0) {
8732            result[index] = (byte) (array2.length & 0xFF);
8733            index++;
8734            for (byte b : array2) {
8735                result[index] = b;
8736                index++;
8737            }
8738        }
8739
8740        if (array3.length != 0) {
8741            result[index] = (byte) (array3.length & 0xFF);
8742            index++;
8743            for (byte b : array3) {
8744                result[index] = b;
8745                index++;
8746            }
8747        }
8748        return result;
8749    }
8750
8751    private static byte[] concatHex(byte[] array1, byte[] array2) {
8752
8753        int len = array1.length + array2.length;
8754
8755        byte[] result = new byte[len];
8756
8757        int index = 0;
8758        if (array1.length != 0) {
8759            for (byte b : array1) {
8760                result[index] = b;
8761                index++;
8762            }
8763        }
8764
8765        if (array2.length != 0) {
8766            for (byte b : array2) {
8767                result[index] = b;
8768                index++;
8769            }
8770        }
8771
8772        return result;
8773    }
8774
8775    void handleGsmAuthRequest(SimAuthRequestData requestData) {
8776        if (targetWificonfiguration == null
8777                || targetWificonfiguration.networkId == requestData.networkId) {
8778            logd("id matches targetWifiConfiguration");
8779        } else {
8780            logd("id does not match targetWifiConfiguration");
8781            return;
8782        }
8783
8784        TelephonyManager tm = (TelephonyManager)
8785                mContext.getSystemService(Context.TELEPHONY_SERVICE);
8786
8787        if (tm != null) {
8788            StringBuilder sb = new StringBuilder();
8789            for (String challenge : requestData.data) {
8790
8791                logd("RAND = " + challenge);
8792
8793                byte[] rand = null;
8794                try {
8795                    rand = parseHex(challenge);
8796                } catch (NumberFormatException e) {
8797                    loge("malformed challenge");
8798                    continue;
8799                }
8800
8801                String base64Challenge = android.util.Base64.encodeToString(
8802                        rand, android.util.Base64.NO_WRAP);
8803                /*
8804                 * appType = 1 => SIM, 2 => USIM according to
8805                 * com.android.internal.telephony.PhoneConstants#APPTYPE_xxx
8806                 */
8807                int appType = 2;
8808                String tmResponse = tm.getIccSimChallengeResponse(appType, base64Challenge);
8809                logv("Raw Response - " + tmResponse);
8810
8811                if (tmResponse != null && tmResponse.length() > 4) {
8812                    byte[] result = android.util.Base64.decode(tmResponse,
8813                            android.util.Base64.DEFAULT);
8814                    logv("Hex Response -" + makeHex(result));
8815                    int sres_len = result[0];
8816                    String sres = makeHex(result, 1, sres_len);
8817                    int kc_offset = 1+sres_len;
8818                    int kc_len = result[kc_offset];
8819                    String kc = makeHex(result, 1+kc_offset, kc_len);
8820                    sb.append(":" + kc + ":" + sres);
8821                    logv("kc:" + kc + " sres:" + sres);
8822                } else {
8823                    loge("bad response - " + tmResponse);
8824                }
8825            }
8826
8827            String response = sb.toString();
8828            logv("Supplicant Response -" + response);
8829            mWifiNative.simAuthResponse(requestData.networkId, "GSM-AUTH", response);
8830        } else {
8831            loge("could not get telephony manager");
8832        }
8833    }
8834
8835    void handle3GAuthRequest(SimAuthRequestData requestData) {
8836        StringBuilder sb = new StringBuilder();
8837        byte[] rand = null;
8838        byte[] authn = null;
8839        String res_type = "UMTS-AUTH";
8840
8841        if (targetWificonfiguration == null
8842                || targetWificonfiguration.networkId == requestData.networkId) {
8843            logd("id matches targetWifiConfiguration");
8844        } else {
8845            logd("id does not match targetWifiConfiguration");
8846            return;
8847        }
8848        if (requestData.data.length == 2) {
8849            try {
8850                rand = parseHex(requestData.data[0]);
8851                authn = parseHex(requestData.data[1]);
8852            } catch (NumberFormatException e) {
8853                loge("malformed challenge");
8854            }
8855        } else {
8856               loge("malformed challenge");
8857        }
8858
8859        String tmResponse = "";
8860        if (rand != null && authn != null) {
8861            String base64Challenge = android.util.Base64.encodeToString(
8862                    concatHex(rand,authn), android.util.Base64.NO_WRAP);
8863
8864            TelephonyManager tm = (TelephonyManager)
8865                    mContext.getSystemService(Context.TELEPHONY_SERVICE);
8866            if (tm != null) {
8867                int appType = 2; // 2 => USIM
8868                tmResponse = tm.getIccSimChallengeResponse(appType, base64Challenge);
8869                logv("Raw Response - " + tmResponse);
8870            } else {
8871                loge("could not get telephony manager");
8872            }
8873        }
8874
8875        if (tmResponse != null && tmResponse.length() > 4) {
8876            byte[] result = android.util.Base64.decode(tmResponse,
8877                    android.util.Base64.DEFAULT);
8878            loge("Hex Response - " + makeHex(result));
8879            byte tag = result[0];
8880            if (tag == (byte) 0xdb) {
8881                logv("successful 3G authentication ");
8882                int res_len = result[1];
8883                String res = makeHex(result, 2, res_len);
8884                int ck_len = result[res_len + 2];
8885                String ck = makeHex(result, res_len + 3, ck_len);
8886                int ik_len = result[res_len + ck_len + 3];
8887                String ik = makeHex(result, res_len + ck_len + 4, ik_len);
8888                sb.append(":" + ik + ":" + ck + ":" + res);
8889                logv("ik:" + ik + "ck:" + ck + " res:" + res);
8890            } else if (tag == (byte) 0xdc) {
8891                loge("synchronisation failure");
8892                int auts_len = result[1];
8893                String auts = makeHex(result, 2, auts_len);
8894                res_type = "UMTS-AUTS";
8895                sb.append(":" + auts);
8896                logv("auts:" + auts);
8897            } else {
8898                loge("bad response - unknown tag = " + tag);
8899                return;
8900            }
8901        } else {
8902            loge("bad response - " + tmResponse);
8903            return;
8904        }
8905
8906        String response = sb.toString();
8907        logv("Supplicant Response -" + response);
8908        mWifiNative.simAuthResponse(requestData.networkId, res_type, response);
8909    }
8910}
8911