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