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