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