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