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