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