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