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