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