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