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