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