WifiStateMachine.java revision 0047ccf563baa288777e06c6fe95d3681fcf5ccd
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        //fix me -- set country code through HAL Here
4656        if (config.apChannel == 0) {
4657            config.apChannel = chooseApChannel(config.apBand);
4658            if (config.apChannel == 0) {
4659                //fail to get available channel
4660                sendMessage(CMD_START_AP_FAILURE);
4661                return;
4662            }
4663        }
4664        //turn off interface
4665        if (!mWifiNative.toggleInterface(0)) {
4666            sendMessage(CMD_START_AP_FAILURE);
4667            return;
4668        }
4669        // Start hostapd on a separate thread
4670        new Thread(new Runnable() {
4671            public void run() {
4672                try {
4673                    mNwService.startAccessPoint(config, mInterfaceName);
4674                } catch (Exception e) {
4675                    loge("Exception in softap start " + e);
4676                    try {
4677                        mNwService.stopAccessPoint(mInterfaceName);
4678                        mNwService.startAccessPoint(config, mInterfaceName);
4679                    } catch (Exception e1) {
4680                        loge("Exception in softap re-start " + e1);
4681                        sendMessage(CMD_START_AP_FAILURE);
4682                        return;
4683                    }
4684                }
4685                if (DBG) log("Soft AP start successful");
4686                sendMessage(CMD_START_AP_SUCCESS);
4687            }
4688        }).start();
4689    }
4690
4691    /*
4692     * Read a MAC address in /proc/arp/table, used by WifistateMachine
4693     * so as to record MAC address of default gateway.
4694     **/
4695    private String macAddressFromRoute(String ipAddress) {
4696        String macAddress = null;
4697        BufferedReader reader = null;
4698        try {
4699            reader = new BufferedReader(new FileReader("/proc/net/arp"));
4700
4701            // Skip over the line bearing colum titles
4702            String line = reader.readLine();
4703
4704            while ((line = reader.readLine()) != null) {
4705                String[] tokens = line.split("[ ]+");
4706                if (tokens.length < 6) {
4707                    continue;
4708                }
4709
4710                // ARP column format is
4711                // Address HWType HWAddress Flags Mask IFace
4712                String ip = tokens[0];
4713                String mac = tokens[3];
4714
4715                if (ipAddress.equals(ip)) {
4716                    macAddress = mac;
4717                    break;
4718                }
4719            }
4720
4721            if (macAddress == null) {
4722                loge("Did not find remoteAddress {" + ipAddress + "} in " +
4723                        "/proc/net/arp");
4724            }
4725
4726        } catch (FileNotFoundException e) {
4727            loge("Could not open /proc/net/arp to lookup mac address");
4728        } catch (IOException e) {
4729            loge("Could not read /proc/net/arp to lookup mac address");
4730        } finally {
4731            try {
4732                if (reader != null) {
4733                    reader.close();
4734                }
4735            } catch (IOException e) {
4736                // Do nothing
4737            }
4738        }
4739        return macAddress;
4740
4741    }
4742
4743    private class WifiNetworkFactory extends NetworkFactory {
4744        public WifiNetworkFactory(Looper l, Context c, String TAG, NetworkCapabilities f) {
4745            super(l, c, TAG, f);
4746        }
4747
4748        @Override
4749        protected void needNetworkFor(NetworkRequest networkRequest, int score) {
4750            ++mConnectionRequests;
4751        }
4752
4753        @Override
4754        protected void releaseNetworkFor(NetworkRequest networkRequest) {
4755            --mConnectionRequests;
4756        }
4757
4758        public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
4759            pw.println("mConnectionRequests " + mConnectionRequests);
4760        }
4761
4762    }
4763
4764    private class UntrustedWifiNetworkFactory extends NetworkFactory {
4765        private int mUntrustedReqCount;
4766
4767        public UntrustedWifiNetworkFactory(Looper l, Context c, String tag, NetworkCapabilities f) {
4768            super(l, c, tag, f);
4769        }
4770
4771        @Override
4772        protected void needNetworkFor(NetworkRequest networkRequest, int score) {
4773            if (!networkRequest.networkCapabilities.hasCapability(
4774                    NetworkCapabilities.NET_CAPABILITY_TRUSTED)) {
4775                if (++mUntrustedReqCount == 1) {
4776                    mWifiAutoJoinController.setAllowUntrustedConnections(true);
4777                }
4778            }
4779        }
4780
4781        @Override
4782        protected void releaseNetworkFor(NetworkRequest networkRequest) {
4783            if (!networkRequest.networkCapabilities.hasCapability(
4784                    NetworkCapabilities.NET_CAPABILITY_TRUSTED)) {
4785                if (--mUntrustedReqCount == 0) {
4786                    mWifiAutoJoinController.setAllowUntrustedConnections(false);
4787                }
4788            }
4789        }
4790
4791        public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
4792            pw.println("mUntrustedReqCount " + mUntrustedReqCount);
4793        }
4794    }
4795
4796    void maybeRegisterNetworkFactory() {
4797        if (mNetworkFactory == null) {
4798            checkAndSetConnectivityInstance();
4799            if (mCm != null) {
4800                mNetworkFactory = new WifiNetworkFactory(getHandler().getLooper(), mContext,
4801                        NETWORKTYPE, mNetworkCapabilitiesFilter);
4802                mNetworkFactory.setScoreFilter(60);
4803                mNetworkFactory.register();
4804
4805                // We can't filter untrusted network in the capabilities filter because a trusted
4806                // network would still satisfy a request that accepts untrusted ones.
4807                mUntrustedNetworkFactory = new UntrustedWifiNetworkFactory(getHandler().getLooper(),
4808                        mContext, NETWORKTYPE_UNTRUSTED, mNetworkCapabilitiesFilter);
4809                mUntrustedNetworkFactory.setScoreFilter(Integer.MAX_VALUE);
4810                mUntrustedNetworkFactory.register();
4811            }
4812        }
4813    }
4814
4815    /********************************************************
4816     * HSM states
4817     *******************************************************/
4818
4819    class DefaultState extends State {
4820        @Override
4821        public boolean processMessage(Message message) {
4822            logStateAndMessage(message, getClass().getSimpleName());
4823
4824            switch (message.what) {
4825                case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: {
4826                    AsyncChannel ac = (AsyncChannel) message.obj;
4827                    if (ac == mWifiP2pChannel) {
4828                        if (message.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
4829                            mWifiP2pChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
4830                        } else {
4831                            loge("WifiP2pService connection failure, error=" + message.arg1);
4832                        }
4833                    } else {
4834                        loge("got HALF_CONNECTED for unknown channel");
4835                    }
4836                    break;
4837                }
4838                case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
4839                    AsyncChannel ac = (AsyncChannel) message.obj;
4840                    if (ac == mWifiP2pChannel) {
4841                        loge("WifiP2pService channel lost, message.arg1 =" + message.arg1);
4842                        //TODO: Re-establish connection to state machine after a delay
4843                        // mWifiP2pChannel.connect(mContext, getHandler(),
4844                        // mWifiP2pManager.getMessenger());
4845                    }
4846                    break;
4847                }
4848                case CMD_BLUETOOTH_ADAPTER_STATE_CHANGE:
4849                    mBluetoothConnectionActive = (message.arg1 !=
4850                            BluetoothAdapter.STATE_DISCONNECTED);
4851                    break;
4852                    /* Synchronous call returns */
4853                case CMD_PING_SUPPLICANT:
4854                case CMD_ENABLE_NETWORK:
4855                case CMD_ADD_OR_UPDATE_NETWORK:
4856                case CMD_REMOVE_NETWORK:
4857                case CMD_SAVE_CONFIG:
4858                    replyToMessage(message, message.what, FAILURE);
4859                    break;
4860                case CMD_GET_CAPABILITY_FREQ:
4861                    replyToMessage(message, message.what, null);
4862                    break;
4863                case CMD_GET_CONFIGURED_NETWORKS:
4864                    replyToMessage(message, message.what, (List<WifiConfiguration>) null);
4865                    break;
4866                case CMD_GET_PRIVILEGED_CONFIGURED_NETWORKS:
4867                    replyToMessage(message, message.what, (List<WifiConfiguration>) null);
4868                    break;
4869                case CMD_ENABLE_RSSI_POLL:
4870                    mEnableRssiPolling = (message.arg1 == 1);
4871                    break;
4872                case CMD_SET_HIGH_PERF_MODE:
4873                    if (message.arg1 == 1) {
4874                        setSuspendOptimizations(SUSPEND_DUE_TO_HIGH_PERF, false);
4875                    } else {
4876                        setSuspendOptimizations(SUSPEND_DUE_TO_HIGH_PERF, true);
4877                    }
4878                    break;
4879                case CMD_BOOT_COMPLETED:
4880                    maybeRegisterNetworkFactory();
4881                    break;
4882                case CMD_SET_BATCHED_SCAN:
4883                    recordBatchedScanSettings(message.arg1, message.arg2, (Bundle)message.obj);
4884                    break;
4885                case CMD_POLL_BATCHED_SCAN:
4886                    handleBatchedScanPollRequest();
4887                    break;
4888                case CMD_START_NEXT_BATCHED_SCAN:
4889                    startNextBatchedScan();
4890                    break;
4891                case CMD_SCREEN_STATE_CHANGED:
4892                    handleScreenStateChanged(message.arg1 != 0,
4893                            /* startBackgroundScanIfNeeded = */ false);
4894                    break;
4895                    /* Discard */
4896                case CMD_START_SCAN:
4897                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
4898                    break;
4899                case CMD_START_SUPPLICANT:
4900                case CMD_STOP_SUPPLICANT:
4901                case CMD_STOP_SUPPLICANT_FAILED:
4902                case CMD_START_DRIVER:
4903                case CMD_STOP_DRIVER:
4904                case CMD_DELAYED_STOP_DRIVER:
4905                case CMD_DRIVER_START_TIMED_OUT:
4906                case CMD_START_AP:
4907                case CMD_START_AP_SUCCESS:
4908                case CMD_START_AP_FAILURE:
4909                case CMD_STOP_AP:
4910                case CMD_TETHER_STATE_CHANGE:
4911                case CMD_TETHER_NOTIFICATION_TIMED_OUT:
4912                case CMD_DISCONNECT:
4913                case CMD_RECONNECT:
4914                case CMD_REASSOCIATE:
4915                case CMD_RELOAD_TLS_AND_RECONNECT:
4916                case WifiMonitor.SUP_CONNECTION_EVENT:
4917                case WifiMonitor.SUP_DISCONNECTION_EVENT:
4918                case WifiMonitor.NETWORK_CONNECTION_EVENT:
4919                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
4920                case WifiMonitor.SCAN_RESULTS_EVENT:
4921                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
4922                case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
4923                case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
4924                case WifiMonitor.WPS_OVERLAP_EVENT:
4925                case CMD_BLACKLIST_NETWORK:
4926                case CMD_CLEAR_BLACKLIST:
4927                case CMD_SET_OPERATIONAL_MODE:
4928                case CMD_SET_COUNTRY_CODE:
4929                case CMD_SET_FREQUENCY_BAND:
4930                case CMD_RSSI_POLL:
4931                case CMD_ENABLE_ALL_NETWORKS:
4932                case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
4933                case DhcpStateMachine.CMD_POST_DHCP_ACTION:
4934                /* Handled by WifiApConfigStore */
4935                case CMD_SET_AP_CONFIG:
4936                case CMD_SET_AP_CONFIG_COMPLETED:
4937                case CMD_REQUEST_AP_CONFIG:
4938                case CMD_RESPONSE_AP_CONFIG:
4939                case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
4940                case WifiWatchdogStateMachine.GOOD_LINK_DETECTED:
4941                case CMD_NO_NETWORKS_PERIODIC_SCAN:
4942                case CMD_DISABLE_P2P_RSP:
4943                case WifiMonitor.SUP_REQUEST_IDENTITY:
4944                case CMD_TEST_NETWORK_DISCONNECT:
4945                case CMD_OBTAINING_IP_ADDRESS_WATCHDOG_TIMER:
4946                case WifiMonitor.SUP_REQUEST_SIM_AUTH:
4947                case CMD_TARGET_BSSID:
4948                case CMD_AUTO_CONNECT:
4949                case CMD_AUTO_ROAM:
4950                case CMD_AUTO_SAVE_NETWORK:
4951                case CMD_ASSOCIATED_BSSID:
4952                case CMD_UNWANTED_NETWORK:
4953                case CMD_DISCONNECTING_WATCHDOG_TIMER:
4954                case CMD_ROAM_WATCHDOG_TIMER:
4955                case CMD_DISABLE_EPHEMERAL_NETWORK:
4956                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
4957                    break;
4958                case DhcpStateMachine.CMD_ON_QUIT:
4959                    mDhcpStateMachine = null;
4960                    break;
4961                case CMD_SET_SUSPEND_OPT_ENABLED:
4962                    if (message.arg1 == 1) {
4963                        mSuspendWakeLock.release();
4964                        setSuspendOptimizations(SUSPEND_DUE_TO_SCREEN, true);
4965                    } else {
4966                        setSuspendOptimizations(SUSPEND_DUE_TO_SCREEN, false);
4967                    }
4968                    break;
4969                case WifiMonitor.DRIVER_HUNG_EVENT:
4970                    setSupplicantRunning(false);
4971                    setSupplicantRunning(true);
4972                    break;
4973                case WifiManager.CONNECT_NETWORK:
4974                    replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
4975                            WifiManager.BUSY);
4976                    break;
4977                case WifiManager.FORGET_NETWORK:
4978                    replyToMessage(message, WifiManager.FORGET_NETWORK_FAILED,
4979                            WifiManager.BUSY);
4980                    break;
4981                case WifiManager.SAVE_NETWORK:
4982                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
4983                    replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED,
4984                            WifiManager.BUSY);
4985                    break;
4986                case WifiManager.START_WPS:
4987                    replyToMessage(message, WifiManager.WPS_FAILED,
4988                            WifiManager.BUSY);
4989                    break;
4990                case WifiManager.CANCEL_WPS:
4991                    replyToMessage(message, WifiManager.CANCEL_WPS_FAILED,
4992                            WifiManager.BUSY);
4993                    break;
4994                case WifiManager.DISABLE_NETWORK:
4995                    replyToMessage(message, WifiManager.DISABLE_NETWORK_FAILED,
4996                            WifiManager.BUSY);
4997                    break;
4998                case WifiManager.RSSI_PKTCNT_FETCH:
4999                    replyToMessage(message, WifiManager.RSSI_PKTCNT_FETCH_FAILED,
5000                            WifiManager.BUSY);
5001                    break;
5002                case CMD_GET_SUPPORTED_FEATURES:
5003                    if (WifiNative.startHal()) {
5004                        int featureSet = WifiNative.getSupportedFeatureSet();
5005                        replyToMessage(message, message.what, featureSet);
5006                    } else {
5007                        replyToMessage(message, message.what, 0);
5008                    }
5009                    break;
5010                case CMD_GET_LINK_LAYER_STATS:
5011                    // Not supported hence reply with error message
5012                    replyToMessage(message, message.what, null);
5013                    break;
5014                case WifiP2pServiceImpl.P2P_CONNECTION_CHANGED:
5015                    NetworkInfo info = (NetworkInfo) message.obj;
5016                    mP2pConnected.set(info.isConnected());
5017                    break;
5018                case WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST:
5019                    mTemporarilyDisconnectWifi = (message.arg1 == 1);
5020                    replyToMessage(message, WifiP2pServiceImpl.DISCONNECT_WIFI_RESPONSE);
5021                    break;
5022                /* Link configuration (IP address, DNS, ...) changes notified via netlink */
5023                case CMD_UPDATE_LINKPROPERTIES:
5024                    updateLinkProperties(CMD_UPDATE_LINKPROPERTIES);
5025                    break;
5026                case CMD_IP_CONFIGURATION_SUCCESSFUL:
5027                case CMD_IP_CONFIGURATION_LOST:
5028                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
5029                    break;
5030                case CMD_GET_CONNECTION_STATISTICS:
5031                    replyToMessage(message, message.what, mWifiConnectionStatistics);
5032                    break;
5033                default:
5034                    loge("Error! unhandled message" + message);
5035                    break;
5036            }
5037            return HANDLED;
5038        }
5039    }
5040
5041    class InitialState extends State {
5042        @Override
5043        public void enter() {
5044            mWifiNative.unloadDriver();
5045
5046            if (mWifiP2pChannel == null) {
5047                mWifiP2pChannel = new AsyncChannel();
5048                mWifiP2pChannel.connect(mContext, getHandler(),
5049                    mWifiP2pServiceImpl.getP2pStateMachineMessenger());
5050            }
5051
5052            if (mWifiApConfigChannel == null) {
5053                mWifiApConfigChannel = new AsyncChannel();
5054                WifiApConfigStore wifiApConfigStore = WifiApConfigStore.makeWifiApConfigStore(
5055                        mContext, getHandler());
5056                wifiApConfigStore.loadApConfiguration();
5057                mWifiApConfigChannel.connectSync(mContext, getHandler(),
5058                        wifiApConfigStore.getMessenger());
5059            }
5060        }
5061        @Override
5062        public boolean processMessage(Message message) {
5063            logStateAndMessage(message, getClass().getSimpleName());
5064            switch (message.what) {
5065                case CMD_START_SUPPLICANT:
5066                    if (mWifiNative.loadDriver()) {
5067                        try {
5068                            mNwService.wifiFirmwareReload(mInterfaceName, "STA");
5069                        } catch (Exception e) {
5070                            loge("Failed to reload STA firmware " + e);
5071                            // Continue
5072                        }
5073
5074                        try {
5075                            // A runtime crash can leave the interface up and
5076                            // IP addresses configured, and this affects
5077                            // connectivity when supplicant starts up.
5078                            // Ensure interface is down and we have no IP
5079                            // addresses before a supplicant start.
5080                            mNwService.setInterfaceDown(mInterfaceName);
5081                            mNwService.clearInterfaceAddresses(mInterfaceName);
5082
5083                            // Set privacy extensions
5084                            mNwService.setInterfaceIpv6PrivacyExtensions(mInterfaceName, true);
5085
5086                           // IPv6 is enabled only as long as access point is connected since:
5087                           // - IPv6 addresses and routes stick around after disconnection
5088                           // - kernel is unaware when connected and fails to start IPv6 negotiation
5089                           // - kernel can start autoconfiguration when 802.1x is not complete
5090                            mNwService.disableIpv6(mInterfaceName);
5091                        } catch (RemoteException re) {
5092                            loge("Unable to change interface settings: " + re);
5093                        } catch (IllegalStateException ie) {
5094                            loge("Unable to change interface settings: " + ie);
5095                        }
5096
5097                       /* Stop a running supplicant after a runtime restart
5098                        * Avoids issues with drivers that do not handle interface down
5099                        * on a running supplicant properly.
5100                        */
5101                        mWifiMonitor.killSupplicant(mP2pSupported);
5102                        if(mWifiNative.startSupplicant(mP2pSupported)) {
5103                            setWifiState(WIFI_STATE_ENABLING);
5104                            if (DBG) log("Supplicant start successful");
5105                            mWifiMonitor.startMonitoring();
5106                            transitionTo(mSupplicantStartingState);
5107                        } else {
5108                            loge("Failed to start supplicant!");
5109                        }
5110                    } else {
5111                        loge("Failed to load driver");
5112                    }
5113                    break;
5114                case CMD_START_AP:
5115                    if (mWifiNative.loadDriver()) {
5116                        setWifiApState(WIFI_AP_STATE_ENABLING);
5117                        transitionTo(mSoftApStartingState);
5118                    } else {
5119                        loge("Failed to load driver for softap");
5120                    }
5121                    break;
5122                default:
5123                    return NOT_HANDLED;
5124            }
5125            return HANDLED;
5126        }
5127    }
5128
5129    class SupplicantStartingState extends State {
5130        private void initializeWpsDetails() {
5131            String detail;
5132            detail = SystemProperties.get("ro.product.name", "");
5133            if (!mWifiNative.setDeviceName(detail)) {
5134                loge("Failed to set device name " +  detail);
5135            }
5136            detail = SystemProperties.get("ro.product.manufacturer", "");
5137            if (!mWifiNative.setManufacturer(detail)) {
5138                loge("Failed to set manufacturer " + detail);
5139            }
5140            detail = SystemProperties.get("ro.product.model", "");
5141            if (!mWifiNative.setModelName(detail)) {
5142                loge("Failed to set model name " + detail);
5143            }
5144            detail = SystemProperties.get("ro.product.model", "");
5145            if (!mWifiNative.setModelNumber(detail)) {
5146                loge("Failed to set model number " + detail);
5147            }
5148            detail = SystemProperties.get("ro.serialno", "");
5149            if (!mWifiNative.setSerialNumber(detail)) {
5150                loge("Failed to set serial number " + detail);
5151            }
5152            if (!mWifiNative.setConfigMethods("physical_display virtual_push_button")) {
5153                loge("Failed to set WPS config methods");
5154            }
5155            if (!mWifiNative.setDeviceType(mPrimaryDeviceType)) {
5156                loge("Failed to set primary device type " + mPrimaryDeviceType);
5157            }
5158        }
5159
5160        @Override
5161        public boolean processMessage(Message message) {
5162            logStateAndMessage(message, getClass().getSimpleName());
5163
5164            switch(message.what) {
5165                case WifiMonitor.SUP_CONNECTION_EVENT:
5166                    if (DBG) log("Supplicant connection established");
5167                    setWifiState(WIFI_STATE_ENABLED);
5168                    mSupplicantRestartCount = 0;
5169                    /* Reset the supplicant state to indicate the supplicant
5170                     * state is not known at this time */
5171                    mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
5172                    /* Initialize data structures */
5173                    mLastBssid = null;
5174                    mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
5175                    mLastSignalLevel = -1;
5176
5177                    mWifiInfo.setMacAddress(mWifiNative.getMacAddress());
5178                    mWifiNative.enableSaveConfig();
5179                    mWifiConfigStore.loadAndEnableAllNetworks();
5180                    if (mWifiConfigStore.enableVerboseLogging.get() > 0) {
5181                        enableVerboseLogging(mWifiConfigStore.enableVerboseLogging.get());
5182                    }
5183                    if (mWifiConfigStore.associatedPartialScanPeriodMilli.get() < 0) {
5184                        mWifiConfigStore.associatedPartialScanPeriodMilli.set(0);
5185                    }
5186                    initializeWpsDetails();
5187
5188                    sendSupplicantConnectionChangedBroadcast(true);
5189                    transitionTo(mDriverStartedState);
5190                    break;
5191                case WifiMonitor.SUP_DISCONNECTION_EVENT:
5192                    if (++mSupplicantRestartCount <= SUPPLICANT_RESTART_TRIES) {
5193                        loge("Failed to setup control channel, restart supplicant");
5194                        mWifiMonitor.killSupplicant(mP2pSupported);
5195                        transitionTo(mInitialState);
5196                        sendMessageDelayed(CMD_START_SUPPLICANT, SUPPLICANT_RESTART_INTERVAL_MSECS);
5197                    } else {
5198                        loge("Failed " + mSupplicantRestartCount +
5199                                " times to start supplicant, unload driver");
5200                        mSupplicantRestartCount = 0;
5201                        setWifiState(WIFI_STATE_UNKNOWN);
5202                        transitionTo(mInitialState);
5203                    }
5204                    break;
5205                case CMD_START_SUPPLICANT:
5206                case CMD_STOP_SUPPLICANT:
5207                case CMD_START_AP:
5208                case CMD_STOP_AP:
5209                case CMD_START_DRIVER:
5210                case CMD_STOP_DRIVER:
5211                case CMD_SET_OPERATIONAL_MODE:
5212                case CMD_SET_COUNTRY_CODE:
5213                case CMD_SET_FREQUENCY_BAND:
5214                case CMD_START_PACKET_FILTERING:
5215                case CMD_STOP_PACKET_FILTERING:
5216                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_DEFERRED;
5217                    deferMessage(message);
5218                    break;
5219                default:
5220                    return NOT_HANDLED;
5221            }
5222            return HANDLED;
5223        }
5224    }
5225
5226    class SupplicantStartedState extends State {
5227        @Override
5228        public void enter() {
5229            /* Wifi is available as long as we have a connection to supplicant */
5230            mNetworkInfo.setIsAvailable(true);
5231            if (mNetworkAgent != null) mNetworkAgent.sendNetworkInfo(mNetworkInfo);
5232
5233            int defaultInterval = mContext.getResources().getInteger(
5234                    R.integer.config_wifi_supplicant_scan_interval);
5235
5236            mSupplicantScanIntervalMs = Settings.Global.getLong(mContext.getContentResolver(),
5237                    Settings.Global.WIFI_SUPPLICANT_SCAN_INTERVAL_MS,
5238                    defaultInterval);
5239
5240            mWifiNative.setScanInterval((int)mSupplicantScanIntervalMs / 1000);
5241            mWifiNative.setExternalSim(true);
5242
5243            /* turn on use of DFS channels */
5244            WifiNative.setDfsFlag(true);
5245
5246            /* set country code */
5247            setCountryCode();
5248
5249            setRandomMacOui();
5250            mWifiNative.enableAutoConnect(false);
5251        }
5252
5253        @Override
5254        public boolean processMessage(Message message) {
5255            logStateAndMessage(message, getClass().getSimpleName());
5256
5257            switch(message.what) {
5258                case CMD_STOP_SUPPLICANT:   /* Supplicant stopped by user */
5259                    if (mP2pSupported) {
5260                        transitionTo(mWaitForP2pDisableState);
5261                    } else {
5262                        transitionTo(mSupplicantStoppingState);
5263                    }
5264                    break;
5265                case WifiMonitor.SUP_DISCONNECTION_EVENT:  /* Supplicant connection lost */
5266                    loge("Connection lost, restart supplicant");
5267                    handleSupplicantConnectionLoss();
5268                    handleNetworkDisconnect();
5269                    mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
5270                    if (mP2pSupported) {
5271                        transitionTo(mWaitForP2pDisableState);
5272                    } else {
5273                        transitionTo(mInitialState);
5274                    }
5275                    sendMessageDelayed(CMD_START_SUPPLICANT, SUPPLICANT_RESTART_INTERVAL_MSECS);
5276                    break;
5277                case WifiMonitor.SCAN_RESULTS_EVENT:
5278                    maybeRegisterNetworkFactory(); // Make sure our NetworkFactory is registered
5279                    closeRadioScanStats();
5280                    noteScanEnd();
5281                    setScanResults();
5282                    if (mIsFullScanOngoing || mSendScanResultsBroadcast) {
5283                        /* Just updated results from full scan, let apps know about this */
5284                        sendScanResultsAvailableBroadcast();
5285                    }
5286                    mSendScanResultsBroadcast = false;
5287                    mIsScanOngoing = false;
5288                    mIsFullScanOngoing = false;
5289                    if (mBufferedScanMsg.size() > 0)
5290                        sendMessage(mBufferedScanMsg.remove());
5291                    break;
5292                case CMD_PING_SUPPLICANT:
5293                    boolean ok = mWifiNative.ping();
5294                    replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
5295                    break;
5296                case CMD_GET_CAPABILITY_FREQ:
5297                    String freqs = mWifiNative.getFreqCapability();
5298                    replyToMessage(message, message.what, freqs);
5299                    break;
5300                case CMD_START_AP:
5301                    /* Cannot start soft AP while in client mode */
5302                    loge("Failed to start soft AP with a running supplicant");
5303                    setWifiApState(WIFI_AP_STATE_FAILED);
5304                    break;
5305                case CMD_SET_OPERATIONAL_MODE:
5306                    mOperationalMode = message.arg1;
5307                    mWifiConfigStore.
5308                            setLastSelectedConfiguration(WifiConfiguration.INVALID_NETWORK_ID);
5309                    break;
5310                case CMD_TARGET_BSSID:
5311                    // Trying to associate to this BSSID
5312                    if (message.obj != null) {
5313                        mTargetRoamBSSID = (String) message.obj;
5314                    }
5315                    break;
5316                case CMD_GET_LINK_LAYER_STATS:
5317                    WifiLinkLayerStats stats = getWifiLinkLayerStats(DBG);
5318                    if (stats == null) {
5319                        // When firmware doesnt support link layer stats, return an empty object
5320                        stats = new WifiLinkLayerStats();
5321                    }
5322                    replyToMessage(message, message.what, stats);
5323                    break;
5324                case CMD_SET_COUNTRY_CODE:
5325                    String country = (String) message.obj;
5326
5327                    final boolean persist = (message.arg2 == 1);
5328                    final int sequence = message.arg1;
5329
5330                    if (sequence != mCountryCodeSequence.get()) {
5331                        if (DBG) log("set country code ignored due to sequnce num");
5332                        break;
5333                    }
5334                    if (DBG) log("set country code " + country);
5335                    country = country.toUpperCase(Locale.ROOT);
5336
5337                    if (mDriverSetCountryCode == null || !mDriverSetCountryCode.equals(country)) {
5338                        if (mWifiNative.setCountryCode(country)) {
5339                            mDriverSetCountryCode = country;
5340                        } else {
5341                            loge("Failed to set country code " + country);
5342                        }
5343                    }
5344
5345                    mWifiP2pChannel.sendMessage(WifiP2pServiceImpl.SET_COUNTRY_CODE, country);
5346                    break;
5347                default:
5348                    return NOT_HANDLED;
5349            }
5350            return HANDLED;
5351        }
5352
5353        @Override
5354        public void exit() {
5355            mNetworkInfo.setIsAvailable(false);
5356            if (mNetworkAgent != null) mNetworkAgent.sendNetworkInfo(mNetworkInfo);
5357        }
5358    }
5359
5360    class SupplicantStoppingState extends State {
5361        @Override
5362        public void enter() {
5363            /* Send any reset commands to supplicant before shutting it down */
5364            handleNetworkDisconnect();
5365            if (mDhcpStateMachine != null) {
5366                mDhcpStateMachine.doQuit();
5367            }
5368
5369            String suppState = System.getProperty("init.svc.wpa_supplicant");
5370            if (suppState == null) suppState = "unknown";
5371            String p2pSuppState = System.getProperty("init.svc.p2p_supplicant");
5372            if (p2pSuppState == null) p2pSuppState = "unknown";
5373
5374            loge("SupplicantStoppingState: stopSupplicant "
5375                    + " init.svc.wpa_supplicant=" + suppState
5376                    + " init.svc.p2p_supplicant=" + p2pSuppState);
5377            mWifiMonitor.stopSupplicant();
5378
5379            /* Send ourselves a delayed message to indicate failure after a wait time */
5380            sendMessageDelayed(obtainMessage(CMD_STOP_SUPPLICANT_FAILED,
5381                    ++mSupplicantStopFailureToken, 0), SUPPLICANT_RESTART_INTERVAL_MSECS);
5382            setWifiState(WIFI_STATE_DISABLING);
5383            mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
5384        }
5385        @Override
5386        public boolean processMessage(Message message) {
5387            logStateAndMessage(message, getClass().getSimpleName());
5388
5389            switch(message.what) {
5390                case WifiMonitor.SUP_CONNECTION_EVENT:
5391                    loge("Supplicant connection received while stopping");
5392                    break;
5393                case WifiMonitor.SUP_DISCONNECTION_EVENT:
5394                    if (DBG) log("Supplicant connection lost");
5395                    handleSupplicantConnectionLoss();
5396                    transitionTo(mInitialState);
5397                    break;
5398                case CMD_STOP_SUPPLICANT_FAILED:
5399                    if (message.arg1 == mSupplicantStopFailureToken) {
5400                        loge("Timed out on a supplicant stop, kill and proceed");
5401                        handleSupplicantConnectionLoss();
5402                        transitionTo(mInitialState);
5403                    }
5404                    break;
5405                case CMD_START_SUPPLICANT:
5406                case CMD_STOP_SUPPLICANT:
5407                case CMD_START_AP:
5408                case CMD_STOP_AP:
5409                case CMD_START_DRIVER:
5410                case CMD_STOP_DRIVER:
5411                case CMD_SET_OPERATIONAL_MODE:
5412                case CMD_SET_COUNTRY_CODE:
5413                case CMD_SET_FREQUENCY_BAND:
5414                case CMD_START_PACKET_FILTERING:
5415                case CMD_STOP_PACKET_FILTERING:
5416                    deferMessage(message);
5417                    break;
5418                default:
5419                    return NOT_HANDLED;
5420            }
5421            return HANDLED;
5422        }
5423    }
5424
5425    class DriverStartingState extends State {
5426        private int mTries;
5427        @Override
5428        public void enter() {
5429            mTries = 1;
5430            /* Send ourselves a delayed message to start driver a second time */
5431            sendMessageDelayed(obtainMessage(CMD_DRIVER_START_TIMED_OUT,
5432                        ++mDriverStartToken, 0), DRIVER_START_TIME_OUT_MSECS);
5433        }
5434        @Override
5435        public boolean processMessage(Message message) {
5436            logStateAndMessage(message, getClass().getSimpleName());
5437
5438            switch(message.what) {
5439               case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
5440                    SupplicantState state = handleSupplicantStateChange(message);
5441                    /* If suplicant is exiting out of INTERFACE_DISABLED state into
5442                     * a state that indicates driver has started, it is ready to
5443                     * receive driver commands
5444                     */
5445                    if (SupplicantState.isDriverActive(state)) {
5446                        transitionTo(mDriverStartedState);
5447                    }
5448                    break;
5449                case CMD_DRIVER_START_TIMED_OUT:
5450                    if (message.arg1 == mDriverStartToken) {
5451                        if (mTries >= 2) {
5452                            loge("Failed to start driver after " + mTries);
5453                            transitionTo(mDriverStoppedState);
5454                        } else {
5455                            loge("Driver start failed, retrying");
5456                            mWakeLock.acquire();
5457                            mWifiNative.startDriver();
5458                            mWakeLock.release();
5459
5460                            ++mTries;
5461                            /* Send ourselves a delayed message to start driver again */
5462                            sendMessageDelayed(obtainMessage(CMD_DRIVER_START_TIMED_OUT,
5463                                        ++mDriverStartToken, 0), DRIVER_START_TIME_OUT_MSECS);
5464                        }
5465                    }
5466                    break;
5467                    /* Queue driver commands & connection events */
5468                case CMD_START_DRIVER:
5469                case CMD_STOP_DRIVER:
5470                case WifiMonitor.NETWORK_CONNECTION_EVENT:
5471                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
5472                case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
5473                case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
5474                case WifiMonitor.WPS_OVERLAP_EVENT:
5475                case CMD_SET_COUNTRY_CODE:
5476                case CMD_SET_FREQUENCY_BAND:
5477                case CMD_START_PACKET_FILTERING:
5478                case CMD_STOP_PACKET_FILTERING:
5479                case CMD_START_SCAN:
5480                case CMD_DISCONNECT:
5481                case CMD_REASSOCIATE:
5482                case CMD_RECONNECT:
5483                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_DEFERRED;
5484                    deferMessage(message);
5485                    break;
5486                case WifiMonitor.SCAN_RESULTS_EVENT:
5487                    // Loose scan results obtained in Driver Starting state, they can only confuse
5488                    // the state machine
5489                    break;
5490                default:
5491                    return NOT_HANDLED;
5492            }
5493            return HANDLED;
5494        }
5495    }
5496
5497    class DriverStartedState extends State {
5498        @Override
5499        public void enter() {
5500
5501            if (PDBG) {
5502                loge("DriverStartedState enter");
5503            }
5504            mIsRunning = true;
5505            mInDelayedStop = false;
5506            mDelayedStopCounter++;
5507            updateBatteryWorkSource(null);
5508            /**
5509             * Enable bluetooth coexistence scan mode when bluetooth connection is active.
5510             * When this mode is on, some of the low-level scan parameters used by the
5511             * driver are changed to reduce interference with bluetooth
5512             */
5513            mWifiNative.setBluetoothCoexistenceScanMode(mBluetoothConnectionActive);
5514            /* set frequency band of operation */
5515            setFrequencyBand();
5516            /* initialize network state */
5517            setNetworkDetailedState(DetailedState.DISCONNECTED);
5518
5519            /* Remove any filtering on Multicast v6 at start */
5520            mWifiNative.stopFilteringMulticastV6Packets();
5521
5522            /* Reset Multicast v4 filtering state */
5523            if (mFilteringMulticastV4Packets.get()) {
5524                mWifiNative.startFilteringMulticastV4Packets();
5525            } else {
5526                mWifiNative.stopFilteringMulticastV4Packets();
5527            }
5528
5529            mDhcpActive = false;
5530
5531            startBatchedScan();
5532
5533            if (mOperationalMode != CONNECT_MODE) {
5534                mWifiNative.disconnect();
5535                mWifiConfigStore.disableAllNetworks();
5536                if (mOperationalMode == SCAN_ONLY_WITH_WIFI_OFF_MODE) {
5537                    setWifiState(WIFI_STATE_DISABLED);
5538                }
5539                transitionTo(mScanModeState);
5540            } else {
5541
5542                // Status pulls in the current supplicant state and network connection state
5543                // events over the monitor connection. This helps framework sync up with
5544                // current supplicant state
5545                // TODO: actually check th supplicant status string and make sure the supplicant
5546                // is in disconnecte4d state.
5547                mWifiNative.status();
5548                // Transitioning to Disconnected state will trigger a scan and subsequently AutoJoin
5549                transitionTo(mDisconnectedState);
5550            }
5551
5552            // We may have missed screen update at boot
5553            if (mScreenBroadcastReceived.get() == false) {
5554                PowerManager powerManager = (PowerManager)mContext.getSystemService(
5555                        Context.POWER_SERVICE);
5556                handleScreenStateChanged(powerManager.isScreenOn(),
5557                        /* startBackgroundScanIfNeeded = */ false);
5558            } else {
5559                // Set the right suspend mode settings
5560                mWifiNative.setSuspendOptimizations(mSuspendOptNeedsDisabled == 0
5561                        && mUserWantsSuspendOpt.get());
5562            }
5563            mWifiNative.setPowerSave(true);
5564
5565            if (mP2pSupported) {
5566                if (mOperationalMode == CONNECT_MODE) {
5567                    mWifiP2pChannel.sendMessage(WifiStateMachine.CMD_ENABLE_P2P);
5568                } else {
5569                    // P2P statemachine starts in disabled state, and is not enabled until
5570                    // CMD_ENABLE_P2P is sent from here; so, nothing needs to be done to
5571                    // keep it disabled.
5572                }
5573            }
5574
5575            final Intent intent = new Intent(WifiManager.WIFI_SCAN_AVAILABLE);
5576            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
5577            intent.putExtra(WifiManager.EXTRA_SCAN_AVAILABLE, WIFI_STATE_ENABLED);
5578            mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
5579
5580            if (PDBG) {
5581                loge("Driverstarted State enter done");
5582            }
5583        }
5584
5585        @Override
5586        public boolean processMessage(Message message) {
5587            logStateAndMessage(message, getClass().getSimpleName());
5588
5589            switch(message.what) {
5590                case CMD_START_SCAN:
5591                    handleScanRequest(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, message);
5592                    break;
5593                case CMD_SET_BATCHED_SCAN:
5594                    if (recordBatchedScanSettings(message.arg1, message.arg2,
5595                            (Bundle) message.obj)) {
5596                        if (mBatchedScanSettings != null) {
5597                            startBatchedScan();
5598                        } else {
5599                            stopBatchedScan();
5600                        }
5601                    }
5602                    break;
5603                case CMD_SET_FREQUENCY_BAND:
5604                    int band =  message.arg1;
5605                    if (DBG) log("set frequency band " + band);
5606                    if (mWifiNative.setBand(band)) {
5607
5608                        if (PDBG)  loge("did set frequency band " + band);
5609
5610                        mFrequencyBand.set(band);
5611                        // Flush old data - like scan results
5612                        mWifiNative.bssFlush();
5613                        // Fetch the latest scan results when frequency band is set
5614                        startScanNative(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, null);
5615
5616                        if (PDBG)  loge("done set frequency band " + band);
5617
5618                    } else {
5619                        loge("Failed to set frequency band " + band);
5620                    }
5621                    break;
5622                case CMD_BLUETOOTH_ADAPTER_STATE_CHANGE:
5623                    mBluetoothConnectionActive = (message.arg1 !=
5624                            BluetoothAdapter.STATE_DISCONNECTED);
5625                    mWifiNative.setBluetoothCoexistenceScanMode(mBluetoothConnectionActive);
5626                    break;
5627                case CMD_STOP_DRIVER:
5628                    int mode = message.arg1;
5629
5630                    /* Already doing a delayed stop */
5631                    if (mInDelayedStop) {
5632                        if (DBG) log("Already in delayed stop");
5633                        break;
5634                    }
5635                    /* disconnect right now, but leave the driver running for a bit */
5636                    mWifiConfigStore.disableAllNetworks();
5637
5638                    mInDelayedStop = true;
5639                    mDelayedStopCounter++;
5640                    if (DBG) log("Delayed stop message " + mDelayedStopCounter);
5641
5642                    /* send regular delayed shut down */
5643                    Intent driverStopIntent = new Intent(ACTION_DELAYED_DRIVER_STOP, null);
5644                    driverStopIntent.setPackage(this.getClass().getPackage().getName());
5645                    driverStopIntent.putExtra(DELAYED_STOP_COUNTER, mDelayedStopCounter);
5646                    mDriverStopIntent = PendingIntent.getBroadcast(mContext,
5647                            DRIVER_STOP_REQUEST, driverStopIntent,
5648                            PendingIntent.FLAG_UPDATE_CURRENT);
5649
5650                    mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()
5651                            + mDriverStopDelayMs, mDriverStopIntent);
5652                    break;
5653                case CMD_START_DRIVER:
5654                    if (mInDelayedStop) {
5655                        mInDelayedStop = false;
5656                        mDelayedStopCounter++;
5657                        mAlarmManager.cancel(mDriverStopIntent);
5658                        if (DBG) log("Delayed stop ignored due to start");
5659                        if (mOperationalMode == CONNECT_MODE) {
5660                            mWifiConfigStore.enableAllNetworks();
5661                        }
5662                    }
5663                    break;
5664                case CMD_DELAYED_STOP_DRIVER:
5665                    if (DBG) log("delayed stop " + message.arg1 + " " + mDelayedStopCounter);
5666                    if (message.arg1 != mDelayedStopCounter) break;
5667                    if (getCurrentState() != mDisconnectedState) {
5668                        mWifiNative.disconnect();
5669                        handleNetworkDisconnect();
5670                    }
5671                    mWakeLock.acquire();
5672                    mWifiNative.stopDriver();
5673                    mWakeLock.release();
5674                    if (mP2pSupported) {
5675                        transitionTo(mWaitForP2pDisableState);
5676                    } else {
5677                        transitionTo(mDriverStoppingState);
5678                    }
5679                    break;
5680                case CMD_START_PACKET_FILTERING:
5681                    if (message.arg1 == MULTICAST_V6) {
5682                        mWifiNative.startFilteringMulticastV6Packets();
5683                    } else if (message.arg1 == MULTICAST_V4) {
5684                        mWifiNative.startFilteringMulticastV4Packets();
5685                    } else {
5686                        loge("Illegal arugments to CMD_START_PACKET_FILTERING");
5687                    }
5688                    break;
5689                case CMD_STOP_PACKET_FILTERING:
5690                    if (message.arg1 == MULTICAST_V6) {
5691                        mWifiNative.stopFilteringMulticastV6Packets();
5692                    } else if (message.arg1 == MULTICAST_V4) {
5693                        mWifiNative.stopFilteringMulticastV4Packets();
5694                    } else {
5695                        loge("Illegal arugments to CMD_STOP_PACKET_FILTERING");
5696                    }
5697                    break;
5698                case CMD_SET_SUSPEND_OPT_ENABLED:
5699                    if (message.arg1 == 1) {
5700                        setSuspendOptimizationsNative(SUSPEND_DUE_TO_SCREEN, true);
5701                        mSuspendWakeLock.release();
5702                    } else {
5703                        setSuspendOptimizationsNative(SUSPEND_DUE_TO_SCREEN, false);
5704                    }
5705                    break;
5706                case CMD_SET_HIGH_PERF_MODE:
5707                    if (message.arg1 == 1) {
5708                        setSuspendOptimizationsNative(SUSPEND_DUE_TO_HIGH_PERF, false);
5709                    } else {
5710                        setSuspendOptimizationsNative(SUSPEND_DUE_TO_HIGH_PERF, true);
5711                    }
5712                    break;
5713                case CMD_ENABLE_TDLS:
5714                    if (message.obj != null) {
5715                        String remoteAddress = (String) message.obj;
5716                        boolean enable = (message.arg1 == 1);
5717                        mWifiNative.startTdls(remoteAddress, enable);
5718                    }
5719                    break;
5720                default:
5721                    return NOT_HANDLED;
5722            }
5723            return HANDLED;
5724        }
5725        @Override
5726        public void exit() {
5727            mIsRunning = false;
5728            updateBatteryWorkSource(null);
5729            mScanResults = new ArrayList<>();
5730
5731            stopBatchedScan();
5732
5733            final Intent intent = new Intent(WifiManager.WIFI_SCAN_AVAILABLE);
5734            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
5735            intent.putExtra(WifiManager.EXTRA_SCAN_AVAILABLE, WIFI_STATE_DISABLED);
5736            mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
5737            noteScanEnd(); // wrap up any pending request.
5738            mBufferedScanMsg.clear();
5739        }
5740    }
5741
5742    class WaitForP2pDisableState extends State {
5743        private State mTransitionToState;
5744        @Override
5745        public void enter() {
5746            switch (getCurrentMessage().what) {
5747                case WifiMonitor.SUP_DISCONNECTION_EVENT:
5748                    mTransitionToState = mInitialState;
5749                    break;
5750                case CMD_DELAYED_STOP_DRIVER:
5751                    mTransitionToState = mDriverStoppingState;
5752                    break;
5753                case CMD_STOP_SUPPLICANT:
5754                    mTransitionToState = mSupplicantStoppingState;
5755                    break;
5756                default:
5757                    mTransitionToState = mDriverStoppingState;
5758                    break;
5759            }
5760            mWifiP2pChannel.sendMessage(WifiStateMachine.CMD_DISABLE_P2P_REQ);
5761        }
5762        @Override
5763        public boolean processMessage(Message message) {
5764            logStateAndMessage(message, getClass().getSimpleName());
5765
5766            switch(message.what) {
5767                case WifiStateMachine.CMD_DISABLE_P2P_RSP:
5768                    transitionTo(mTransitionToState);
5769                    break;
5770                /* Defer wifi start/shut and driver commands */
5771                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
5772                case CMD_START_SUPPLICANT:
5773                case CMD_STOP_SUPPLICANT:
5774                case CMD_START_AP:
5775                case CMD_STOP_AP:
5776                case CMD_START_DRIVER:
5777                case CMD_STOP_DRIVER:
5778                case CMD_SET_OPERATIONAL_MODE:
5779                case CMD_SET_COUNTRY_CODE:
5780                case CMD_SET_FREQUENCY_BAND:
5781                case CMD_START_PACKET_FILTERING:
5782                case CMD_STOP_PACKET_FILTERING:
5783                case CMD_START_SCAN:
5784                case CMD_DISCONNECT:
5785                case CMD_REASSOCIATE:
5786                case CMD_RECONNECT:
5787                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_DEFERRED;
5788                    deferMessage(message);
5789                    break;
5790                default:
5791                    return NOT_HANDLED;
5792            }
5793            return HANDLED;
5794        }
5795    }
5796
5797    class DriverStoppingState extends State {
5798        @Override
5799        public boolean processMessage(Message message) {
5800            logStateAndMessage(message, getClass().getSimpleName());
5801
5802            switch(message.what) {
5803                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
5804                    SupplicantState state = handleSupplicantStateChange(message);
5805                    if (state == SupplicantState.INTERFACE_DISABLED) {
5806                        transitionTo(mDriverStoppedState);
5807                    }
5808                    break;
5809                    /* Queue driver commands */
5810                case CMD_START_DRIVER:
5811                case CMD_STOP_DRIVER:
5812                case CMD_SET_COUNTRY_CODE:
5813                case CMD_SET_FREQUENCY_BAND:
5814                case CMD_START_PACKET_FILTERING:
5815                case CMD_STOP_PACKET_FILTERING:
5816                case CMD_START_SCAN:
5817                case CMD_DISCONNECT:
5818                case CMD_REASSOCIATE:
5819                case CMD_RECONNECT:
5820                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_DEFERRED;
5821                    deferMessage(message);
5822                    break;
5823                default:
5824                    return NOT_HANDLED;
5825            }
5826            return HANDLED;
5827        }
5828    }
5829
5830    class DriverStoppedState extends State {
5831        @Override
5832        public boolean processMessage(Message message) {
5833            logStateAndMessage(message, getClass().getSimpleName());
5834            switch (message.what) {
5835                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
5836                    StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
5837                    SupplicantState state = stateChangeResult.state;
5838                    // A WEXT bug means that we can be back to driver started state
5839                    // unexpectedly
5840                    if (SupplicantState.isDriverActive(state)) {
5841                        transitionTo(mDriverStartedState);
5842                    }
5843                    break;
5844                case CMD_START_DRIVER:
5845                    mWakeLock.acquire();
5846                    mWifiNative.startDriver();
5847                    mWakeLock.release();
5848                    transitionTo(mDriverStartingState);
5849                    break;
5850                default:
5851                    return NOT_HANDLED;
5852            }
5853            return HANDLED;
5854        }
5855    }
5856
5857    class ScanModeState extends State {
5858        private int mLastOperationMode;
5859        @Override
5860        public void enter() {
5861            mLastOperationMode = mOperationalMode;
5862        }
5863        @Override
5864        public boolean processMessage(Message message) {
5865            logStateAndMessage(message, getClass().getSimpleName());
5866
5867            switch(message.what) {
5868                case CMD_SET_OPERATIONAL_MODE:
5869                    if (message.arg1 == CONNECT_MODE) {
5870
5871                        if (mLastOperationMode == SCAN_ONLY_WITH_WIFI_OFF_MODE) {
5872                            setWifiState(WIFI_STATE_ENABLED);
5873                            // Load and re-enable networks when going back to enabled state
5874                            // This is essential for networks to show up after restore
5875                            mWifiConfigStore.loadAndEnableAllNetworks();
5876                            mWifiP2pChannel.sendMessage(CMD_ENABLE_P2P);
5877                        } else {
5878                            mWifiConfigStore.enableAllNetworks();
5879                        }
5880
5881                        // Try autojoining with recent network already present in the cache
5882                        // If none are found then trigger a scan which will trigger autojoin
5883                        // upon reception of scan results event
5884                        if (!mWifiAutoJoinController.attemptAutoJoin()) {
5885                            startScan(ENABLE_WIFI, 0, null, null);
5886                        }
5887
5888                        // Loose last selection choice since user toggled WiFi
5889                        mWifiConfigStore.
5890                                setLastSelectedConfiguration(WifiConfiguration.INVALID_NETWORK_ID);
5891
5892                        mOperationalMode = CONNECT_MODE;
5893                        transitionTo(mDisconnectedState);
5894                    } else {
5895                        // Nothing to do
5896                        return HANDLED;
5897                    }
5898                    break;
5899                // Handle scan. All the connection related commands are
5900                // handled only in ConnectModeState
5901                case CMD_START_SCAN:
5902                    handleScanRequest(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, message);
5903                    break;
5904                default:
5905                    return NOT_HANDLED;
5906            }
5907            return HANDLED;
5908        }
5909    }
5910
5911
5912    String smToString(Message message) {
5913        return smToString(message.what);
5914    }
5915
5916    String smToString(int what) {
5917        String s = "unknown";
5918        switch (what) {
5919            case WifiMonitor.DRIVER_HUNG_EVENT:
5920                s = "DRIVER_HUNG_EVENT";
5921                break;
5922            case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
5923                s = "AsyncChannel.CMD_CHANNEL_HALF_CONNECTED";
5924                break;
5925            case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
5926                s = "AsyncChannel.CMD_CHANNEL_DISCONNECTED";
5927                break;
5928            case CMD_SET_FREQUENCY_BAND:
5929                s = "CMD_SET_FREQUENCY_BAND";
5930                break;
5931            case CMD_DELAYED_NETWORK_DISCONNECT:
5932                s = "CMD_DELAYED_NETWORK_DISCONNECT";
5933                break;
5934            case CMD_TEST_NETWORK_DISCONNECT:
5935                s = "CMD_TEST_NETWORK_DISCONNECT";
5936                break;
5937            case CMD_OBTAINING_IP_ADDRESS_WATCHDOG_TIMER:
5938                s = "CMD_OBTAINING_IP_ADDRESS_WATCHDOG_TIMER";
5939                break;
5940            case CMD_DISABLE_EPHEMERAL_NETWORK:
5941                s = "CMD_DISABLE_EPHEMERAL_NETWORK";
5942                break;
5943            case CMD_START_DRIVER:
5944                s = "CMD_START_DRIVER";
5945                break;
5946            case CMD_STOP_DRIVER:
5947                s = "CMD_STOP_DRIVER";
5948                break;
5949            case CMD_STOP_SUPPLICANT:
5950                s = "CMD_STOP_SUPPLICANT";
5951                break;
5952            case CMD_STOP_SUPPLICANT_FAILED:
5953                s = "CMD_STOP_SUPPLICANT_FAILED";
5954                break;
5955            case CMD_START_SUPPLICANT:
5956                s = "CMD_START_SUPPLICANT";
5957                break;
5958            case CMD_REQUEST_AP_CONFIG:
5959                s = "CMD_REQUEST_AP_CONFIG";
5960                break;
5961            case CMD_RESPONSE_AP_CONFIG:
5962                s = "CMD_RESPONSE_AP_CONFIG";
5963                break;
5964            case CMD_TETHER_STATE_CHANGE:
5965                s = "CMD_TETHER_STATE_CHANGE";
5966                break;
5967            case CMD_TETHER_NOTIFICATION_TIMED_OUT:
5968                s = "CMD_TETHER_NOTIFICATION_TIMED_OUT";
5969                break;
5970            case CMD_BLUETOOTH_ADAPTER_STATE_CHANGE:
5971                s = "CMD_BLUETOOTH_ADAPTER_STATE_CHANGE";
5972                break;
5973            case CMD_ADD_OR_UPDATE_NETWORK:
5974                s = "CMD_ADD_OR_UPDATE_NETWORK";
5975                break;
5976            case CMD_REMOVE_NETWORK:
5977                s = "CMD_REMOVE_NETWORK";
5978                break;
5979            case CMD_ENABLE_NETWORK:
5980                s = "CMD_ENABLE_NETWORK";
5981                break;
5982            case CMD_ENABLE_ALL_NETWORKS:
5983                s = "CMD_ENABLE_ALL_NETWORKS";
5984                break;
5985            case CMD_AUTO_CONNECT:
5986                s = "CMD_AUTO_CONNECT";
5987                break;
5988            case CMD_AUTO_ROAM:
5989                s = "CMD_AUTO_ROAM";
5990                break;
5991            case CMD_AUTO_SAVE_NETWORK:
5992                s = "CMD_AUTO_SAVE_NETWORK";
5993                break;
5994            case CMD_BOOT_COMPLETED:
5995                s = "CMD_BOOT_COMPLETED";
5996                break;
5997            case DhcpStateMachine.CMD_START_DHCP:
5998                s = "CMD_START_DHCP";
5999                break;
6000            case DhcpStateMachine.CMD_STOP_DHCP:
6001                s = "CMD_STOP_DHCP";
6002                break;
6003            case DhcpStateMachine.CMD_RENEW_DHCP:
6004                s = "CMD_RENEW_DHCP";
6005                break;
6006            case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
6007                s = "CMD_PRE_DHCP_ACTION";
6008                break;
6009            case DhcpStateMachine.CMD_POST_DHCP_ACTION:
6010                s = "CMD_POST_DHCP_ACTION";
6011                break;
6012            case DhcpStateMachine.CMD_PRE_DHCP_ACTION_COMPLETE:
6013                s = "CMD_PRE_DHCP_ACTION_COMPLETE";
6014                break;
6015            case DhcpStateMachine.CMD_ON_QUIT:
6016                s = "CMD_ON_QUIT";
6017                break;
6018            case WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST:
6019                s = "WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST";
6020                break;
6021            case WifiManager.DISABLE_NETWORK:
6022                s = "WifiManager.DISABLE_NETWORK";
6023                break;
6024            case CMD_BLACKLIST_NETWORK:
6025                s = "CMD_BLACKLIST_NETWORK";
6026                break;
6027            case CMD_CLEAR_BLACKLIST:
6028                s = "CMD_CLEAR_BLACKLIST";
6029                break;
6030            case CMD_SAVE_CONFIG:
6031                s = "CMD_SAVE_CONFIG";
6032                break;
6033            case CMD_GET_CONFIGURED_NETWORKS:
6034                s = "CMD_GET_CONFIGURED_NETWORKS";
6035                break;
6036            case CMD_GET_SUPPORTED_FEATURES:
6037                s = "CMD_GET_ADAPTORS";
6038                break;
6039            case CMD_UNWANTED_NETWORK:
6040                s = "CMD_UNWANTED_NETWORK";
6041                break;
6042            case CMD_NETWORK_STATUS:
6043                s = "CMD_NETWORK_STATUS";
6044                break;
6045            case CMD_GET_LINK_LAYER_STATS:
6046                s = "CMD_GET_LINK_LAYER_STATS";
6047                break;
6048            case CMD_GET_PRIVILEGED_CONFIGURED_NETWORKS:
6049                s = "CMD_GET_PRIVILEGED_CONFIGURED_NETWORKS";
6050                break;
6051            case CMD_DISCONNECT:
6052                s = "CMD_DISCONNECT";
6053                break;
6054            case CMD_RECONNECT:
6055                s = "CMD_RECONNECT";
6056                break;
6057            case CMD_REASSOCIATE:
6058                s = "CMD_REASSOCIATE";
6059                break;
6060            case CMD_GET_CONNECTION_STATISTICS:
6061                s = "CMD_GET_CONNECTION_STATISTICS";
6062                break;
6063            case CMD_SET_HIGH_PERF_MODE:
6064                s = "CMD_SET_HIGH_PERF_MODE";
6065                break;
6066            case CMD_SET_COUNTRY_CODE:
6067                s = "CMD_SET_COUNTRY_CODE";
6068                break;
6069            case CMD_ENABLE_RSSI_POLL:
6070                s = "CMD_ENABLE_RSSI_POLL";
6071                break;
6072            case CMD_RSSI_POLL:
6073                s = "CMD_RSSI_POLL";
6074                break;
6075            case CMD_START_PACKET_FILTERING:
6076                s = "CMD_START_PACKET_FILTERING";
6077                break;
6078            case CMD_STOP_PACKET_FILTERING:
6079                s = "CMD_STOP_PACKET_FILTERING";
6080                break;
6081            case CMD_SET_SUSPEND_OPT_ENABLED:
6082                s = "CMD_SET_SUSPEND_OPT_ENABLED";
6083                break;
6084            case CMD_NO_NETWORKS_PERIODIC_SCAN:
6085                s = "CMD_NO_NETWORKS_PERIODIC_SCAN";
6086                break;
6087            case CMD_SET_BATCHED_SCAN:
6088                s = "CMD_SET_BATCHED_SCAN";
6089                break;
6090            case CMD_START_NEXT_BATCHED_SCAN:
6091                s = "CMD_START_NEXT_BATCHED_SCAN";
6092                break;
6093            case CMD_POLL_BATCHED_SCAN:
6094                s = "CMD_POLL_BATCHED_SCAN";
6095                break;
6096            case CMD_UPDATE_LINKPROPERTIES:
6097                s = "CMD_UPDATE_LINKPROPERTIES";
6098                break;
6099            case CMD_RELOAD_TLS_AND_RECONNECT:
6100                s = "CMD_RELOAD_TLS_AND_RECONNECT";
6101                break;
6102            case WifiManager.CONNECT_NETWORK:
6103                s = "CONNECT_NETWORK";
6104                break;
6105            case WifiManager.SAVE_NETWORK:
6106                s = "SAVE_NETWORK";
6107                break;
6108            case WifiManager.FORGET_NETWORK:
6109                s = "FORGET_NETWORK";
6110                break;
6111            case WifiMonitor.SUP_CONNECTION_EVENT:
6112                s = "SUP_CONNECTION_EVENT";
6113                break;
6114            case WifiMonitor.SUP_DISCONNECTION_EVENT:
6115                s = "SUP_DISCONNECTION_EVENT";
6116                break;
6117            case WifiMonitor.SCAN_RESULTS_EVENT:
6118                s = "SCAN_RESULTS_EVENT";
6119                break;
6120            case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
6121                s = "SUPPLICANT_STATE_CHANGE_EVENT";
6122                break;
6123            case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
6124                s = "AUTHENTICATION_FAILURE_EVENT";
6125                break;
6126            case WifiMonitor.SSID_TEMP_DISABLED:
6127                s = "SSID_TEMP_DISABLED";
6128                break;
6129            case WifiMonitor.SSID_REENABLED:
6130                s = "SSID_REENABLED";
6131                break;
6132            case WifiMonitor.WPS_SUCCESS_EVENT:
6133                s = "WPS_SUCCESS_EVENT";
6134                break;
6135            case WifiMonitor.WPS_FAIL_EVENT:
6136                s = "WPS_FAIL_EVENT";
6137                break;
6138            case WifiMonitor.SUP_REQUEST_IDENTITY:
6139                s = "SUP_REQUEST_IDENTITY";
6140                break;
6141            case WifiMonitor.NETWORK_CONNECTION_EVENT:
6142                s = "NETWORK_CONNECTION_EVENT";
6143                break;
6144            case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
6145                s = "NETWORK_DISCONNECTION_EVENT";
6146                break;
6147            case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
6148                s = "ASSOCIATION_REJECTION_EVENT";
6149                break;
6150            case CMD_SET_OPERATIONAL_MODE:
6151                s = "CMD_SET_OPERATIONAL_MODE";
6152                break;
6153            case CMD_START_SCAN:
6154                s = "CMD_START_SCAN";
6155                break;
6156            case CMD_DISABLE_P2P_RSP:
6157                s = "CMD_DISABLE_P2P_RSP";
6158                break;
6159            case CMD_DISABLE_P2P_REQ:
6160                s = "CMD_DISABLE_P2P_REQ";
6161                break;
6162            case WifiWatchdogStateMachine.GOOD_LINK_DETECTED:
6163                s = "GOOD_LINK_DETECTED";
6164                break;
6165            case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
6166                s = "POOR_LINK_DETECTED";
6167                break;
6168            case WifiP2pServiceImpl.GROUP_CREATING_TIMED_OUT:
6169                s = "GROUP_CREATING_TIMED_OUT";
6170                break;
6171            case WifiP2pServiceImpl.P2P_CONNECTION_CHANGED:
6172                s = "P2P_CONNECTION_CHANGED";
6173                break;
6174            case WifiP2pServiceImpl.DISCONNECT_WIFI_RESPONSE:
6175                s = "P2P.DISCONNECT_WIFI_RESPONSE";
6176                break;
6177            case WifiP2pServiceImpl.SET_MIRACAST_MODE:
6178                s = "P2P.SET_MIRACAST_MODE";
6179                break;
6180            case WifiP2pServiceImpl.BLOCK_DISCOVERY:
6181                s = "P2P.BLOCK_DISCOVERY";
6182                break;
6183            case WifiP2pServiceImpl.SET_COUNTRY_CODE:
6184                s = "P2P.SET_COUNTRY_CODE";
6185                break;
6186            case WifiManager.CANCEL_WPS:
6187                s = "CANCEL_WPS";
6188                break;
6189            case WifiManager.CANCEL_WPS_FAILED:
6190                s = "CANCEL_WPS_FAILED";
6191                break;
6192            case WifiManager.CANCEL_WPS_SUCCEDED:
6193                s = "CANCEL_WPS_SUCCEDED";
6194                break;
6195            case WifiManager.START_WPS:
6196                s = "START_WPS";
6197                break;
6198            case WifiManager.START_WPS_SUCCEEDED:
6199                s = "START_WPS_SUCCEEDED";
6200                break;
6201            case WifiManager.WPS_FAILED:
6202                s = "WPS_FAILED";
6203                break;
6204            case WifiManager.WPS_COMPLETED:
6205                s = "WPS_COMPLETED";
6206                break;
6207            case WifiManager.RSSI_PKTCNT_FETCH:
6208                s = "RSSI_PKTCNT_FETCH";
6209                break;
6210            case CMD_IP_CONFIGURATION_LOST:
6211                s = "CMD_IP_CONFIGURATION_LOST";
6212                break;
6213            case CMD_IP_CONFIGURATION_SUCCESSFUL:
6214                s = "CMD_IP_CONFIGURATION_SUCCESSFUL";
6215                break;
6216            case CMD_STATIC_IP_SUCCESS:
6217                s = "CMD_STATIC_IP_SUCCESSFUL";
6218                break;
6219            case CMD_STATIC_IP_FAILURE:
6220                s = "CMD_STATIC_IP_FAILURE";
6221                break;
6222            case DhcpStateMachine.DHCP_SUCCESS:
6223                s = "DHCP_SUCCESS";
6224                break;
6225            case DhcpStateMachine.DHCP_FAILURE:
6226                s = "DHCP_FAILURE";
6227                break;
6228            case CMD_TARGET_BSSID:
6229                s = "CMD_TARGET_BSSID";
6230                break;
6231            case CMD_ASSOCIATED_BSSID:
6232                s = "CMD_ASSOCIATED_BSSID";
6233                break;
6234            case CMD_ROAM_WATCHDOG_TIMER:
6235                s = "CMD_ROAM_WATCHDOG_TIMER";
6236                break;
6237            case CMD_SCREEN_STATE_CHANGED:
6238                s = "CMD_SCREEN_STATE_CHANGED";
6239                break;
6240            case CMD_DISCONNECTING_WATCHDOG_TIMER:
6241                s = "CMD_DISCONNECTING_WATCHDOG_TIMER";
6242                break;
6243            default:
6244                s = "what:" + Integer.toString(what);
6245                break;
6246        }
6247        return s;
6248    }
6249
6250    void registerConnected() {
6251       if (mLastNetworkId != WifiConfiguration.INVALID_NETWORK_ID) {
6252           long now_ms = System.currentTimeMillis();
6253           // We are switching away from this configuration,
6254           // hence record the time we were connected last
6255           WifiConfiguration config = mWifiConfigStore.getWifiConfiguration(mLastNetworkId);
6256           if (config != null) {
6257               config.lastConnected = System.currentTimeMillis();
6258               config.autoJoinBailedDueToLowRssi = false;
6259               config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED);
6260               config.numConnectionFailures = 0;
6261               config.numIpConfigFailures = 0;
6262               config.numAuthFailures = 0;
6263               config.numAssociation++;
6264           }
6265           mBadLinkspeedcount = 0;
6266       }
6267    }
6268
6269    void registerDisconnected() {
6270        if (mLastNetworkId != WifiConfiguration.INVALID_NETWORK_ID) {
6271            long now_ms = System.currentTimeMillis();
6272            // We are switching away from this configuration,
6273            // hence record the time we were connected last
6274            WifiConfiguration config = mWifiConfigStore.getWifiConfiguration(mLastNetworkId);
6275            if (config != null) {
6276                config.lastDisconnected = System.currentTimeMillis();
6277                if (config.ephemeral) {
6278                    // Remove ephemeral WifiConfigurations from file
6279                    mWifiConfigStore.forgetNetwork(mLastNetworkId);
6280                }
6281            }
6282        }
6283    }
6284
6285    void noteWifiDisabledWhileAssociated() {
6286        // We got disabled by user while we were associated, make note of it
6287        int rssi = mWifiInfo.getRssi();
6288        WifiConfiguration config = getCurrentWifiConfiguration();
6289        if (getCurrentState() == mConnectedState
6290                && rssi != WifiInfo.INVALID_RSSI
6291                && config != null) {
6292            boolean is24GHz = mWifiInfo.is24GHz();
6293            boolean isBadRSSI = (is24GHz && rssi < mWifiConfigStore.thresholdBadRssi24.get())
6294                    || (!is24GHz && rssi < mWifiConfigStore.thresholdBadRssi5.get());
6295            boolean isLowRSSI = (is24GHz && rssi < mWifiConfigStore.thresholdLowRssi24.get())
6296                    || (!is24GHz && mWifiInfo.getRssi() < mWifiConfigStore.thresholdLowRssi5.get());
6297            boolean isHighRSSI = (is24GHz && rssi >= mWifiConfigStore.thresholdGoodRssi24.get())
6298                    || (!is24GHz && mWifiInfo.getRssi() >= mWifiConfigStore.thresholdGoodRssi5.get());
6299            if (isBadRSSI) {
6300                // Take note that we got disabled while RSSI was Bad
6301                config.numUserTriggeredWifiDisableLowRSSI++;
6302            } else if (isLowRSSI) {
6303                // Take note that we got disabled while RSSI was Low
6304                config.numUserTriggeredWifiDisableBadRSSI++;
6305            } else if (!isHighRSSI) {
6306                // Take note that we got disabled while RSSI was Not high
6307                config.numUserTriggeredWifiDisableNotHighRSSI++;
6308            }
6309        }
6310    }
6311
6312    WifiConfiguration getCurrentWifiConfiguration() {
6313        if (mLastNetworkId == WifiConfiguration.INVALID_NETWORK_ID) {
6314            return null;
6315        }
6316        return mWifiConfigStore.getWifiConfiguration(mLastNetworkId);
6317    }
6318
6319    ScanResult getCurrentScanResult() {
6320        WifiConfiguration config = getCurrentWifiConfiguration();
6321        if (config == null) {
6322            return null;
6323        }
6324        String BSSID = mWifiInfo.getBSSID();
6325        if (BSSID == null) {
6326            BSSID = mTargetRoamBSSID;
6327        }
6328        if (config.scanResultCache == null) {
6329            return null;
6330        }
6331        return config.scanResultCache.get(BSSID);
6332    }
6333
6334    String getCurrentBSSID() {
6335        if (linkDebouncing) {
6336            return null;
6337        }
6338        return mLastBssid;
6339    }
6340
6341    class ConnectModeState extends State {
6342        @Override
6343        public boolean processMessage(Message message) {
6344            WifiConfiguration config;
6345            int netId;
6346            boolean ok;
6347            boolean didDisconnect;
6348            String bssid;
6349            String ssid;
6350            NetworkUpdateResult result;
6351            logStateAndMessage(message, getClass().getSimpleName());
6352
6353            switch (message.what) {
6354                case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
6355                    didBlackListBSSID = false;
6356                    bssid = (String) message.obj;
6357                    if (bssid == null || TextUtils.isEmpty(bssid)) {
6358                        // If BSSID is null, use the target roam BSSID
6359                        bssid = mTargetRoamBSSID;
6360                    }
6361                    if (bssid != null) {
6362                        // If we have a BSSID, tell configStore to black list it
6363                        synchronized(mScanResultCache) {
6364                            didBlackListBSSID = mWifiConfigStore.handleBSSIDBlackList
6365                                    (mLastNetworkId, bssid, false);
6366                        }
6367                    }
6368                    mSupplicantStateTracker.sendMessage(WifiMonitor.ASSOCIATION_REJECTION_EVENT);
6369                    break;
6370                case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
6371                    mSupplicantStateTracker.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT);
6372                    break;
6373                case WifiMonitor.SSID_TEMP_DISABLED:
6374                case WifiMonitor.SSID_REENABLED:
6375                    String substr = (String) message.obj;
6376                    String en = message.what == WifiMonitor.SSID_TEMP_DISABLED ?
6377                            "temp-disabled" : "re-enabled";
6378                    loge("ConnectModeState SSID state=" + en + " nid="
6379                            + Integer.toString(message.arg1) + " [" + substr + "]");
6380                    synchronized(mScanResultCache) {
6381                        mWifiConfigStore.handleSSIDStateChange(message.arg1, message.what ==
6382                                WifiMonitor.SSID_REENABLED, substr, mWifiInfo.getBSSID());
6383                    }
6384                    break;
6385                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
6386                    SupplicantState state = handleSupplicantStateChange(message);
6387                    // A driver/firmware hang can now put the interface in a down state.
6388                    // We detect the interface going down and recover from it
6389                    if (!SupplicantState.isDriverActive(state)) {
6390                        if (mNetworkInfo.getState() != NetworkInfo.State.DISCONNECTED) {
6391                            handleNetworkDisconnect();
6392                        }
6393                        log("Detected an interface down, restart driver");
6394                        transitionTo(mDriverStoppedState);
6395                        sendMessage(CMD_START_DRIVER);
6396                        break;
6397                    }
6398
6399                    // Supplicant can fail to report a NETWORK_DISCONNECTION_EVENT
6400                    // when authentication times out after a successful connection,
6401                    // we can figure this from the supplicant state. If supplicant
6402                    // state is DISCONNECTED, but the mNetworkInfo says we are not
6403                    // disconnected, we need to handle a disconnection
6404                    if (!linkDebouncing && state == SupplicantState.DISCONNECTED &&
6405                            mNetworkInfo.getState() != NetworkInfo.State.DISCONNECTED) {
6406                        if (DBG) log("Missed CTRL-EVENT-DISCONNECTED, disconnect");
6407                        handleNetworkDisconnect();
6408                        transitionTo(mDisconnectedState);
6409                    }
6410                    break;
6411                case WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST:
6412                    if (message.arg1 == 1) {
6413                        mWifiNative.disconnect();
6414                        mTemporarilyDisconnectWifi = true;
6415                    } else {
6416                        mWifiNative.reconnect();
6417                        mTemporarilyDisconnectWifi = false;
6418                    }
6419                    break;
6420                case CMD_ADD_OR_UPDATE_NETWORK:
6421                    config = (WifiConfiguration) message.obj;
6422                    int res = mWifiConfigStore.addOrUpdateNetwork(config, message.sendingUid);
6423                    if (res < 0) {
6424                        messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
6425                    } else {
6426                        WifiConfiguration curConfig = getCurrentWifiConfiguration();
6427                        if (curConfig != null && config != null) {
6428                            if (curConfig.priority < config.priority
6429                                    && config.status == WifiConfiguration.Status.ENABLED) {
6430                                // Interpret this as a connect attempt
6431                                // Set the last selected configuration so as to allow the system to
6432                                // stick the last user choice without persisting the choice
6433                                mWifiConfigStore.setLastSelectedConfiguration(res);
6434
6435                                // Remember time of last connection attempt
6436                                lastConnectAttempt = System.currentTimeMillis();
6437
6438                                mWifiConnectionStatistics.numWifiManagerJoinAttempt++;
6439
6440                                // As a courtesy to the caller, trigger a scan now
6441                                startScan(ADD_OR_UPDATE_SOURCE, 0, null, null);
6442                            }
6443                        }
6444                    }
6445                    replyToMessage(message, CMD_ADD_OR_UPDATE_NETWORK, res);
6446                    break;
6447                case CMD_REMOVE_NETWORK:
6448                    ok = mWifiConfigStore.removeNetwork(message.arg1);
6449                    if (!ok) {
6450                        messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
6451                    }
6452                    replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
6453                    break;
6454                case CMD_ENABLE_NETWORK:
6455                    boolean others = message.arg2 == 1;
6456                    // Tell autojoin the user did try to select to that network
6457                    // However, do NOT persist the choice by bumping the priority of the network
6458                    if (others) {
6459                        mWifiAutoJoinController.
6460                                updateConfigurationHistory(message.arg1, true, false);
6461                        // Set the last selected configuration so as to allow the system to
6462                        // stick the last user choice without persisting the choice
6463                        mWifiConfigStore.setLastSelectedConfiguration(message.arg1);
6464
6465                        // Remember time of last connection attempt
6466                        lastConnectAttempt = System.currentTimeMillis();
6467
6468                        mWifiConnectionStatistics.numWifiManagerJoinAttempt++;
6469                    }
6470                    // Cancel auto roam requests
6471                    autoRoamSetBSSID(message.arg1, "any");
6472
6473                    ok = mWifiConfigStore.enableNetwork(message.arg1, message.arg2 == 1);
6474                    if (!ok) {
6475                        messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
6476                    }
6477                    replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
6478                    break;
6479                case CMD_ENABLE_ALL_NETWORKS:
6480                    long time = android.os.SystemClock.elapsedRealtime();
6481                    if (time - mLastEnableAllNetworksTime > MIN_INTERVAL_ENABLE_ALL_NETWORKS_MS) {
6482                        mWifiConfigStore.enableAllNetworks();
6483                        mLastEnableAllNetworksTime = time;
6484                    }
6485                    break;
6486                case WifiManager.DISABLE_NETWORK:
6487                    if (mWifiConfigStore.disableNetwork(message.arg1,
6488                            WifiConfiguration.DISABLED_BY_WIFI_MANAGER) == true) {
6489                        replyToMessage(message, WifiManager.DISABLE_NETWORK_SUCCEEDED);
6490                    } else {
6491                        messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
6492                        replyToMessage(message, WifiManager.DISABLE_NETWORK_FAILED,
6493                                WifiManager.ERROR);
6494                    }
6495                    break;
6496                case CMD_DISABLE_EPHEMERAL_NETWORK:
6497                    config = mWifiConfigStore.disableEphemeralNetwork((String)message.obj);
6498                    if (config != null) {
6499                        if (config.networkId == mLastNetworkId) {
6500                            // Disconnect and let autojoin reselect a new network
6501                            sendMessage(CMD_DISCONNECT);
6502                        }
6503                    }
6504                    break;
6505                case CMD_BLACKLIST_NETWORK:
6506                    mWifiNative.addToBlacklist((String) message.obj);
6507                    break;
6508                case CMD_CLEAR_BLACKLIST:
6509                    mWifiNative.clearBlacklist();
6510                    break;
6511                case CMD_SAVE_CONFIG:
6512                    ok = mWifiConfigStore.saveConfig();
6513
6514                    if (DBG) loge("wifistatemachine did save config " + ok);
6515                    replyToMessage(message, CMD_SAVE_CONFIG, ok ? SUCCESS : FAILURE);
6516
6517                    // Inform the backup manager about a data change
6518                    IBackupManager ibm = IBackupManager.Stub.asInterface(
6519                            ServiceManager.getService(Context.BACKUP_SERVICE));
6520                    if (ibm != null) {
6521                        try {
6522                            ibm.dataChanged("com.android.providers.settings");
6523                        } catch (Exception e) {
6524                            // Try again later
6525                        }
6526                    }
6527                    break;
6528                case CMD_GET_CONFIGURED_NETWORKS:
6529                    replyToMessage(message, message.what,
6530                            mWifiConfigStore.getConfiguredNetworks());
6531                    break;
6532                case WifiMonitor.SUP_REQUEST_IDENTITY:
6533                    // Supplicant lacks credentials to connect to that network, hence black list
6534                    ssid = (String) message.obj;
6535
6536                    if (targetWificonfiguration != null && ssid != null
6537                            && targetWificonfiguration.SSID != null
6538                            && targetWificonfiguration.SSID.equals("\"" + ssid + "\"")) {
6539                        mWifiConfigStore.handleSSIDStateChange(targetWificonfiguration.networkId,
6540                                false, "AUTH_FAILED no identity", null);
6541                    }
6542                    // Disconnect now, as we don't have any way to fullfill the  supplicant request.
6543                    mWifiConfigStore.setLastSelectedConfiguration
6544                            (WifiConfiguration.INVALID_NETWORK_ID);
6545                    mWifiNative.disconnect();
6546                    break;
6547                case WifiMonitor.SUP_REQUEST_SIM_AUTH:
6548                    logd("Received SUP_REQUEST_SIM_AUTH");
6549                    SimAuthRequestData requestData = (SimAuthRequestData) message.obj;
6550                    if (requestData != null) {
6551                        if (requestData.protocol == WifiEnterpriseConfig.Eap.SIM) {
6552                            handleGsmAuthRequest(requestData);
6553                        } else if (requestData.protocol == WifiEnterpriseConfig.Eap.AKA) {
6554                            handle3GAuthRequest(requestData);
6555                        }
6556                    } else {
6557                        loge("Invalid sim auth request");
6558                    }
6559                    break;
6560                case CMD_GET_PRIVILEGED_CONFIGURED_NETWORKS:
6561                    replyToMessage(message, message.what,
6562                            mWifiConfigStore.getPrivilegedConfiguredNetworks());
6563                    break;
6564                    /* Do a redundant disconnect without transition */
6565                case CMD_DISCONNECT:
6566                    mWifiConfigStore.setLastSelectedConfiguration
6567                            (WifiConfiguration.INVALID_NETWORK_ID);
6568                    mWifiNative.disconnect();
6569                    break;
6570                case CMD_RECONNECT:
6571                    mWifiAutoJoinController.attemptAutoJoin();
6572                    break;
6573                case CMD_REASSOCIATE:
6574                    lastConnectAttempt = System.currentTimeMillis();
6575                    mWifiNative.reassociate();
6576                    break;
6577                case CMD_RELOAD_TLS_AND_RECONNECT:
6578                    if (mWifiConfigStore.needsUnlockedKeyStore()) {
6579                        logd("Reconnecting to give a chance to un-connected TLS networks");
6580                        mWifiNative.disconnect();
6581                        lastConnectAttempt = System.currentTimeMillis();
6582                        mWifiNative.reconnect();
6583                    }
6584                    break;
6585                case CMD_AUTO_ROAM:
6586                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
6587                    return HANDLED;
6588                case CMD_AUTO_CONNECT:
6589                    /* Work Around: wpa_supplicant can get in a bad state where it returns a non
6590                     * associated status to the STATUS command but somehow-someplace still thinks
6591                     * it is associated and thus will ignore select/reconnect command with
6592                     * following message:
6593                     * "Already associated with the selected network - do nothing"
6594                     *
6595                     * Hence, sends a disconnect to supplicant first.
6596                     */
6597                    didDisconnect = false;
6598                    if (getCurrentState() != mDisconnectedState) {
6599                        /** Supplicant will ignore the reconnect if we are currently associated,
6600                         * hence trigger a disconnect
6601                         */
6602                        didDisconnect = true;
6603                        mWifiNative.disconnect();
6604                    }
6605
6606                    /* connect command coming from auto-join */
6607                    config = (WifiConfiguration) message.obj;
6608                    netId = message.arg1;
6609                    int roam = message.arg2;
6610                    loge("CMD_AUTO_CONNECT sup state "
6611                            + mSupplicantStateTracker.getSupplicantStateName()
6612                            + " my state " + getCurrentState().getName()
6613                            + " nid=" + Integer.toString(netId)
6614                            + " roam=" + Integer.toString(roam));
6615                    if (config == null) {
6616                        loge("AUTO_CONNECT and no config, bail out...");
6617                        break;
6618                    }
6619
6620                    /* Make sure we cancel any previous roam request */
6621                    autoRoamSetBSSID(netId, config.BSSID);
6622
6623                    /* Save the network config */
6624                    loge("CMD_AUTO_CONNECT will save config -> " + config.SSID
6625                            + " nid=" + Integer.toString(netId));
6626                    result = mWifiConfigStore.saveNetwork(config, -1);
6627                    netId = result.getNetworkId();
6628                    loge("CMD_AUTO_CONNECT did save config -> "
6629                            + " nid=" + Integer.toString(netId));
6630
6631                    // Make sure the network is enabled, since supplicant will not reenable it
6632                    mWifiConfigStore.enableNetworkWithoutBroadcast(netId, false);
6633
6634                    if (mWifiConfigStore.selectNetwork(config, /* updatePriorities = */ false) &&
6635                            mWifiNative.reconnect()) {
6636                        lastConnectAttempt = System.currentTimeMillis();
6637                        targetWificonfiguration = mWifiConfigStore.getWifiConfiguration(netId);
6638                        config = mWifiConfigStore.getWifiConfiguration(netId);
6639                        if (config != null
6640                                && !mWifiConfigStore.isLastSelectedConfiguration(config)) {
6641                            // If we autojoined a different config than the user selected one,
6642                            // it means we could not see the last user selection,
6643                            // or that the last user selection was faulty and ended up blacklisted
6644                            // for some reason (in which case the user is notified with an error
6645                            // message in the Wifi picker), and thus we managed to auto-join away
6646                            // from the selected  config. -> in that case we need to forget
6647                            // the selection because we don't want to abruptly switch back to it.
6648                            //
6649                            // Note that the user selection is also forgotten after a period of time
6650                            // during which the device has been disconnected.
6651                            // The default value is 30 minutes : see the code path at bottom of
6652                            // setScanResults() function.
6653                            mWifiConfigStore.
6654                                 setLastSelectedConfiguration(WifiConfiguration.INVALID_NETWORK_ID);
6655                        }
6656                        mAutoRoaming = roam;
6657                        if (isRoaming() || linkDebouncing) {
6658                            transitionTo(mRoamingState);
6659                        } else if (didDisconnect) {
6660                            transitionTo(mDisconnectingState);
6661                        } else {
6662                            /* Already in disconnected state, nothing to change */
6663                        }
6664                    } else {
6665                        loge("Failed to connect config: " + config + " netId: " + netId);
6666                        replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
6667                                WifiManager.ERROR);
6668                        break;
6669                    }
6670                    break;
6671                case WifiManager.CONNECT_NETWORK:
6672                    /**
6673                     *  The connect message can contain a network id passed as arg1 on message or
6674                     * or a config passed as obj on message.
6675                     * For a new network, a config is passed to create and connect.
6676                     * For an existing network, a network id is passed
6677                     */
6678                    netId = message.arg1;
6679                    config = (WifiConfiguration) message.obj;
6680                    mWifiConnectionStatistics.numWifiManagerJoinAttempt++;
6681                    boolean updatedExisting = false;
6682
6683                    /* Save the network config */
6684                    if (config != null) {
6685                        String configKey = config.configKey(true /* allowCached */);
6686                        WifiConfiguration savedConfig =
6687                                mWifiConfigStore.getWifiConfiguration(configKey);
6688                        if (savedConfig != null) {
6689                            // There is an existing config with this netId, but it wasn't exposed
6690                            // (either AUTO_JOIN_DELETED or ephemeral; see WifiConfigStore#
6691                            // getConfiguredNetworks). Remove those bits and update the config.
6692                            config = savedConfig;
6693                            loge("CONNECT_NETWORK updating existing config with id=" +
6694                                    config.networkId + " configKey=" + configKey);
6695                            config.ephemeral = false;
6696                            config.autoJoinStatus = WifiConfiguration.AUTO_JOIN_ENABLED;
6697                            updatedExisting = true;
6698                        }
6699
6700                        result = mWifiConfigStore.saveNetwork(config, message.sendingUid);
6701                        netId = result.getNetworkId();
6702                    }
6703                    config = mWifiConfigStore.getWifiConfiguration(netId);
6704
6705                    if (config == null) {
6706                        loge("CONNECT_NETWORK no config for id=" + Integer.toString(netId) + " "
6707                                + mSupplicantStateTracker.getSupplicantStateName() + " my state "
6708                                + getCurrentState().getName());
6709                        replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
6710                                WifiManager.ERROR);
6711                        break;
6712                    } else {
6713                        String wasSkipped = config.autoJoinBailedDueToLowRssi ? " skipped" : "";
6714                        loge("CONNECT_NETWORK id=" + Integer.toString(netId)
6715                                + " config=" + config.SSID
6716                                + " cnid=" + config.networkId
6717                                + " supstate=" + mSupplicantStateTracker.getSupplicantStateName()
6718                                + " my state " + getCurrentState().getName()
6719                                + " uid = " + message.sendingUid
6720                                + wasSkipped);
6721                    }
6722
6723                    autoRoamSetBSSID(netId, "any");
6724
6725                    if (message.sendingUid == Process.WIFI_UID
6726                        || message.sendingUid == Process.SYSTEM_UID) {
6727                        // As a sanity measure, clear the BSSID in the supplicant network block.
6728                        // If system or Wifi Settings want to connect, they will not
6729                        // specify the BSSID.
6730                        // If an app however had added a BSSID to this configuration, and the BSSID
6731                        // was wrong, Then we would forever fail to connect until that BSSID
6732                        // is cleaned up.
6733                        clearConfigBSSID(config, "CONNECT_NETWORK");
6734                    }
6735
6736                    mAutoRoaming = WifiAutoJoinController.AUTO_JOIN_IDLE;
6737
6738                    /* Tell autojoin the user did try to connect to that network */
6739                    mWifiAutoJoinController.updateConfigurationHistory(netId, true, true);
6740
6741                    mWifiConfigStore.setLastSelectedConfiguration(netId);
6742
6743                    didDisconnect = false;
6744                    if (mLastNetworkId != WifiConfiguration.INVALID_NETWORK_ID
6745                            && mLastNetworkId != netId) {
6746                        /** Supplicant will ignore the reconnect if we are currently associated,
6747                         * hence trigger a disconnect
6748                         */
6749                        didDisconnect = true;
6750                        mWifiNative.disconnect();
6751                    }
6752
6753                    // Make sure the network is enabled, since supplicant will not reenable it
6754                    mWifiConfigStore.enableNetworkWithoutBroadcast(netId, false);
6755
6756                    if (mWifiConfigStore.selectNetwork(config, /* updatePriorities = */ true) &&
6757                            mWifiNative.reconnect()) {
6758                        lastConnectAttempt = System.currentTimeMillis();
6759                        targetWificonfiguration = mWifiConfigStore.getWifiConfiguration(netId);
6760
6761                        /* The state tracker handles enabling networks upon completion/failure */
6762                        mSupplicantStateTracker.sendMessage(WifiManager.CONNECT_NETWORK);
6763                        replyToMessage(message, WifiManager.CONNECT_NETWORK_SUCCEEDED);
6764                        if (didDisconnect) {
6765                            /* Expect a disconnection from the old connection */
6766                            transitionTo(mDisconnectingState);
6767                        } else if (updatedExisting && getCurrentState() == mConnectedState &&
6768                                getCurrentWifiConfiguration().networkId == netId) {
6769                            // Update the current set of network capabilities, but stay in the
6770                            // current state.
6771                            updateCapabilities(config);
6772                        } else {
6773                            /**
6774                             *  Directly go to disconnected state where we
6775                             * process the connection events from supplicant
6776                             **/
6777                            transitionTo(mDisconnectedState);
6778                        }
6779                    } else {
6780                        loge("Failed to connect config: " + config + " netId: " + netId);
6781                        replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
6782                                WifiManager.ERROR);
6783                        break;
6784                    }
6785                    break;
6786                case WifiManager.SAVE_NETWORK:
6787                    mWifiConnectionStatistics.numWifiManagerJoinAttempt++;
6788                    // Fall thru
6789                case WifiStateMachine.CMD_AUTO_SAVE_NETWORK:
6790                    lastSavedConfigurationAttempt = null; // Used for debug
6791                    config = (WifiConfiguration) message.obj;
6792                    if (config == null) {
6793                        loge("ERROR: SAVE_NETWORK with null configuration"
6794                                + mSupplicantStateTracker.getSupplicantStateName()
6795                                + " my state " + getCurrentState().getName());
6796                        messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
6797                        replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED,
6798                                WifiManager.ERROR);
6799                        break;
6800                    }
6801                    lastSavedConfigurationAttempt = new WifiConfiguration(config);
6802                    int nid = config.networkId;
6803                    loge("SAVE_NETWORK id=" + Integer.toString(nid)
6804                                + " config=" + config.SSID
6805                                + " nid=" + config.networkId
6806                                + " supstate=" + mSupplicantStateTracker.getSupplicantStateName()
6807                                + " my state " + getCurrentState().getName());
6808
6809                    result = mWifiConfigStore.saveNetwork(config, -1);
6810                    if (result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID) {
6811                        if (mWifiInfo.getNetworkId() == result.getNetworkId()) {
6812                            if (result.hasIpChanged()) {
6813                                // The currently connection configuration was changed
6814                                // We switched from DHCP to static or from static to DHCP, or the
6815                                // static IP address has changed.
6816                                log("Reconfiguring IP on connection");
6817                                // TODO: clear addresses and disable IPv6
6818                                // to simplify obtainingIpState.
6819                                transitionTo(mObtainingIpState);
6820                            }
6821                            if (result.hasProxyChanged()) {
6822                                log("Reconfiguring proxy on connection");
6823                                updateLinkProperties(CMD_UPDATE_LINKPROPERTIES);
6824                            }
6825                        }
6826                        replyToMessage(message, WifiManager.SAVE_NETWORK_SUCCEEDED);
6827                        if (VDBG) {
6828                           loge("Success save network nid="
6829                                        + Integer.toString(result.getNetworkId()));
6830                        }
6831
6832                        synchronized(mScanResultCache) {
6833                            /**
6834                             * If the command comes from WifiManager, then
6835                             * tell autojoin the user did try to modify and save that network,
6836                             * and interpret the SAVE_NETWORK as a request to connect
6837                             */
6838                            boolean user = message.what == WifiManager.SAVE_NETWORK;
6839                            mWifiAutoJoinController.updateConfigurationHistory(result.getNetworkId()
6840                                    , user, true);
6841                            mWifiAutoJoinController.attemptAutoJoin();
6842                        }
6843                    } else {
6844                        loge("Failed to save network");
6845                        messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
6846                        replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED,
6847                                WifiManager.ERROR);
6848                    }
6849                    break;
6850                case WifiManager.FORGET_NETWORK:
6851                    // Debug only, remember last configuration that was forgotten
6852                    WifiConfiguration toRemove
6853                            = mWifiConfigStore.getWifiConfiguration(message.arg1);
6854                    if (toRemove == null) {
6855                        lastForgetConfigurationAttempt = null;
6856                    } else {
6857                        lastForgetConfigurationAttempt = new WifiConfiguration(toRemove);
6858                    }
6859                    if (mWifiConfigStore.forgetNetwork(message.arg1)) {
6860                        replyToMessage(message, WifiManager.FORGET_NETWORK_SUCCEEDED);
6861                    } else {
6862                        loge("Failed to forget network");
6863                        replyToMessage(message, WifiManager.FORGET_NETWORK_FAILED,
6864                                WifiManager.ERROR);
6865                    }
6866                    break;
6867                case WifiManager.START_WPS:
6868                    WpsInfo wpsInfo = (WpsInfo) message.obj;
6869                    WpsResult wpsResult;
6870                    switch (wpsInfo.setup) {
6871                        case WpsInfo.PBC:
6872                            wpsResult = mWifiConfigStore.startWpsPbc(wpsInfo);
6873                            break;
6874                        case WpsInfo.KEYPAD:
6875                            wpsResult = mWifiConfigStore.startWpsWithPinFromAccessPoint(wpsInfo);
6876                            break;
6877                        case WpsInfo.DISPLAY:
6878                            wpsResult = mWifiConfigStore.startWpsWithPinFromDevice(wpsInfo);
6879                            break;
6880                        default:
6881                            wpsResult = new WpsResult(Status.FAILURE);
6882                            loge("Invalid setup for WPS");
6883                            break;
6884                    }
6885                    mWifiConfigStore.setLastSelectedConfiguration
6886                            (WifiConfiguration.INVALID_NETWORK_ID);
6887                    if (wpsResult.status == Status.SUCCESS) {
6888                        replyToMessage(message, WifiManager.START_WPS_SUCCEEDED, wpsResult);
6889                        transitionTo(mWpsRunningState);
6890                    } else {
6891                        loge("Failed to start WPS with config " + wpsInfo.toString());
6892                        replyToMessage(message, WifiManager.WPS_FAILED, WifiManager.ERROR);
6893                    }
6894                    break;
6895                case WifiMonitor.NETWORK_CONNECTION_EVENT:
6896                    if (DBG) log("Network connection established");
6897                    mLastNetworkId = message.arg1;
6898                    mLastBssid = (String) message.obj;
6899
6900                    mWifiInfo.setBSSID(mLastBssid);
6901                    mWifiInfo.setNetworkId(mLastNetworkId);
6902
6903                    sendNetworkStateChangeBroadcast(mLastBssid);
6904                    transitionTo(mObtainingIpState);
6905                    break;
6906                case WifiMonitor.ANQP_DONE_EVENT:
6907                    Log.d("HS2J", String.format("WFSM: ANQP for %016x %s",
6908                            (Long)message.obj, message.arg1 != 0 ? "success" : "fail"));
6909                    mWifiConfigStore.notifyANQPDone((Long) message.obj, message.arg1 != 0);
6910                    break;
6911                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
6912                    // Calling handleNetworkDisconnect here is redundant because we might already
6913                    // have called it when leaving L2ConnectedState to go to disconnecting state
6914                    // or thru other path
6915                    // We should normally check the mWifiInfo or mLastNetworkId so as to check
6916                    // if they are valid, and only in this case call handleNEtworkDisconnect,
6917                    // TODO: this should be fixed for a L MR release
6918                    // The side effect of calling handleNetworkDisconnect twice is that a bunch of
6919                    // idempotent commands are executed twice (stopping Dhcp, enabling the SPS mode
6920                    // at the chip etc...
6921                    if (DBG) log("ConnectModeState: Network connection lost ");
6922                    handleNetworkDisconnect();
6923                    transitionTo(mDisconnectedState);
6924                    break;
6925                default:
6926                    return NOT_HANDLED;
6927            }
6928            return HANDLED;
6929        }
6930    }
6931
6932    private void updateCapabilities(WifiConfiguration config) {
6933        if (config.ephemeral) {
6934            mNetworkCapabilities.removeCapability(
6935                    NetworkCapabilities.NET_CAPABILITY_TRUSTED);
6936        } else {
6937            mNetworkCapabilities.addCapability(
6938                    NetworkCapabilities.NET_CAPABILITY_TRUSTED);
6939        }
6940        mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
6941    }
6942
6943    private class WifiNetworkAgent extends NetworkAgent {
6944        public WifiNetworkAgent(Looper l, Context c, String TAG, NetworkInfo ni,
6945                NetworkCapabilities nc, LinkProperties lp, int score) {
6946            super(l, c, TAG, ni, nc, lp, score);
6947        }
6948        protected void unwanted() {
6949            // Ignore if we're not the current networkAgent.
6950            if (this != mNetworkAgent) return;
6951            if (DBG) log("WifiNetworkAgent -> Wifi unwanted score "
6952                    + Integer.toString(mWifiInfo.score));
6953            unwantedNetwork(network_status_unwanted_disconnect);
6954        }
6955
6956        protected void networkStatus(int status) {
6957            if (status == NetworkAgent.INVALID_NETWORK) {
6958                if (DBG) log("WifiNetworkAgent -> Wifi networkStatus invalid, score="
6959                        + Integer.toString(mWifiInfo.score));
6960                unwantedNetwork(network_status_unwanted_disable_autojoin);
6961            } else if (status == NetworkAgent.VALID_NETWORK) {
6962                if (DBG && mWifiInfo != null) log("WifiNetworkAgent -> Wifi networkStatus valid, score= "
6963                        + Integer.toString(mWifiInfo.score));
6964                doNetworkStatus(status);
6965            }
6966        }
6967    }
6968
6969    void unwantedNetwork(int reason) {
6970        sendMessage(CMD_UNWANTED_NETWORK, reason);
6971    }
6972
6973    void doNetworkStatus(int status) {
6974        sendMessage(CMD_NETWORK_STATUS, status);
6975    }
6976
6977    boolean startScanForConfiguration(WifiConfiguration config, boolean restrictChannelList) {
6978        if (config == null)
6979            return false;
6980
6981        // We are still seeing a fairly high power consumption triggered by autojoin scans
6982        // Hence do partial scans only for PSK configuration that are roamable since the
6983        // primary purpose of the partial scans is roaming.
6984        // Full badn scans with exponential backoff for the purpose or extended roaming and
6985        // network switching are performed unconditionally.
6986        if (config.scanResultCache == null
6987                || !config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)
6988                || config.scanResultCache.size() > 6) {
6989            //return true but to not trigger the scan
6990            return true;
6991        }
6992        HashSet<Integer> channels
6993                = mWifiConfigStore.makeChannelList(config,
6994                ONE_HOUR_MILLI, restrictChannelList);
6995        if (channels != null && channels.size() != 0) {
6996            StringBuilder freqs = new StringBuilder();
6997            boolean first = true;
6998            for (Integer channel : channels) {
6999                if (!first)
7000                    freqs.append(",");
7001                freqs.append(channel.toString());
7002                first = false;
7003            }
7004            //if (DBG) {
7005            loge("WifiStateMachine starting scan for " + config.configKey() + " with " + freqs);
7006            //}
7007            // Call wifi native to start the scan
7008            if (startScanNative(
7009                    WifiNative.SCAN_WITHOUT_CONNECTION_SETUP,
7010                    freqs.toString())) {
7011                // Only count battery consumption if scan request is accepted
7012                noteScanStart(SCAN_ALARM_SOURCE, null);
7013                messageHandlingStatus = MESSAGE_HANDLING_STATUS_OK;
7014            } else {
7015                // used for debug only, mark scan as failed
7016                messageHandlingStatus = MESSAGE_HANDLING_STATUS_HANDLING_ERROR;
7017            }
7018            return true;
7019        } else {
7020            if (DBG) loge("WifiStateMachine no channels for " + config.configKey());
7021            return false;
7022        }
7023    }
7024
7025    void clearCurrentConfigBSSID(String dbg) {
7026        // Clear the bssid in the current config's network block
7027        WifiConfiguration config = getCurrentWifiConfiguration();
7028        if (config == null)
7029            return;
7030        clearConfigBSSID(config, dbg);
7031    }
7032    void clearConfigBSSID(WifiConfiguration config, String dbg) {
7033        if (config == null)
7034            return;
7035        if (DBG) {
7036            loge(dbg + " " + mTargetRoamBSSID + " config " + config.configKey()
7037                    + " config.bssid " + config.BSSID);
7038        }
7039        config.autoJoinBSSID = "any";
7040        config.BSSID = "any";
7041        if (DBG) {
7042           loge(dbg + " " + config.SSID
7043                    + " nid=" + Integer.toString(config.networkId));
7044        }
7045        mWifiConfigStore.saveWifiConfigBSSID(config);
7046    }
7047
7048    class L2ConnectedState extends State {
7049        @Override
7050        public void enter() {
7051            mRssiPollToken++;
7052            if (mEnableRssiPolling) {
7053                sendMessage(CMD_RSSI_POLL, mRssiPollToken, 0);
7054            }
7055            if (mNetworkAgent != null) {
7056                loge("Have NetworkAgent when entering L2Connected");
7057                setNetworkDetailedState(DetailedState.DISCONNECTED);
7058            }
7059            setNetworkDetailedState(DetailedState.CONNECTING);
7060
7061            if (TextUtils.isEmpty(mTcpBufferSizes) == false) {
7062                mLinkProperties.setTcpBufferSizes(mTcpBufferSizes);
7063            }
7064            mNetworkAgent = new WifiNetworkAgent(getHandler().getLooper(), mContext,
7065                    "WifiNetworkAgent", mNetworkInfo, mNetworkCapabilitiesFilter,
7066                    mLinkProperties, 60);
7067
7068            // We must clear the config BSSID, as the wifi chipset may decide to roam
7069            // from this point on and having the BSSID specified in the network block would
7070            // cause the roam to faile and the device to disconnect
7071            clearCurrentConfigBSSID("L2ConnectedState");
7072        }
7073
7074        @Override
7075        public void exit() {
7076            // This is handled by receiving a NETWORK_DISCONNECTION_EVENT in ConnectModeState
7077            // Bug: 15347363
7078            // For paranoia's sake, call handleNetworkDisconnect
7079            // only if BSSID is null or last networkId
7080            // is not invalid.
7081            if (DBG) {
7082                StringBuilder sb = new StringBuilder();
7083                sb.append("leaving L2ConnectedState state nid=" + Integer.toString(mLastNetworkId));
7084                if (mLastBssid !=null) {
7085                    sb.append(" ").append(mLastBssid);
7086                }
7087            }
7088            if (mLastBssid != null || mLastNetworkId != WifiConfiguration.INVALID_NETWORK_ID) {
7089                handleNetworkDisconnect();
7090            }
7091        }
7092
7093        @Override
7094        public boolean processMessage(Message message) {
7095            logStateAndMessage(message, getClass().getSimpleName());
7096
7097            switch (message.what) {
7098              case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
7099                  handlePreDhcpSetup();
7100                  break;
7101              case DhcpStateMachine.CMD_POST_DHCP_ACTION:
7102                  handlePostDhcpSetup();
7103                  if (message.arg1 == DhcpStateMachine.DHCP_SUCCESS) {
7104                      if (DBG) log("WifiStateMachine DHCP successful");
7105                      handleIPv4Success((DhcpResults) message.obj, DhcpStateMachine.DHCP_SUCCESS);
7106                      // We advance to mVerifyingLinkState because handleIPv4Success will call
7107                      // updateLinkProperties, which then sends CMD_IP_CONFIGURATION_SUCCESSFUL.
7108                  } else if (message.arg1 == DhcpStateMachine.DHCP_FAILURE) {
7109                      if (DBG) {
7110                          int count = -1;
7111                          WifiConfiguration config = getCurrentWifiConfiguration();
7112                          if (config != null) {
7113                              count = config.numConnectionFailures;
7114                          }
7115                          log("WifiStateMachine DHCP failure count=" + count);
7116                      }
7117                      handleIPv4Failure(DhcpStateMachine.DHCP_FAILURE);
7118                      // As above, we transition to mDisconnectingState via updateLinkProperties.
7119                  }
7120                  break;
7121                case CMD_IP_CONFIGURATION_SUCCESSFUL:
7122                    handleSuccessfulIpConfiguration();
7123                    sendConnectedState();
7124                    transitionTo(mConnectedState);
7125                    break;
7126                case CMD_IP_CONFIGURATION_LOST:
7127                    // Get Link layer stats so as we get fresh tx packet counters
7128                    getWifiLinkLayerStats(true);
7129                    handleIpConfigurationLost();
7130                    transitionTo(mDisconnectingState);
7131                    break;
7132                case CMD_DISCONNECT:
7133                    mWifiNative.disconnect();
7134                    transitionTo(mDisconnectingState);
7135                    break;
7136                case WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST:
7137                    if (message.arg1 == 1) {
7138                        mWifiNative.disconnect();
7139                        mTemporarilyDisconnectWifi = true;
7140                        transitionTo(mDisconnectingState);
7141                    }
7142                    break;
7143                case CMD_SET_OPERATIONAL_MODE:
7144                    if (message.arg1 != CONNECT_MODE) {
7145                        sendMessage(CMD_DISCONNECT);
7146                        deferMessage(message);
7147                        if (message.arg1 == SCAN_ONLY_WITH_WIFI_OFF_MODE) {
7148                            noteWifiDisabledWhileAssociated();
7149                        }
7150                    }
7151                    mWifiConfigStore.
7152                                setLastSelectedConfiguration(WifiConfiguration.INVALID_NETWORK_ID);
7153                    break;
7154                case CMD_SET_COUNTRY_CODE:
7155                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_DEFERRED;
7156                    deferMessage(message);
7157                    break;
7158                case CMD_START_SCAN:
7159                    //if (DBG) {
7160                        loge("WifiStateMachine CMD_START_SCAN source " + message.arg1
7161                              + " txSuccessRate="+String.format( "%.2f", mWifiInfo.txSuccessRate)
7162                              + " rxSuccessRate="+String.format( "%.2f", mWifiInfo.rxSuccessRate)
7163                              + " targetRoamBSSID=" + mTargetRoamBSSID
7164                              + " RSSI=" + mWifiInfo.getRssi());
7165                    //}
7166                    if (message.arg1 == SCAN_ALARM_SOURCE) {
7167                        // Check if the CMD_START_SCAN message is obsolete (and thus if it should
7168                        // not be processed) and restart the scan if needed
7169                        boolean shouldScan =
7170                                mScreenOn && mWifiConfigStore.enableAutoJoinScanWhenAssociated.get();
7171                        if (!checkAndRestartDelayedScan(message.arg2,
7172                                shouldScan,
7173                                mWifiConfigStore.associatedPartialScanPeriodMilli.get(), null, null)) {
7174                            messageHandlingStatus = MESSAGE_HANDLING_STATUS_OBSOLETE;
7175                            loge("WifiStateMachine L2Connected CMD_START_SCAN source "
7176                                    + message.arg1
7177                                    + " " + message.arg2 + ", " + mDelayedScanCounter
7178                                    + " -> obsolete");
7179                            return HANDLED;
7180                        }
7181                        if (mP2pConnected.get()) {
7182                            loge("WifiStateMachine L2Connected CMD_START_SCAN source "
7183                                    + message.arg1
7184                                    + " " + message.arg2 + ", " + mDelayedScanCounter
7185                                    + " ignore because P2P is connected");
7186                            messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
7187                            return HANDLED;
7188                        }
7189                        boolean tryFullBandScan = false;
7190                        boolean restrictChannelList = false;
7191                        long now_ms = System.currentTimeMillis();
7192                        if (DBG) {
7193                            loge("WifiStateMachine CMD_START_SCAN with age="
7194                                    + Long.toString(now_ms - lastFullBandConnectedTimeMilli)
7195                                    + " interval=" + fullBandConnectedTimeIntervalMilli
7196                                    + " maxinterval=" + maxFullBandConnectedTimeIntervalMilli);
7197                        }
7198                        if (mWifiInfo != null) {
7199                            if (mWifiConfigStore.enableFullBandScanWhenAssociated.get() &&
7200                                    (now_ms - lastFullBandConnectedTimeMilli)
7201                                    > fullBandConnectedTimeIntervalMilli) {
7202                                if (DBG) {
7203                                    loge("WifiStateMachine CMD_START_SCAN try full band scan age="
7204                                         + Long.toString(now_ms - lastFullBandConnectedTimeMilli)
7205                                         + " interval=" + fullBandConnectedTimeIntervalMilli
7206                                         + " maxinterval=" + maxFullBandConnectedTimeIntervalMilli);
7207                                }
7208                                tryFullBandScan = true;
7209                            }
7210
7211                            if (mWifiInfo.txSuccessRate >
7212                                    mWifiConfigStore.maxTxPacketForFullScans
7213                                    || mWifiInfo.rxSuccessRate >
7214                                    mWifiConfigStore.maxRxPacketForFullScans) {
7215                                // Too much traffic at the interface, hence no full band scan
7216                                if (DBG) {
7217                                    loge("WifiStateMachine CMD_START_SCAN " +
7218                                            "prevent full band scan due to pkt rate");
7219                                }
7220                                tryFullBandScan = false;
7221                            }
7222
7223                            if (mWifiInfo.txSuccessRate >
7224                                    mWifiConfigStore.maxTxPacketForPartialScans
7225                                    || mWifiInfo.rxSuccessRate >
7226                                    mWifiConfigStore.maxRxPacketForPartialScans) {
7227                                // Don't scan if lots of packets are being sent
7228                                restrictChannelList = true;
7229                                if (mWifiConfigStore.alwaysEnableScansWhileAssociated.get() == 0) {
7230                                    if (DBG) {
7231                                     loge("WifiStateMachine CMD_START_SCAN source " + message.arg1
7232                                        + " ...and ignore scans"
7233                                        + " tx=" + String.format("%.2f", mWifiInfo.txSuccessRate)
7234                                        + " rx=" + String.format("%.2f", mWifiInfo.rxSuccessRate));
7235                                    }
7236                                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_REFUSED;
7237                                    return HANDLED;
7238                                }
7239                            }
7240                        }
7241
7242                        WifiConfiguration currentConfiguration = getCurrentWifiConfiguration();
7243                        if (DBG) {
7244                            loge("WifiStateMachine CMD_START_SCAN full=" +
7245                                    tryFullBandScan);
7246                        }
7247                        if (currentConfiguration != null) {
7248                            if (fullBandConnectedTimeIntervalMilli
7249                                    < mWifiConfigStore.associatedPartialScanPeriodMilli.get()) {
7250                                // Sanity
7251                                fullBandConnectedTimeIntervalMilli
7252                                        = mWifiConfigStore.associatedPartialScanPeriodMilli.get();
7253                            }
7254                            if (tryFullBandScan) {
7255                                lastFullBandConnectedTimeMilli = now_ms;
7256                                if (fullBandConnectedTimeIntervalMilli
7257                                        < mWifiConfigStore.associatedFullScanMaxIntervalMilli) {
7258                                    // Increase the interval
7259                                    fullBandConnectedTimeIntervalMilli
7260                                            = fullBandConnectedTimeIntervalMilli
7261                                            * mWifiConfigStore.associatedFullScanBackoff.get() / 8;
7262
7263                                    if (DBG) {
7264                                        loge("WifiStateMachine CMD_START_SCAN bump interval ="
7265                                        + fullBandConnectedTimeIntervalMilli);
7266                                    }
7267                                }
7268                                handleScanRequest(
7269                                        WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, message);
7270                            } else {
7271                                if (!startScanForConfiguration(
7272                                        currentConfiguration, restrictChannelList)) {
7273                                    if (DBG) {
7274                                        loge("WifiStateMachine starting scan, " +
7275                                                " did not find channels -> full");
7276                                    }
7277                                    lastFullBandConnectedTimeMilli = now_ms;
7278                                    if (fullBandConnectedTimeIntervalMilli
7279                                            < mWifiConfigStore.associatedFullScanMaxIntervalMilli) {
7280                                        // Increase the interval
7281                                        fullBandConnectedTimeIntervalMilli
7282                                                = fullBandConnectedTimeIntervalMilli
7283                                                * mWifiConfigStore.associatedFullScanBackoff.get() / 8;
7284
7285                                        if (DBG) {
7286                                            loge("WifiStateMachine CMD_START_SCAN bump interval ="
7287                                                    + fullBandConnectedTimeIntervalMilli);
7288                                        }
7289                                    }
7290                                    handleScanRequest(
7291                                                WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, message);
7292                                }
7293                            }
7294
7295                        } else {
7296                            loge("CMD_START_SCAN : connected mode and no configuration");
7297                            messageHandlingStatus = MESSAGE_HANDLING_STATUS_HANDLING_ERROR;
7298                        }
7299                    } else {
7300                        // Not scan alarm source
7301                        return NOT_HANDLED;
7302                    }
7303                    break;
7304                    /* Ignore connection to same network */
7305                case WifiManager.CONNECT_NETWORK:
7306                    int netId = message.arg1;
7307                    if (mWifiInfo.getNetworkId() == netId) {
7308                        break;
7309                    }
7310                    return NOT_HANDLED;
7311                    /* Ignore */
7312                case WifiMonitor.NETWORK_CONNECTION_EVENT:
7313                    break;
7314                case CMD_RSSI_POLL:
7315                    if (message.arg1 == mRssiPollToken) {
7316                        if (mWifiConfigStore.enableChipWakeUpWhenAssociated.get()) {
7317                            if (VVDBG) log(" get link layer stats " + mWifiLinkLayerStatsSupported);
7318                            WifiLinkLayerStats stats = getWifiLinkLayerStats(VDBG);
7319                            if (stats != null) {
7320                                // Sanity check the results provided by driver
7321                                if (mWifiInfo.getRssi() != WifiInfo.INVALID_RSSI
7322                                        && (stats.rssi_mgmt == 0
7323                                        || stats.beacon_rx == 0)) {
7324                                    stats = null;
7325                                }
7326                            }
7327                            // Get Info and continue polling
7328                            fetchRssiLinkSpeedAndFrequencyNative();
7329                            calculateWifiScore(stats);
7330                        }
7331                        sendMessageDelayed(obtainMessage(CMD_RSSI_POLL,
7332                                mRssiPollToken, 0), POLL_RSSI_INTERVAL_MSECS);
7333
7334                        if (DBG) sendRssiChangeBroadcast(mWifiInfo.getRssi());
7335                    } else {
7336                        // Polling has completed
7337                    }
7338                    break;
7339                case CMD_ENABLE_RSSI_POLL:
7340                    if (mWifiConfigStore.enableRssiPollWhenAssociated.get()) {
7341                        mEnableRssiPolling = (message.arg1 == 1);
7342                    } else {
7343                        mEnableRssiPolling = false;
7344                    }
7345                    mRssiPollToken++;
7346                    if (mEnableRssiPolling) {
7347                        // First poll
7348                        fetchRssiLinkSpeedAndFrequencyNative();
7349                        sendMessageDelayed(obtainMessage(CMD_RSSI_POLL,
7350                                mRssiPollToken, 0), POLL_RSSI_INTERVAL_MSECS);
7351                    } else {
7352                        cleanWifiScore();
7353                    }
7354                    break;
7355                case WifiManager.RSSI_PKTCNT_FETCH:
7356                    RssiPacketCountInfo info = new RssiPacketCountInfo();
7357                    fetchRssiLinkSpeedAndFrequencyNative();
7358                    info.rssi = mWifiInfo.getRssi();
7359                    fetchPktcntNative(info);
7360                    replyToMessage(message, WifiManager.RSSI_PKTCNT_FETCH_SUCCEEDED, info);
7361                    break;
7362                case CMD_DELAYED_NETWORK_DISCONNECT:
7363                    if (!linkDebouncing && mWifiConfigStore.enableLinkDebouncing) {
7364
7365                        // Ignore if we are not debouncing
7366                        loge("CMD_DELAYED_NETWORK_DISCONNECT and not debouncing - ignore "
7367                                + message.arg1);
7368                        return HANDLED;
7369                    } else {
7370                        loge("CMD_DELAYED_NETWORK_DISCONNECT and debouncing - disconnect "
7371                                + message.arg1);
7372
7373                        linkDebouncing = false;
7374                        // If we are still debouncing while this message comes,
7375                        // it means we were not able to reconnect within the alloted time
7376                        // = LINK_FLAPPING_DEBOUNCE_MSEC
7377                        // and thus, trigger a real disconnect
7378                        handleNetworkDisconnect();
7379                        transitionTo(mDisconnectedState);
7380                    }
7381                    break;
7382                case CMD_ASSOCIATED_BSSID:
7383                    if ((String) message.obj == null) {
7384                        loge("Associated command w/o BSSID");
7385                        break;
7386                    }
7387                    mLastBssid = (String) message.obj;
7388                    if (mLastBssid != null
7389                            && (mWifiInfo.getBSSID() == null
7390                            || !mLastBssid.equals(mWifiInfo.getBSSID()))) {
7391                        mWifiInfo.setBSSID((String) message.obj);
7392                        sendNetworkStateChangeBroadcast(mLastBssid);
7393                    }
7394                    break;
7395                default:
7396                    return NOT_HANDLED;
7397            }
7398
7399            return HANDLED;
7400        }
7401    }
7402
7403    class ObtainingIpState extends State {
7404        @Override
7405        public void enter() {
7406            if (DBG) {
7407                String key = "";
7408                if (getCurrentWifiConfiguration() != null) {
7409                    key = getCurrentWifiConfiguration().configKey();
7410                }
7411                log("enter ObtainingIpState netId=" + Integer.toString(mLastNetworkId)
7412                        + " " + key + " "
7413                        + " roam=" + mAutoRoaming
7414                        + " static=" + mWifiConfigStore.isUsingStaticIp(mLastNetworkId)
7415                        + " watchdog= " + obtainingIpWatchdogCount);
7416            }
7417
7418            // Reset link Debouncing, indicating we have successfully re-connected to the AP
7419            // We might still be roaming
7420            linkDebouncing = false;
7421
7422            // Send event to CM & network change broadcast
7423            setNetworkDetailedState(DetailedState.OBTAINING_IPADDR);
7424
7425            // We must clear the config BSSID, as the wifi chipset may decide to roam
7426            // from this point on and having the BSSID specified in the network block would
7427            // cause the roam to faile and the device to disconnect
7428            clearCurrentConfigBSSID("ObtainingIpAddress");
7429
7430            try {
7431                mNwService.enableIpv6(mInterfaceName);
7432            } catch (RemoteException re) {
7433                loge("Failed to enable IPv6: " + re);
7434            } catch (IllegalStateException e) {
7435                loge("Failed to enable IPv6: " + e);
7436            }
7437
7438            if (!mWifiConfigStore.isUsingStaticIp(mLastNetworkId)) {
7439                if (isRoaming()) {
7440                    renewDhcp();
7441                } else {
7442                    // Remove any IP address on the interface in case we're switching from static
7443                    // IP configuration to DHCP. This is safe because if we get here when not
7444                    // roaming, we don't have a usable address.
7445                    clearIPv4Address(mInterfaceName);
7446                    startDhcp();
7447                }
7448                obtainingIpWatchdogCount++;
7449                loge("Start Dhcp Watchdog " + obtainingIpWatchdogCount);
7450                // Get Link layer stats so as we get fresh tx packet counters
7451                getWifiLinkLayerStats(true);
7452                sendMessageDelayed(obtainMessage(CMD_OBTAINING_IP_ADDRESS_WATCHDOG_TIMER,
7453                        obtainingIpWatchdogCount, 0), OBTAINING_IP_ADDRESS_GUARD_TIMER_MSEC);
7454            } else {
7455                // stop any running dhcp before assigning static IP
7456                stopDhcp();
7457                StaticIpConfiguration config = mWifiConfigStore.getStaticIpConfiguration(
7458                        mLastNetworkId);
7459                if (config.ipAddress == null) {
7460                    loge("Static IP lacks address");
7461                    sendMessage(CMD_STATIC_IP_FAILURE);
7462                } else {
7463                    InterfaceConfiguration ifcg = new InterfaceConfiguration();
7464                    ifcg.setLinkAddress(config.ipAddress);
7465                    ifcg.setInterfaceUp();
7466                    try {
7467                        mNwService.setInterfaceConfig(mInterfaceName, ifcg);
7468                        if (DBG) log("Static IP configuration succeeded");
7469                        DhcpResults dhcpResults = new DhcpResults(config);
7470                        sendMessage(CMD_STATIC_IP_SUCCESS, dhcpResults);
7471                    } catch (RemoteException re) {
7472                        loge("Static IP configuration failed: " + re);
7473                        sendMessage(CMD_STATIC_IP_FAILURE);
7474                    } catch (IllegalStateException e) {
7475                        loge("Static IP configuration failed: " + e);
7476                        sendMessage(CMD_STATIC_IP_FAILURE);
7477                    }
7478                }
7479            }
7480        }
7481      @Override
7482      public boolean processMessage(Message message) {
7483          logStateAndMessage(message, getClass().getSimpleName());
7484
7485          switch(message.what) {
7486              case CMD_STATIC_IP_SUCCESS:
7487                  handleIPv4Success((DhcpResults) message.obj, CMD_STATIC_IP_SUCCESS);
7488                  break;
7489              case CMD_STATIC_IP_FAILURE:
7490                  handleIPv4Failure(CMD_STATIC_IP_FAILURE);
7491                  break;
7492              case CMD_AUTO_CONNECT:
7493              case CMD_AUTO_ROAM:
7494                  messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
7495                  break;
7496              case WifiManager.SAVE_NETWORK:
7497              case WifiStateMachine.CMD_AUTO_SAVE_NETWORK:
7498                  messageHandlingStatus = MESSAGE_HANDLING_STATUS_DEFERRED;
7499                  deferMessage(message);
7500                  break;
7501                  /* Defer any power mode changes since we must keep active power mode at DHCP */
7502              case CMD_SET_HIGH_PERF_MODE:
7503                  messageHandlingStatus = MESSAGE_HANDLING_STATUS_DEFERRED;
7504                  deferMessage(message);
7505                  break;
7506                  /* Defer scan request since we should not switch to other channels at DHCP */
7507              case CMD_START_SCAN:
7508                  messageHandlingStatus = MESSAGE_HANDLING_STATUS_DEFERRED;
7509                  deferMessage(message);
7510                  break;
7511              case CMD_OBTAINING_IP_ADDRESS_WATCHDOG_TIMER:
7512                  if (message.arg1 == obtainingIpWatchdogCount) {
7513                      loge("ObtainingIpAddress: Watchdog Triggered, count="
7514                              + obtainingIpWatchdogCount);
7515                      handleIpConfigurationLost();
7516                      transitionTo(mDisconnectingState);
7517                      break;
7518                  }
7519                  messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
7520                  break;
7521              default:
7522                  return NOT_HANDLED;
7523          }
7524          return HANDLED;
7525      }
7526    }
7527
7528    class VerifyingLinkState extends State {
7529        @Override
7530        public void enter() {
7531            log(getName() + " enter");
7532            setNetworkDetailedState(DetailedState.VERIFYING_POOR_LINK);
7533            mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.VERIFYING_POOR_LINK);
7534            sendNetworkStateChangeBroadcast(mLastBssid);
7535            // End roaming
7536            mAutoRoaming = WifiAutoJoinController.AUTO_JOIN_IDLE;
7537        }
7538        @Override
7539        public boolean processMessage(Message message) {
7540            logStateAndMessage(message, getClass().getSimpleName());
7541
7542            switch (message.what) {
7543                case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
7544                    // Stay here
7545                    log(getName() + " POOR_LINK_DETECTED: no transition");
7546                    break;
7547                case WifiWatchdogStateMachine.GOOD_LINK_DETECTED:
7548                    log(getName() + " GOOD_LINK_DETECTED: transition to captive portal check");
7549
7550                    log(getName() + " GOOD_LINK_DETECTED: transition to CONNECTED");
7551                    sendConnectedState();
7552                    transitionTo(mConnectedState);
7553                    break;
7554                default:
7555                    if (DBG) log(getName() + " what=" + message.what + " NOT_HANDLED");
7556                    return NOT_HANDLED;
7557            }
7558            return HANDLED;
7559        }
7560    }
7561
7562    private void sendConnectedState() {
7563        // Send out a broadcast with the CAPTIVE_PORTAL_CHECK to preserve
7564        // existing behaviour. The captive portal check really happens after we
7565        // transition into DetailedState.CONNECTED.
7566        setNetworkDetailedState(DetailedState.CAPTIVE_PORTAL_CHECK);
7567        mWifiConfigStore.updateStatus(mLastNetworkId,
7568        DetailedState.CAPTIVE_PORTAL_CHECK);
7569        sendNetworkStateChangeBroadcast(mLastBssid);
7570
7571        if (mWifiConfigStore.getLastSelectedConfiguration() != null) {
7572            if (mNetworkAgent != null) mNetworkAgent.explicitlySelected();
7573        }
7574
7575        setNetworkDetailedState(DetailedState.CONNECTED);
7576        mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.CONNECTED);
7577        sendNetworkStateChangeBroadcast(mLastBssid);
7578    }
7579
7580    class RoamingState extends State {
7581        boolean mAssociated;
7582        @Override
7583        public void enter() {
7584            if (DBG) {
7585                log("RoamingState Enter"
7586                        + " mScreenOn=" + mScreenOn );
7587            }
7588            setScanAlarm(false);
7589
7590            // Make sure we disconnect if roaming fails
7591            roamWatchdogCount++;
7592            loge("Start Roam Watchdog " + roamWatchdogCount);
7593            sendMessageDelayed(obtainMessage(CMD_ROAM_WATCHDOG_TIMER,
7594                    roamWatchdogCount, 0), ROAM_GUARD_TIMER_MSEC);
7595            mAssociated = false;
7596        }
7597        @Override
7598        public boolean processMessage(Message message) {
7599            logStateAndMessage(message, getClass().getSimpleName());
7600            WifiConfiguration config;
7601            switch (message.what) {
7602                case CMD_IP_CONFIGURATION_LOST:
7603                    config = getCurrentWifiConfiguration();
7604                    if (config != null) {
7605                        mWifiConfigStore.noteRoamingFailure(config,
7606                                WifiConfiguration.ROAMING_FAILURE_IP_CONFIG);
7607                    }
7608                    return NOT_HANDLED;
7609               case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
7610                    if (DBG) log("Roaming and Watchdog reports poor link -> ignore");
7611                    return HANDLED;
7612               case CMD_UNWANTED_NETWORK:
7613                    if (DBG) log("Roaming and CS doesnt want the network -> ignore");
7614                    return HANDLED;
7615               case CMD_SET_OPERATIONAL_MODE:
7616                    if (message.arg1 != CONNECT_MODE) {
7617                        deferMessage(message);
7618                    }
7619                    break;
7620               case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
7621                    /**
7622                     * If we get a SUPPLICANT_STATE_CHANGE_EVENT indicating a DISCONNECT
7623                     * before NETWORK_DISCONNECTION_EVENT
7624                     * And there is an associated BSSID corresponding to our target BSSID, then
7625                     * we have missed the network disconnection, transition to mDisconnectedState
7626                     * and handle the rest of the events there.
7627                     */
7628                    StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
7629                    if (stateChangeResult.state == SupplicantState.DISCONNECTED
7630                            || stateChangeResult.state == SupplicantState.INACTIVE
7631                            || stateChangeResult.state == SupplicantState.INTERFACE_DISABLED) {
7632                        if (DBG) {
7633                            log("STATE_CHANGE_EVENT in roaming state "
7634                                    + stateChangeResult.toString() );
7635                        }
7636                        if (stateChangeResult.BSSID != null
7637                                && stateChangeResult.BSSID.equals(mTargetRoamBSSID)) {
7638                            handleNetworkDisconnect();
7639                            transitionTo(mDisconnectedState);
7640                        }
7641                    }
7642                    if (stateChangeResult.state == SupplicantState.ASSOCIATED) {
7643                        // We completed the layer2 roaming part
7644                        mAssociated = true;
7645                        if (stateChangeResult.BSSID != null) {
7646                            mTargetRoamBSSID = (String) stateChangeResult.BSSID;
7647                        }
7648                    }
7649                    break;
7650                case CMD_ROAM_WATCHDOG_TIMER:
7651                    if (roamWatchdogCount == message.arg1) {
7652                        if (DBG) log("roaming watchdog! -> disconnect");
7653                        mRoamFailCount++;
7654                        handleNetworkDisconnect();
7655                        mWifiNative.disconnect();
7656                        transitionTo(mDisconnectedState);
7657                    }
7658                    break;
7659               case WifiMonitor.NETWORK_CONNECTION_EVENT:
7660                   if (mAssociated) {
7661                       if (DBG) log("roaming and Network connection established");
7662                       mLastNetworkId = message.arg1;
7663                       mLastBssid = (String) message.obj;
7664                       mWifiInfo.setBSSID(mLastBssid);
7665                       mWifiInfo.setNetworkId(mLastNetworkId);
7666                       mWifiConfigStore.handleBSSIDBlackList(mLastNetworkId, mLastBssid, true);
7667                       sendNetworkStateChangeBroadcast(mLastBssid);
7668                       transitionTo(mObtainingIpState);
7669                   } else {
7670                       messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
7671                   }
7672                   break;
7673               case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
7674                   // Throw away but only if it corresponds to the network we're roaming to
7675                   String bssid = (String)message.obj;
7676                   if (true) {
7677                       String target = "";
7678                       if (mTargetRoamBSSID != null) target = mTargetRoamBSSID;
7679                       log("NETWORK_DISCONNECTION_EVENT in roaming state"
7680                               + " BSSID=" + bssid
7681                               + " target=" + target);
7682                   }
7683                   if (bssid != null && bssid.equals(mTargetRoamBSSID)) {
7684                       handleNetworkDisconnect();
7685                       transitionTo(mDisconnectedState);
7686                   }
7687                   break;
7688                case WifiMonitor.SSID_TEMP_DISABLED:
7689                    // Auth error while roaming
7690                    loge("SSID_TEMP_DISABLED nid=" + Integer.toString(mLastNetworkId)
7691                            + " id=" + Integer.toString(message.arg1)
7692                            + " isRoaming=" + isRoaming()
7693                            + " roam=" + Integer.toString(mAutoRoaming));
7694                    if (message.arg1 == mLastNetworkId) {
7695                        config = getCurrentWifiConfiguration();
7696                        if (config != null) {
7697                            mWifiConfigStore.noteRoamingFailure(config,
7698                                    WifiConfiguration.ROAMING_FAILURE_AUTH_FAILURE);
7699                        }
7700                        handleNetworkDisconnect();
7701                        transitionTo(mDisconnectingState);
7702                    }
7703                    return NOT_HANDLED;
7704                case CMD_START_SCAN:
7705                    deferMessage(message);
7706                    break;
7707                default:
7708                    return NOT_HANDLED;
7709            }
7710            return HANDLED;
7711        }
7712
7713        @Override
7714        public void exit() {
7715            loge("WifiStateMachine: Leaving Roaming state");
7716        }
7717    }
7718
7719    class ConnectedState extends State {
7720        @Override
7721        public void enter() {
7722            String address;
7723            updateDefaultRouteMacAddress(1000);
7724            if (DBG) {
7725                log("ConnectedState Enter "
7726                        + " mScreenOn=" + mScreenOn
7727                        + " scanperiod="
7728                        + Integer.toString(mWifiConfigStore.associatedPartialScanPeriodMilli.get()) );
7729            }
7730            if (mScreenOn
7731                    && mWifiConfigStore.enableAutoJoinScanWhenAssociated.get()) {
7732                // restart scan alarm
7733                startDelayedScan(mWifiConfigStore.associatedPartialScanPeriodMilli.get(), null, null);
7734            }
7735            registerConnected();
7736            lastConnectAttempt = 0;
7737            targetWificonfiguration = null;
7738            // Paranoia
7739            linkDebouncing = false;
7740
7741            // Not roaming anymore
7742            mAutoRoaming = WifiAutoJoinController.AUTO_JOIN_IDLE;
7743
7744            if (testNetworkDisconnect) {
7745                testNetworkDisconnectCounter++;
7746                loge("ConnectedState Enter start disconnect test " +
7747                        testNetworkDisconnectCounter);
7748                sendMessageDelayed(obtainMessage(CMD_TEST_NETWORK_DISCONNECT,
7749                        testNetworkDisconnectCounter, 0), 15000);
7750            }
7751
7752            // Reenable all networks, allow for hidden networks to be scanned
7753            mWifiConfigStore.enableAllNetworks();
7754
7755            mLastDriverRoamAttempt = 0;
7756        }
7757        @Override
7758        public boolean processMessage(Message message) {
7759            WifiConfiguration config = null;
7760            logStateAndMessage(message, getClass().getSimpleName());
7761
7762            switch (message.what) {
7763                case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
7764                    if (DBG) log("Watchdog reports poor link");
7765                    transitionTo(mVerifyingLinkState);
7766                    break;
7767                case CMD_UNWANTED_NETWORK:
7768                    if (message.arg1 == network_status_unwanted_disconnect) {
7769                        mWifiConfigStore.handleBadNetworkDisconnectReport(mLastNetworkId, mWifiInfo);
7770                        mWifiNative.disconnect();
7771                        transitionTo(mDisconnectingState);
7772                    } else if (message.arg1 == network_status_unwanted_disable_autojoin) {
7773                        config = getCurrentWifiConfiguration();
7774                        if (config != null) {
7775                            // Disable autojoin
7776                            config.numNoInternetAccessReports += 1;
7777                        }
7778                    }
7779                    return HANDLED;
7780                case CMD_NETWORK_STATUS:
7781                    if (message.arg1 == NetworkAgent.VALID_NETWORK) {
7782                        config = getCurrentWifiConfiguration();
7783                        if (config != null) {
7784                            // re-enable autojoin
7785                            config.numNoInternetAccessReports = 0;
7786                            config.validatedInternetAccess = true;
7787                        }
7788                    }
7789                    return HANDLED;
7790                case CMD_TEST_NETWORK_DISCONNECT:
7791                    // Force a disconnect
7792                    if (message.arg1 == testNetworkDisconnectCounter) {
7793                        mWifiNative.disconnect();
7794                    }
7795                    break;
7796                case CMD_ASSOCIATED_BSSID:
7797                    // ASSOCIATING to a new BSSID while already connected, indicates
7798                    // that driver is roaming
7799                    mLastDriverRoamAttempt = System.currentTimeMillis();
7800                    String toBSSID = (String)message.obj;
7801                    if (toBSSID != null && !toBSSID.equals(mWifiInfo.getBSSID())) {
7802                        mWifiConfigStore.driverRoamedFrom(mWifiInfo);
7803                    }
7804                    return NOT_HANDLED;
7805                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
7806                    long lastRoam = 0;
7807                    if (mLastDriverRoamAttempt != 0) {
7808                        // Calculate time since last driver roam attempt
7809                        lastRoam = System.currentTimeMillis() - mLastDriverRoamAttempt;
7810                        mLastDriverRoamAttempt = 0;
7811                    }
7812                    config = getCurrentWifiConfiguration();
7813                    if (mScreenOn
7814                            && !linkDebouncing
7815                            && config != null
7816                            && config.autoJoinStatus == WifiConfiguration.AUTO_JOIN_ENABLED
7817                            && !mWifiConfigStore.isLastSelectedConfiguration(config)
7818                            && (message.arg2 != 3 /* reason cannot be 3, i.e. locally generated */
7819                                || (lastRoam > 0 && lastRoam < 2000) /* unless driver is roaming */)
7820                            && ((ScanResult.is24GHz(mWifiInfo.getFrequency())
7821                                    && mWifiInfo.getRssi() >
7822                                    WifiConfiguration.BAD_RSSI_24)
7823                                    || (ScanResult.is5GHz(mWifiInfo.getFrequency())
7824                                    && mWifiInfo.getRssi() >
7825                                    WifiConfiguration.BAD_RSSI_5))) {
7826                        // Start de-bouncing the L2 disconnection:
7827                        // this L2 disconnection might be spurious.
7828                        // Hence we allow 7 seconds for the state machine to try
7829                        // to reconnect, go thru the
7830                        // roaming cycle and enter Obtaining IP address
7831                        // before signalling the disconnect to ConnectivityService and L3
7832                        startScanForConfiguration(getCurrentWifiConfiguration(), false);
7833                        linkDebouncing = true;
7834
7835                        sendMessageDelayed(obtainMessage(CMD_DELAYED_NETWORK_DISCONNECT,
7836                                0, mLastNetworkId), LINK_FLAPPING_DEBOUNCE_MSEC);
7837                        if (DBG) {
7838                            log("NETWORK_DISCONNECTION_EVENT in connected state"
7839                                    + " BSSID=" + mWifiInfo.getBSSID()
7840                                    + " RSSI=" + mWifiInfo.getRssi()
7841                                    + " freq=" + mWifiInfo.getFrequency()
7842                                    + " reason=" + message.arg2
7843                                    + " -> debounce");
7844                        }
7845                        return HANDLED;
7846                    } else {
7847                        if (DBG) {
7848                            int ajst = -1;
7849                            if (config != null) ajst = config.autoJoinStatus;
7850                            log("NETWORK_DISCONNECTION_EVENT in connected state"
7851                                    + " BSSID=" + mWifiInfo.getBSSID()
7852                                    + " RSSI=" + mWifiInfo.getRssi()
7853                                    + " freq=" + mWifiInfo.getFrequency()
7854                                    + " was debouncing=" + linkDebouncing
7855                                    + " reason=" + message.arg2
7856                                    + " ajst=" + ajst);
7857                        }
7858                    }
7859                    break;
7860                case CMD_AUTO_ROAM:
7861                    // Clear the driver roam indication since we are attempting a framerwork roam
7862                    mLastDriverRoamAttempt = 0;
7863
7864                    /* Connect command coming from auto-join */
7865                    ScanResult candidate = (ScanResult)message.obj;
7866                    String bssid = "any";
7867                    if (candidate != null && candidate.is5GHz()) {
7868                        // Only lock BSSID for 5GHz networks
7869                        bssid = candidate.BSSID;
7870                    }
7871                    int netId = mLastNetworkId;
7872                    config = getCurrentWifiConfiguration();
7873
7874
7875                    if (config == null) {
7876                        loge("AUTO_ROAM and no config, bail out...");
7877                        break;
7878                    }
7879
7880                    loge("CMD_AUTO_ROAM sup state "
7881                            + mSupplicantStateTracker.getSupplicantStateName()
7882                            + " my state " + getCurrentState().getName()
7883                            + " nid=" + Integer.toString(netId)
7884                            + " config " + config.configKey()
7885                            + " roam=" + Integer.toString(message.arg2)
7886                            + " to " + bssid
7887                            + " targetRoamBSSID " + mTargetRoamBSSID);
7888
7889                    /* Save the BSSID so as to lock it @ firmware */
7890                    if (!autoRoamSetBSSID(config, bssid) && !linkDebouncing) {
7891                        loge("AUTO_ROAM nothing to do");
7892                        // Same BSSID, nothing to do
7893                        messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
7894                        break;
7895                    };
7896
7897                    // Make sure the network is enabled, since supplicant will not reenable it
7898                    mWifiConfigStore.enableNetworkWithoutBroadcast(netId, false);
7899
7900                    boolean ret = false;
7901                    if (mLastNetworkId != netId) {
7902                       if (mWifiConfigStore.selectNetwork(config, /* updatePriorities = */ false) &&
7903                           mWifiNative.reconnect()) {
7904                           ret = true;
7905                       }
7906                    } else {
7907                         ret = mWifiNative.reassociate();
7908                    }
7909                    if (ret) {
7910                        lastConnectAttempt = System.currentTimeMillis();
7911                        targetWificonfiguration = mWifiConfigStore.getWifiConfiguration(netId);
7912
7913                        // replyToMessage(message, WifiManager.CONNECT_NETWORK_SUCCEEDED);
7914                        mAutoRoaming = message.arg2;
7915                        transitionTo(mRoamingState);
7916
7917                    } else {
7918                        loge("Failed to connect config: " + config + " netId: " + netId);
7919                        replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
7920                                WifiManager.ERROR);
7921                        messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
7922                        break;
7923                    }
7924                    break;
7925                default:
7926                    return NOT_HANDLED;
7927            }
7928            return HANDLED;
7929        }
7930
7931        @Override
7932        public void exit() {
7933            loge("WifiStateMachine: Leaving Connected state");
7934            setScanAlarm(false);
7935            mLastDriverRoamAttempt = 0;
7936        }
7937    }
7938
7939    class DisconnectingState extends State {
7940
7941        @Override
7942        public void enter() {
7943
7944            if (PDBG) {
7945                loge(" Enter DisconnectingState State scan interval " + mFrameworkScanIntervalMs
7946                        + " mEnableBackgroundScan= " + mEnableBackgroundScan
7947                        + " screenOn=" + mScreenOn);
7948            }
7949
7950            // Make sure we disconnect: we enter this state prior connecting to a new
7951            // network, waiting for either a DISCONECT event or a SUPPLICANT_STATE_CHANGE
7952            // event which in this case will be indicating that supplicant started to associate.
7953            // In some cases supplicant doesn't ignore the connect requests (it might not
7954            // find the target SSID in its cache),
7955            // Therefore we end up stuck that state, hence the need for the watchdog.
7956            disconnectingWatchdogCount++;
7957            loge("Start Disconnecting Watchdog " + disconnectingWatchdogCount);
7958            sendMessageDelayed(obtainMessage(CMD_DISCONNECTING_WATCHDOG_TIMER,
7959                    disconnectingWatchdogCount, 0), DISCONNECTING_GUARD_TIMER_MSEC);
7960        }
7961
7962        @Override
7963        public boolean processMessage(Message message) {
7964            logStateAndMessage(message, getClass().getSimpleName());
7965            switch (message.what) {
7966                case CMD_SET_OPERATIONAL_MODE:
7967                    if (message.arg1 != CONNECT_MODE) {
7968                        deferMessage(message);
7969                    }
7970                    break;
7971                case CMD_START_SCAN:
7972                    deferMessage(message);
7973                    return HANDLED;
7974                case CMD_DISCONNECTING_WATCHDOG_TIMER:
7975                    if (disconnectingWatchdogCount == message.arg1) {
7976                        if (DBG) log("disconnecting watchdog! -> disconnect");
7977                        handleNetworkDisconnect();
7978                        transitionTo(mDisconnectedState);
7979                    }
7980                    break;
7981                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
7982                    /**
7983                     * If we get a SUPPLICANT_STATE_CHANGE_EVENT before NETWORK_DISCONNECTION_EVENT
7984                     * we have missed the network disconnection, transition to mDisconnectedState
7985                     * and handle the rest of the events there
7986                     */
7987                    deferMessage(message);
7988                    handleNetworkDisconnect();
7989                    transitionTo(mDisconnectedState);
7990                    break;
7991                default:
7992                    return NOT_HANDLED;
7993            }
7994            return HANDLED;
7995        }
7996    }
7997
7998    class DisconnectedState extends State {
7999        @Override
8000        public void enter() {
8001            // We dont scan frequently if this is a temporary disconnect
8002            // due to p2p
8003            if (mTemporarilyDisconnectWifi) {
8004                mWifiP2pChannel.sendMessage(WifiP2pServiceImpl.DISCONNECT_WIFI_RESPONSE);
8005                return;
8006            }
8007
8008            mFrameworkScanIntervalMs = Settings.Global.getLong(mContext.getContentResolver(),
8009                    Settings.Global.WIFI_FRAMEWORK_SCAN_INTERVAL_MS,
8010                    mDefaultFrameworkScanIntervalMs);
8011
8012            if (PDBG) {
8013                loge(" Enter disconnected State scan interval " + mFrameworkScanIntervalMs
8014                        + " mEnableBackgroundScan= " + mEnableBackgroundScan
8015                        + " screenOn=" + mScreenOn
8016                        + " mFrameworkScanIntervalMs=" + mFrameworkScanIntervalMs);
8017            }
8018
8019            /** clear the roaming state, if we were roaming, we failed */
8020            mAutoRoaming = WifiAutoJoinController.AUTO_JOIN_IDLE;
8021
8022            if (mScreenOn) {
8023                /**
8024                 * screen lit and => delayed timer
8025                 */
8026                startDelayedScan(mDisconnectedScanPeriodMs, null, null);
8027            } else {
8028                /**
8029                 * screen dark and PNO supported => scan alarm disabled
8030                 */
8031                if (mEnableBackgroundScan) {
8032                    /* If a regular scan result is pending, do not initiate background
8033                     * scan until the scan results are returned. This is needed because
8034                     * initiating a background scan will cancel the regular scan and
8035                     * scan results will not be returned until background scanning is
8036                     * cleared
8037                     */
8038                    if (!mIsScanOngoing) {
8039                        enableBackgroundScan(true);
8040                    }
8041                } else {
8042                    setScanAlarm(true);
8043                }
8044            }
8045
8046            /**
8047             * If we have no networks saved, the supplicant stops doing the periodic scan.
8048             * The scans are useful to notify the user of the presence of an open network.
8049             * Note that these are not wake up scans.
8050             */
8051            if (!mP2pConnected.get() && mWifiConfigStore.getConfiguredNetworks().size() == 0) {
8052                sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,
8053                        ++mPeriodicScanToken, 0), mSupplicantScanIntervalMs);
8054            }
8055
8056            mDisconnectedTimeStamp = System.currentTimeMillis();
8057
8058        }
8059        @Override
8060        public boolean processMessage(Message message) {
8061            boolean ret = HANDLED;
8062
8063            logStateAndMessage(message, getClass().getSimpleName());
8064
8065            switch (message.what) {
8066                case CMD_NO_NETWORKS_PERIODIC_SCAN:
8067                    if (mP2pConnected.get()) break;
8068                    if (message.arg1 == mPeriodicScanToken &&
8069                            mWifiConfigStore.getConfiguredNetworks().size() == 0) {
8070                        startScan(UNKNOWN_SCAN_SOURCE, -1, null, null);
8071                        sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,
8072                                    ++mPeriodicScanToken, 0), mSupplicantScanIntervalMs);
8073                    }
8074                    break;
8075                case WifiManager.FORGET_NETWORK:
8076                case CMD_REMOVE_NETWORK:
8077                    // Set up a delayed message here. After the forget/remove is handled
8078                    // the handled delayed message will determine if there is a need to
8079                    // scan and continue
8080                    sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,
8081                                ++mPeriodicScanToken, 0), mSupplicantScanIntervalMs);
8082                    ret = NOT_HANDLED;
8083                    break;
8084                case CMD_SET_OPERATIONAL_MODE:
8085                    if (message.arg1 != CONNECT_MODE) {
8086                        mOperationalMode = message.arg1;
8087
8088                        mWifiConfigStore.disableAllNetworks();
8089                        if (mOperationalMode == SCAN_ONLY_WITH_WIFI_OFF_MODE) {
8090                            mWifiP2pChannel.sendMessage(CMD_DISABLE_P2P_REQ);
8091                            setWifiState(WIFI_STATE_DISABLED);
8092                        }
8093                        transitionTo(mScanModeState);
8094                    }
8095                    mWifiConfigStore.
8096                            setLastSelectedConfiguration(WifiConfiguration.INVALID_NETWORK_ID);
8097                    break;
8098                    /* Ignore network disconnect */
8099                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
8100                    break;
8101                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
8102                    StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
8103                    if (DBG) {
8104                        loge("SUPPLICANT_STATE_CHANGE_EVENT state=" + stateChangeResult.state +
8105                                " -> state= " + WifiInfo.getDetailedStateOf(stateChangeResult.state)
8106                                + " debouncing=" + linkDebouncing);
8107                    }
8108                    setNetworkDetailedState(WifiInfo.getDetailedStateOf(stateChangeResult.state));
8109                    /* ConnectModeState does the rest of the handling */
8110                    ret = NOT_HANDLED;
8111                    break;
8112                case CMD_START_SCAN:
8113                    if (!checkOrDeferScanAllowed(message)) {
8114                        // The scan request was rescheduled
8115                        messageHandlingStatus = MESSAGE_HANDLING_STATUS_REFUSED;
8116                        return HANDLED;
8117                    }
8118                    /* Disable background scan temporarily during a regular scan */
8119                    if (mEnableBackgroundScan) {
8120                        enableBackgroundScan(false);
8121                    }
8122                    if (message.arg1 == SCAN_ALARM_SOURCE) {
8123                        // Check if the CMD_START_SCAN message is obsolete (and thus if it should
8124                        // not be processed) and restart the scan
8125                        int period =  mDisconnectedScanPeriodMs;
8126                        if (mP2pConnected.get()) {
8127                           period = (int)Settings.Global.getLong(mContext.getContentResolver(),
8128                                    Settings.Global.WIFI_SCAN_INTERVAL_WHEN_P2P_CONNECTED_MS,
8129                                    mDisconnectedScanPeriodMs);
8130                        }
8131                        if (!checkAndRestartDelayedScan(message.arg2,
8132                                true, period, null, null)) {
8133                            messageHandlingStatus = MESSAGE_HANDLING_STATUS_OBSOLETE;
8134                            loge("WifiStateMachine Disconnected CMD_START_SCAN source "
8135                                    + message.arg1
8136                                    + " " + message.arg2 + ", " + mDelayedScanCounter
8137                                    + " -> obsolete");
8138                            return HANDLED;
8139                        }
8140                        handleScanRequest(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, message);
8141                        ret = HANDLED;
8142                    } else {
8143                        ret = NOT_HANDLED;
8144                    }
8145                    break;
8146                case WifiMonitor.SCAN_RESULTS_EVENT:
8147                    /* Re-enable background scan when a pending scan result is received */
8148                    if (mEnableBackgroundScan && mIsScanOngoing) {
8149                        enableBackgroundScan(true);
8150                    }
8151                    /* Handled in parent state */
8152                    ret = NOT_HANDLED;
8153                    break;
8154                case WifiP2pServiceImpl.P2P_CONNECTION_CHANGED:
8155                    NetworkInfo info = (NetworkInfo) message.obj;
8156                    mP2pConnected.set(info.isConnected());
8157                    if (mP2pConnected.get()) {
8158                        int defaultInterval = mContext.getResources().getInteger(
8159                                R.integer.config_wifi_scan_interval_p2p_connected);
8160                        long scanIntervalMs = Settings.Global.getLong(mContext.getContentResolver(),
8161                                Settings.Global.WIFI_SCAN_INTERVAL_WHEN_P2P_CONNECTED_MS,
8162                                defaultInterval);
8163                        mWifiNative.setScanInterval((int) scanIntervalMs/1000);
8164                    } else if (mWifiConfigStore.getConfiguredNetworks().size() == 0) {
8165                        if (DBG) log("Turn on scanning after p2p disconnected");
8166                        sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,
8167                                    ++mPeriodicScanToken, 0), mSupplicantScanIntervalMs);
8168                    } else {
8169                        // If P2P is not connected and there are saved networks, then restart
8170                        // scanning at the normal period. This is necessary because scanning might
8171                        // have been disabled altogether if WIFI_SCAN_INTERVAL_WHEN_P2P_CONNECTED_MS
8172                        // was set to zero.
8173                        startDelayedScan(mDisconnectedScanPeriodMs, null, null);
8174                    }
8175                    break;
8176                case CMD_RECONNECT:
8177                case CMD_REASSOCIATE:
8178                    if (mTemporarilyDisconnectWifi) {
8179                        // Drop a third party reconnect/reassociate if STA is
8180                        // temporarily disconnected for p2p
8181                        break;
8182                    } else {
8183                        // ConnectModeState handles it
8184                        ret = NOT_HANDLED;
8185                    }
8186                    break;
8187                case CMD_SCREEN_STATE_CHANGED:
8188                    handleScreenStateChanged(message.arg1 != 0,
8189                            /* startBackgroundScanIfNeeded = */ true);
8190                    break;
8191                default:
8192                    ret = NOT_HANDLED;
8193            }
8194            return ret;
8195        }
8196
8197        @Override
8198        public void exit() {
8199            /* No need for a background scan upon exit from a disconnected state */
8200            if (mEnableBackgroundScan) {
8201                enableBackgroundScan(false);
8202            }
8203            setScanAlarm(false);
8204        }
8205    }
8206
8207    class WpsRunningState extends State {
8208        // Tracks the source to provide a reply
8209        private Message mSourceMessage;
8210        @Override
8211        public void enter() {
8212            mSourceMessage = Message.obtain(getCurrentMessage());
8213        }
8214        @Override
8215        public boolean processMessage(Message message) {
8216            logStateAndMessage(message, getClass().getSimpleName());
8217
8218            switch (message.what) {
8219                case WifiMonitor.WPS_SUCCESS_EVENT:
8220                    // Ignore intermediate success, wait for full connection
8221                    break;
8222                case WifiMonitor.NETWORK_CONNECTION_EVENT:
8223                    replyToMessage(mSourceMessage, WifiManager.WPS_COMPLETED);
8224                    mSourceMessage.recycle();
8225                    mSourceMessage = null;
8226                    deferMessage(message);
8227                    transitionTo(mDisconnectedState);
8228                    break;
8229                case WifiMonitor.WPS_OVERLAP_EVENT:
8230                    replyToMessage(mSourceMessage, WifiManager.WPS_FAILED,
8231                            WifiManager.WPS_OVERLAP_ERROR);
8232                    mSourceMessage.recycle();
8233                    mSourceMessage = null;
8234                    transitionTo(mDisconnectedState);
8235                    break;
8236                case WifiMonitor.WPS_FAIL_EVENT:
8237                    // Arg1 has the reason for the failure
8238                    if ((message.arg1 != WifiManager.ERROR) || (message.arg2 != 0)) {
8239                        replyToMessage(mSourceMessage, WifiManager.WPS_FAILED, message.arg1);
8240                        mSourceMessage.recycle();
8241                        mSourceMessage = null;
8242                        transitionTo(mDisconnectedState);
8243                    } else {
8244                        if (DBG) log("Ignore unspecified fail event during WPS connection");
8245                    }
8246                    break;
8247                case WifiMonitor.WPS_TIMEOUT_EVENT:
8248                    replyToMessage(mSourceMessage, WifiManager.WPS_FAILED,
8249                            WifiManager.WPS_TIMED_OUT);
8250                    mSourceMessage.recycle();
8251                    mSourceMessage = null;
8252                    transitionTo(mDisconnectedState);
8253                    break;
8254                case WifiManager.START_WPS:
8255                    replyToMessage(message, WifiManager.WPS_FAILED, WifiManager.IN_PROGRESS);
8256                    break;
8257                case WifiManager.CANCEL_WPS:
8258                    if (mWifiNative.cancelWps()) {
8259                        replyToMessage(message, WifiManager.CANCEL_WPS_SUCCEDED);
8260                    } else {
8261                        replyToMessage(message, WifiManager.CANCEL_WPS_FAILED, WifiManager.ERROR);
8262                    }
8263                    transitionTo(mDisconnectedState);
8264                    break;
8265                /**
8266                 * Defer all commands that can cause connections to a different network
8267                 * or put the state machine out of connect mode
8268                 */
8269                case CMD_STOP_DRIVER:
8270                case CMD_SET_OPERATIONAL_MODE:
8271                case WifiManager.CONNECT_NETWORK:
8272                case CMD_ENABLE_NETWORK:
8273                case CMD_RECONNECT:
8274                case CMD_REASSOCIATE:
8275                    deferMessage(message);
8276                    break;
8277                case CMD_AUTO_CONNECT:
8278                case CMD_AUTO_ROAM:
8279                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
8280                    return HANDLED;
8281                case CMD_START_SCAN:
8282                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
8283                    return HANDLED;
8284                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
8285                    if (DBG) log("Network connection lost");
8286                    handleNetworkDisconnect();
8287                    break;
8288                case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
8289                    if (DBG) log("Ignore Assoc reject event during WPS Connection");
8290                    break;
8291                case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
8292                    // Disregard auth failure events during WPS connection. The
8293                    // EAP sequence is retried several times, and there might be
8294                    // failures (especially for wps pin). We will get a WPS_XXX
8295                    // event at the end of the sequence anyway.
8296                    if (DBG) log("Ignore auth failure during WPS connection");
8297                    break;
8298                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
8299                    // Throw away supplicant state changes when WPS is running.
8300                    // We will start getting supplicant state changes once we get
8301                    // a WPS success or failure
8302                    break;
8303                default:
8304                    return NOT_HANDLED;
8305            }
8306            return HANDLED;
8307        }
8308
8309        @Override
8310        public void exit() {
8311            mWifiConfigStore.enableAllNetworks();
8312            mWifiConfigStore.loadConfiguredNetworks();
8313        }
8314    }
8315
8316    class SoftApStartingState extends State {
8317        @Override
8318        public void enter() {
8319            final Message message = getCurrentMessage();
8320            if (message.what == CMD_START_AP) {
8321                final WifiConfiguration config = (WifiConfiguration) message.obj;
8322
8323                if (config == null) {
8324                    mWifiApConfigChannel.sendMessage(CMD_REQUEST_AP_CONFIG);
8325                } else {
8326                    mWifiApConfigChannel.sendMessage(CMD_SET_AP_CONFIG, config);
8327                    startSoftApWithConfig(config);
8328                }
8329            } else {
8330                throw new RuntimeException("Illegal transition to SoftApStartingState: " + message);
8331            }
8332        }
8333        @Override
8334        public boolean processMessage(Message message) {
8335            logStateAndMessage(message, getClass().getSimpleName());
8336
8337            switch(message.what) {
8338                case CMD_START_SUPPLICANT:
8339                case CMD_STOP_SUPPLICANT:
8340                case CMD_START_AP:
8341                case CMD_STOP_AP:
8342                case CMD_START_DRIVER:
8343                case CMD_STOP_DRIVER:
8344                case CMD_SET_OPERATIONAL_MODE:
8345                case CMD_SET_COUNTRY_CODE:
8346                case CMD_SET_FREQUENCY_BAND:
8347                case CMD_START_PACKET_FILTERING:
8348                case CMD_STOP_PACKET_FILTERING:
8349                case CMD_TETHER_STATE_CHANGE:
8350                    deferMessage(message);
8351                    break;
8352                case WifiStateMachine.CMD_RESPONSE_AP_CONFIG:
8353                    WifiConfiguration config = (WifiConfiguration) message.obj;
8354                    if (config != null) {
8355                        startSoftApWithConfig(config);
8356                    } else {
8357                        loge("Softap config is null!");
8358                        sendMessage(CMD_START_AP_FAILURE);
8359                    }
8360                    break;
8361                case CMD_START_AP_SUCCESS:
8362                    setWifiApState(WIFI_AP_STATE_ENABLED);
8363                    transitionTo(mSoftApStartedState);
8364                    break;
8365                case CMD_START_AP_FAILURE:
8366                    setWifiApState(WIFI_AP_STATE_FAILED);
8367                    transitionTo(mInitialState);
8368                    break;
8369                default:
8370                    return NOT_HANDLED;
8371            }
8372            return HANDLED;
8373        }
8374    }
8375
8376    class SoftApStartedState extends State {
8377        @Override
8378        public boolean processMessage(Message message) {
8379            logStateAndMessage(message, getClass().getSimpleName());
8380
8381            switch(message.what) {
8382                case CMD_STOP_AP:
8383                    if (DBG) log("Stopping Soft AP");
8384                    /* We have not tethered at this point, so we just shutdown soft Ap */
8385                    try {
8386                        mNwService.stopAccessPoint(mInterfaceName);
8387                    } catch(Exception e) {
8388                        loge("Exception in stopAccessPoint()");
8389                    }
8390                    setWifiApState(WIFI_AP_STATE_DISABLED);
8391                    transitionTo(mInitialState);
8392                    break;
8393                case CMD_START_AP:
8394                    // Ignore a start on a running access point
8395                    break;
8396                    // Fail client mode operation when soft AP is enabled
8397                case CMD_START_SUPPLICANT:
8398                    loge("Cannot start supplicant with a running soft AP");
8399                    setWifiState(WIFI_STATE_UNKNOWN);
8400                    break;
8401                case CMD_TETHER_STATE_CHANGE:
8402                    TetherStateChange stateChange = (TetherStateChange) message.obj;
8403                    if (startTethering(stateChange.available)) {
8404                        transitionTo(mTetheringState);
8405                    }
8406                    break;
8407                default:
8408                    return NOT_HANDLED;
8409            }
8410            return HANDLED;
8411        }
8412    }
8413
8414    class TetheringState extends State {
8415        @Override
8416        public void enter() {
8417            /* Send ourselves a delayed message to shut down if tethering fails to notify */
8418            sendMessageDelayed(obtainMessage(CMD_TETHER_NOTIFICATION_TIMED_OUT,
8419                    ++mTetherToken, 0), TETHER_NOTIFICATION_TIME_OUT_MSECS);
8420        }
8421        @Override
8422        public boolean processMessage(Message message) {
8423            logStateAndMessage(message, getClass().getSimpleName());
8424
8425            switch(message.what) {
8426                case CMD_TETHER_STATE_CHANGE:
8427                    TetherStateChange stateChange = (TetherStateChange) message.obj;
8428                    if (isWifiTethered(stateChange.active)) {
8429                        transitionTo(mTetheredState);
8430                    }
8431                    return HANDLED;
8432                case CMD_TETHER_NOTIFICATION_TIMED_OUT:
8433                    if (message.arg1 == mTetherToken) {
8434                        loge("Failed to get tether update, shutdown soft access point");
8435                        transitionTo(mSoftApStartedState);
8436                        // Needs to be first thing handled
8437                        sendMessageAtFrontOfQueue(CMD_STOP_AP);
8438                    }
8439                    break;
8440                case CMD_START_SUPPLICANT:
8441                case CMD_STOP_SUPPLICANT:
8442                case CMD_START_AP:
8443                case CMD_STOP_AP:
8444                case CMD_START_DRIVER:
8445                case CMD_STOP_DRIVER:
8446                case CMD_SET_OPERATIONAL_MODE:
8447                case CMD_SET_COUNTRY_CODE:
8448                case CMD_SET_FREQUENCY_BAND:
8449                case CMD_START_PACKET_FILTERING:
8450                case CMD_STOP_PACKET_FILTERING:
8451                    deferMessage(message);
8452                    break;
8453                default:
8454                    return NOT_HANDLED;
8455            }
8456            return HANDLED;
8457        }
8458    }
8459
8460    class TetheredState extends State {
8461        @Override
8462        public boolean processMessage(Message message) {
8463            logStateAndMessage(message, getClass().getSimpleName());
8464
8465            switch(message.what) {
8466                case CMD_TETHER_STATE_CHANGE:
8467                    TetherStateChange stateChange = (TetherStateChange) message.obj;
8468                    if (!isWifiTethered(stateChange.active)) {
8469                        loge("Tethering reports wifi as untethered!, shut down soft Ap");
8470                        setHostApRunning(null, false);
8471                        setHostApRunning(null, true);
8472                    }
8473                    return HANDLED;
8474                case CMD_STOP_AP:
8475                    if (DBG) log("Untethering before stopping AP");
8476                    setWifiApState(WIFI_AP_STATE_DISABLING);
8477                    stopTethering();
8478                    transitionTo(mUntetheringState);
8479                    // More work to do after untethering
8480                    deferMessage(message);
8481                    break;
8482                default:
8483                    return NOT_HANDLED;
8484            }
8485            return HANDLED;
8486        }
8487    }
8488
8489    class UntetheringState extends State {
8490        @Override
8491        public void enter() {
8492            /* Send ourselves a delayed message to shut down if tethering fails to notify */
8493            sendMessageDelayed(obtainMessage(CMD_TETHER_NOTIFICATION_TIMED_OUT,
8494                    ++mTetherToken, 0), TETHER_NOTIFICATION_TIME_OUT_MSECS);
8495
8496        }
8497        @Override
8498        public boolean processMessage(Message message) {
8499            logStateAndMessage(message, getClass().getSimpleName());
8500
8501            switch(message.what) {
8502                case CMD_TETHER_STATE_CHANGE:
8503                    TetherStateChange stateChange = (TetherStateChange) message.obj;
8504
8505                    /* Wait till wifi is untethered */
8506                    if (isWifiTethered(stateChange.active)) break;
8507
8508                    transitionTo(mSoftApStartedState);
8509                    break;
8510                case CMD_TETHER_NOTIFICATION_TIMED_OUT:
8511                    if (message.arg1 == mTetherToken) {
8512                        loge("Failed to get tether update, force stop access point");
8513                        transitionTo(mSoftApStartedState);
8514                    }
8515                    break;
8516                case CMD_START_SUPPLICANT:
8517                case CMD_STOP_SUPPLICANT:
8518                case CMD_START_AP:
8519                case CMD_STOP_AP:
8520                case CMD_START_DRIVER:
8521                case CMD_STOP_DRIVER:
8522                case CMD_SET_OPERATIONAL_MODE:
8523                case CMD_SET_COUNTRY_CODE:
8524                case CMD_SET_FREQUENCY_BAND:
8525                case CMD_START_PACKET_FILTERING:
8526                case CMD_STOP_PACKET_FILTERING:
8527                    deferMessage(message);
8528                    break;
8529                default:
8530                    return NOT_HANDLED;
8531            }
8532            return HANDLED;
8533        }
8534    }
8535
8536    //State machine initiated requests can have replyTo set to null indicating
8537    //there are no recepients, we ignore those reply actions
8538    private void replyToMessage(Message msg, int what) {
8539        if (msg.replyTo == null) return;
8540        Message dstMsg = obtainMessageWithArg2(msg);
8541        dstMsg.what = what;
8542        mReplyChannel.replyToMessage(msg, dstMsg);
8543    }
8544
8545    private void replyToMessage(Message msg, int what, int arg1) {
8546        if (msg.replyTo == null) return;
8547        Message dstMsg = obtainMessageWithArg2(msg);
8548        dstMsg.what = what;
8549        dstMsg.arg1 = arg1;
8550        mReplyChannel.replyToMessage(msg, dstMsg);
8551    }
8552
8553    private void replyToMessage(Message msg, int what, Object obj) {
8554        if (msg.replyTo == null) return;
8555        Message dstMsg = obtainMessageWithArg2(msg);
8556        dstMsg.what = what;
8557        dstMsg.obj = obj;
8558        mReplyChannel.replyToMessage(msg, dstMsg);
8559    }
8560
8561    /**
8562     * arg2 on the source message has a unique id that needs to be retained in replies
8563     * to match the request
8564
8565     * see WifiManager for details
8566     */
8567    private Message obtainMessageWithArg2(Message srcMsg) {
8568        Message msg = Message.obtain();
8569        msg.arg2 = srcMsg.arg2;
8570        return msg;
8571    }
8572
8573    private static int parseHex(char ch) {
8574        if ('0' <= ch && ch <= '9') {
8575            return ch - '0';
8576        } else if ('a' <= ch && ch <= 'f') {
8577            return ch - 'a' + 10;
8578        } else if ('A' <= ch && ch <= 'F') {
8579            return ch - 'A' + 10;
8580        } else {
8581            throw new NumberFormatException("" + ch + " is not a valid hex digit");
8582        }
8583    }
8584
8585    private byte[] parseHex(String hex) {
8586        /* This only works for good input; don't throw bad data at it */
8587        if (hex == null) {
8588            return new byte[0];
8589        }
8590
8591        if (hex.length() % 2 != 0) {
8592            throw new NumberFormatException(hex + " is not a valid hex string");
8593        }
8594
8595        byte[] result = new byte[(hex.length())/2 + 1];
8596        result[0] = (byte) ((hex.length())/2);
8597        for (int i = 0, j = 1; i < hex.length(); i += 2, j++) {
8598            int val = parseHex(hex.charAt(i)) * 16 + parseHex(hex.charAt(i+1));
8599            byte b = (byte) (val & 0xFF);
8600            result[j] = b;
8601        }
8602
8603        return result;
8604    }
8605
8606    private static String makeHex(byte[] bytes) {
8607        StringBuilder sb = new StringBuilder();
8608        for (byte b : bytes) {
8609            sb.append(String.format("%02x", b));
8610        }
8611        return sb.toString();
8612    }
8613
8614    private static String makeHex(byte[] bytes, int from, int len) {
8615        StringBuilder sb = new StringBuilder();
8616        for (int i = 0; i < len; i++) {
8617            sb.append(String.format("%02x", bytes[from+i]));
8618        }
8619        return sb.toString();
8620    }
8621
8622
8623    private static byte[] concat(byte[] array1, byte[] array2, byte[] array3) {
8624
8625        int len = array1.length + array2.length + array3.length;
8626
8627        if (array1.length != 0) {
8628            len++;                      /* add another byte for size */
8629        }
8630
8631        if (array2.length != 0) {
8632            len++;                      /* add another byte for size */
8633        }
8634
8635        if (array3.length != 0) {
8636            len++;                      /* add another byte for size */
8637        }
8638
8639        byte[] result = new byte[len];
8640
8641        int index = 0;
8642        if (array1.length != 0) {
8643            result[index] = (byte) (array1.length & 0xFF);
8644            index++;
8645            for (byte b : array1) {
8646                result[index] = b;
8647                index++;
8648            }
8649        }
8650
8651        if (array2.length != 0) {
8652            result[index] = (byte) (array2.length & 0xFF);
8653            index++;
8654            for (byte b : array2) {
8655                result[index] = b;
8656                index++;
8657            }
8658        }
8659
8660        if (array3.length != 0) {
8661            result[index] = (byte) (array3.length & 0xFF);
8662            index++;
8663            for (byte b : array3) {
8664                result[index] = b;
8665                index++;
8666            }
8667        }
8668        return result;
8669    }
8670
8671    void handleGsmAuthRequest(SimAuthRequestData requestData) {
8672        if (targetWificonfiguration == null
8673                || targetWificonfiguration.networkId == requestData.networkId) {
8674            logd("id matches targetWifiConfiguration");
8675        } else {
8676            logd("id does not match targetWifiConfiguration");
8677            return;
8678        }
8679
8680        TelephonyManager tm = (TelephonyManager)
8681                mContext.getSystemService(Context.TELEPHONY_SERVICE);
8682
8683        if (tm != null) {
8684            StringBuilder sb = new StringBuilder();
8685            for (String challenge : requestData.challenges) {
8686
8687                logd("RAND = " + challenge);
8688
8689                byte[] rand = null;
8690                try {
8691                    rand = parseHex(challenge);
8692                } catch (NumberFormatException e) {
8693                    loge("malformed challenge");
8694                    continue;
8695                }
8696
8697                String base64Challenge = android.util.Base64.encodeToString(
8698                        rand, android.util.Base64.NO_WRAP);
8699                /*
8700                 * appType = 1 => SIM, 2 => USIM according to
8701                 * com.android.internal.telephony.PhoneConstants#APPTYPE_xxx
8702                 */
8703                int appType = 2;
8704                String tmResponse = tm.getIccSimChallengeResponse(appType, base64Challenge);
8705                logv("Raw Response - " + tmResponse);
8706
8707                if (tmResponse != null && tmResponse.length() > 4) {
8708                    byte[] result = android.util.Base64.decode(tmResponse,
8709                            android.util.Base64.DEFAULT);
8710                    logv("Hex Response -" + makeHex(result));
8711                    int sres_len = result[0];
8712                    String sres = makeHex(result, 1, sres_len);
8713                    int kc_offset = 1+sres_len;
8714                    int kc_len = result[kc_offset];
8715                    String kc = makeHex(result, 1+kc_offset, kc_len);
8716                    sb.append(":" + kc + ":" + sres);
8717                    logv("kc:" + kc + " sres:" + sres);
8718                } else {
8719                    loge("bad response - " + tmResponse);
8720                }
8721            }
8722
8723            String response = sb.toString();
8724            logv("Supplicant Response -" + response);
8725            mWifiNative.simAuthResponse(requestData.networkId, response);
8726        } else {
8727            loge("could not get telephony manager");
8728        }
8729    }
8730
8731    void handle3GAuthRequest(SimAuthRequestData requestData) {
8732
8733    }
8734}
8735