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