WifiStateMachine.java revision badd604178e757a65dcff91ab1ff818e3527c8e3
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            mNetworkAgent.sendNetworkScore(score);
2948        }
2949    }
2950
2951    public double getTxPacketRate() {
2952        if (mWifiInfo != null) {
2953            return mWifiInfo.txSuccessRate;
2954        }
2955        return -1;
2956    }
2957
2958    public double getRxPacketRate() {
2959        if (mWifiInfo != null) {
2960            return mWifiInfo.rxSuccessRate;
2961        }
2962        return -1;
2963    }
2964
2965    /**
2966     * Fetch TX packet counters on current connection
2967     */
2968    private void fetchPktcntNative(RssiPacketCountInfo info) {
2969        String pktcntPoll = mWifiNative.pktcntPoll();
2970
2971        if (pktcntPoll != null) {
2972            String[] lines = pktcntPoll.split("\n");
2973            for (String line : lines) {
2974                String[] prop = line.split("=");
2975                if (prop.length < 2) continue;
2976                try {
2977                    if (prop[0].equals("TXGOOD")) {
2978                        info.txgood = Integer.parseInt(prop[1]);
2979                    } else if (prop[0].equals("TXBAD")) {
2980                        info.txbad = Integer.parseInt(prop[1]);
2981                    }
2982                } catch (NumberFormatException e) {
2983                    // Ignore
2984                }
2985            }
2986        }
2987    }
2988
2989    private boolean isProvisioned(LinkProperties lp) {
2990        // LinkProperties#isProvisioned returns true even if all we have is an IPv4 address and no
2991        // connectivity. This turns out not to be very useful, because we can't distinguish it from
2992        // a state where we have an IPv4 address assigned to the interface but are still running
2993        // DHCP.
2994        // TODO: Fix LinkProperties and remove this function.
2995        if (mWifiConfigStore.isUsingStaticIp(mLastNetworkId)) {
2996            return lp.hasIPv4Address();
2997        } else {
2998            return (lp.hasIPv4Address() && lp.hasIPv4DefaultRoute() && lp.hasIPv4DnsServer()) ||
2999                   (lp.hasGlobalIPv6Address() && lp.hasIPv6DefaultRoute() && lp.hasIPv6DnsServer());
3000        }
3001    }
3002
3003    /**
3004     * Updates mLinkProperties by merging information from various sources.
3005     *
3006     * This is needed because the information in mLinkProperties comes from multiple sources (DHCP,
3007     * netlink, static configuration, ...). When one of these sources of information has updated
3008     * link properties, we can't just assign them to mLinkProperties or we'd lose track of the
3009     * information that came from other sources. Instead, when one of those sources has new
3010     * information, we update the object that tracks the information from that source and then
3011     * call this method to apply the change to mLinkProperties.
3012     *
3013     * The information in mLinkProperties is currently obtained as follows:
3014     * - Interface name: set in the constructor.
3015     * - IPv4 and IPv6 addresses: netlink, passed in by mNetlinkTracker.
3016     * - IPv4 routes, DNS servers, and domains: DHCP.
3017     * - IPv6 routes and DNS servers: netlink, passed in by mNetlinkTracker.
3018     * - HTTP proxy: the wifi config store.
3019     */
3020    private void updateLinkProperties(int reason) {
3021        LinkProperties newLp = new LinkProperties();
3022
3023        // Interface name and proxy are locally configured.
3024        newLp.setInterfaceName(mInterfaceName);
3025        newLp.setHttpProxy(mWifiConfigStore.getProxyProperties(mLastNetworkId));
3026
3027        // IPv4/v6 addresses, IPv6 routes and IPv6 DNS servers come from netlink.
3028        LinkProperties netlinkLinkProperties = mNetlinkTracker.getLinkProperties();
3029        newLp.setLinkAddresses(netlinkLinkProperties.getLinkAddresses());
3030        for (RouteInfo route : netlinkLinkProperties.getRoutes()) {
3031            newLp.addRoute(route);
3032        }
3033        for (InetAddress dns : netlinkLinkProperties.getDnsServers()) {
3034            newLp.addDnsServer(dns);
3035        }
3036
3037        // IPv4 routes, DNS servers and domains come from mDhcpResults.
3038        synchronized (mDhcpResultsLock) {
3039            // Even when we're using static configuration, we don't need to look at the config
3040            // store, because static IP configuration also populates mDhcpResults.
3041            if ((mDhcpResults != null) && (mDhcpResults.linkProperties != null)) {
3042                LinkProperties lp = mDhcpResults.linkProperties;
3043                for (RouteInfo route : lp.getRoutes()) {
3044                    newLp.addRoute(route);
3045                }
3046                for (InetAddress dns : lp.getDnsServers()) {
3047                    newLp.addDnsServer(dns);
3048                }
3049                newLp.setDomains(lp.getDomains());
3050            }
3051        }
3052
3053        final boolean linkChanged = !newLp.equals(mLinkProperties);
3054        final boolean wasProvisioned = isProvisioned(mLinkProperties);
3055        final boolean isProvisioned = isProvisioned(newLp);
3056        final DetailedState detailedState = getNetworkDetailedState();
3057
3058        if (linkChanged) {
3059            if (DBG) {
3060                log("Link configuration changed for netId: " + mLastNetworkId
3061                        + " old: " + mLinkProperties + " new: " + newLp);
3062            }
3063            mLinkProperties = newLp;
3064            if (mNetworkAgent != null) mNetworkAgent.sendLinkProperties(mLinkProperties);
3065        }
3066
3067        if (DBG) {
3068            StringBuilder sb = new StringBuilder();
3069            sb.append("updateLinkProperties nid: " + mLastNetworkId);
3070            sb.append(" state: " + detailedState);
3071            sb.append(" reason: " + smToString(reason));
3072
3073            if (mLinkProperties != null) {
3074                if (mLinkProperties.hasIPv4Address()) {
3075                    sb.append(" v4");
3076                }
3077                if (mLinkProperties.hasGlobalIPv6Address()) {
3078                    sb.append(" v6");
3079                }
3080                if (mLinkProperties.hasIPv4DefaultRoute()) {
3081                    sb.append(" v4r");
3082                }
3083                if (mLinkProperties.hasIPv6DefaultRoute()) {
3084                    sb.append(" v6r");
3085                }
3086                if (mLinkProperties.hasIPv4DnsServer()) {
3087                    sb.append(" v4dns");
3088                }
3089                if (mLinkProperties.hasIPv6DnsServer()) {
3090                    sb.append(" v6dns");
3091                }
3092                if (isProvisioned) {
3093                    sb.append(" isprov");
3094                }
3095            }
3096            loge(sb.toString());
3097        }
3098
3099        // If we just configured or lost IP configuration, do the needful.
3100        // We don't just call handleSuccessfulIpConfiguration() or handleIpConfigurationLost()
3101        // here because those should only be called if we're attempting to connect or already
3102        // connected, whereas updateLinkProperties can be called at any time.
3103        switch (reason) {
3104            case DhcpStateMachine.DHCP_SUCCESS:
3105            case CMD_STATIC_IP_SUCCESS:
3106                // IPv4 provisioning succeded. Advance to connected state.
3107                sendMessage(CMD_IP_CONFIGURATION_SUCCESSFUL);
3108                if (!isProvisioned) {
3109                    // Can never happen unless DHCP reports success but isProvisioned thinks the
3110                    // resulting configuration is invalid (e.g., no IPv4 address, or the state in
3111                    // mLinkProperties is out of sync with reality, or there's a bug in this code).
3112                    // TODO: disconnect here instead. If our configuration is not usable, there's no
3113                    // point in staying connected, and if mLinkProperties is out of sync with
3114                    // reality, that will cause problems in the future.
3115                    loge("IPv4 config succeeded, but not provisioned");
3116                }
3117                break;
3118
3119            case DhcpStateMachine.DHCP_FAILURE:
3120                // DHCP failed. If we're not already provisioned, give up and disconnect.
3121                // If we're already provisioned (e.g., IPv6-only network), stay connected.
3122                if (!isProvisioned) {
3123                    sendMessage(CMD_IP_CONFIGURATION_LOST);
3124                } else {
3125                    // DHCP failed, but we're provisioned (e.g., if we're on an IPv6-only network).
3126                    sendMessage(CMD_IP_CONFIGURATION_SUCCESSFUL);
3127
3128                    // To be sure we don't get stuck with a non-working network if all we had is
3129                    // IPv4, remove the IPv4 address from the interface (since we're using DHCP,
3130                    // and DHCP failed). If we had an IPv4 address before, the deletion of the
3131                    // address  will cause a CMD_UPDATE_LINKPROPERTIES. If the IPv4 address was
3132                    // necessary for provisioning, its deletion will cause us to disconnect.
3133                    //
3134                    // This should never happen, because a DHCP failure will have empty DhcpResults
3135                    // and thus empty LinkProperties, and isProvisioned will not return true if
3136                    // we're using DHCP and don't have an IPv4 default route. So for now it's only
3137                    // here for extra redundancy. However, it will increase robustness if we move
3138                    // to getting IPv4 routes from netlink as well.
3139                    loge("DHCP failure: provisioned, should not happen! Clearing IPv4 address.");
3140                    try {
3141                        InterfaceConfiguration ifcg = new InterfaceConfiguration();
3142                        ifcg.setLinkAddress(new LinkAddress("0.0.0.0/0"));
3143                        ifcg.setInterfaceUp();
3144                        mNwService.setInterfaceConfig(mInterfaceName, ifcg);
3145                    } catch (RemoteException e) {
3146                        sendMessage(CMD_IP_CONFIGURATION_LOST);
3147                    }
3148                }
3149                break;
3150
3151            case CMD_STATIC_IP_FAILURE:
3152                // Static configuration was invalid, or an error occurred in applying it. Give up.
3153                sendMessage(CMD_IP_CONFIGURATION_LOST);
3154                break;
3155
3156            case CMD_UPDATE_LINKPROPERTIES:
3157                // IP addresses, DNS servers, etc. changed. Act accordingly.
3158                if (wasProvisioned && !isProvisioned) {
3159                    // We no longer have a usable network configuration. Disconnect.
3160                    sendMessage(CMD_IP_CONFIGURATION_LOST);
3161                } else if (!wasProvisioned && isProvisioned) {
3162                    // We have a usable IPv6-only config. Advance to connected state.
3163                    sendMessage(CMD_IP_CONFIGURATION_SUCCESSFUL);
3164                }
3165                if (linkChanged && getNetworkDetailedState() == DetailedState.CONNECTED) {
3166                    // If anything has changed and we're already connected, send out a notification.
3167                    sendLinkConfigurationChangedBroadcast();
3168                }
3169                break;
3170        }
3171    }
3172
3173    /**
3174     * Clears all our link properties.
3175     */
3176     private void clearLinkProperties() {
3177         // If the network used DHCP, clear the LinkProperties we stored in the config store.
3178         if (!mWifiConfigStore.isUsingStaticIp(mLastNetworkId)) {
3179             mWifiConfigStore.clearLinkProperties(mLastNetworkId);
3180         }
3181
3182         // Clear the link properties obtained from DHCP and netlink.
3183         synchronized (mDhcpResultsLock) {
3184             if (mDhcpResults != null && mDhcpResults.linkProperties != null) {
3185                 mDhcpResults.linkProperties.clear();
3186             }
3187         }
3188         mNetlinkTracker.clearLinkProperties();
3189
3190         // Now clear the merged link properties.
3191         mLinkProperties.clear();
3192         if (mNetworkAgent != null) mNetworkAgent.sendLinkProperties(mLinkProperties);
3193     }
3194
3195     /**
3196      * try to update default route MAC address.
3197      */
3198      private String updateDefaultRouteMacAddress(int timeout) {
3199          String address = null;
3200          for (RouteInfo route : mLinkProperties.getRoutes()) {
3201              if (route.isDefaultRoute() && route.hasGateway()) {
3202                  InetAddress gateway = route.getGateway();
3203                  if (gateway instanceof Inet4Address) {
3204                      if (PDBG) {
3205                          loge("updateDefaultRouteMacAddress found Ipv4 default :"
3206                                  + gateway.getHostAddress());
3207                      }
3208                      address = macAddressFromRoute(gateway.getHostAddress());
3209                     /* The gateway's MAC address is known */
3210                      if ((address == null) && (timeout > 0)) {
3211                          boolean reachable = false;
3212                          try {
3213                              reachable = gateway.isReachable(timeout);
3214                          } catch (Exception e) {
3215                              loge("updateDefaultRouteMacAddress exception reaching :"
3216                                      + gateway.getHostAddress());
3217
3218                          } finally {
3219                              if (reachable == true) {
3220
3221                                  address = macAddressFromRoute(gateway.getHostAddress());
3222                                  if (PDBG) {
3223                                      loge("updateDefaultRouteMacAddress reachable (tried again) :"
3224                                              + gateway.getHostAddress() + " found " + address);
3225                                  }
3226                              }
3227                          }
3228                      }
3229                      if (address != null) {
3230                          mWifiConfigStore.setLinkProperties(mLastNetworkId,
3231                                  new LinkProperties(mLinkProperties));
3232                          mWifiConfigStore.setDefaultGwMacAddress(mLastNetworkId, address);
3233
3234                      }
3235                  }
3236              }
3237          }
3238          return address;
3239      }
3240
3241    private void sendScanResultsAvailableBroadcast() {
3242        noteScanEnd();
3243        Intent intent = new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
3244        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
3245        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
3246    }
3247
3248    private void sendRssiChangeBroadcast(final int newRssi) {
3249        try {
3250            mBatteryStats.noteWifiRssiChanged(newRssi);
3251        } catch (RemoteException e) {
3252            // Won't happen.
3253        }
3254        Intent intent = new Intent(WifiManager.RSSI_CHANGED_ACTION);
3255        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
3256        intent.putExtra(WifiManager.EXTRA_NEW_RSSI, newRssi);
3257        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
3258    }
3259
3260    private void sendNetworkStateChangeBroadcast(String bssid) {
3261        Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
3262        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
3263        intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, new NetworkInfo(mNetworkInfo));
3264        intent.putExtra(WifiManager.EXTRA_LINK_PROPERTIES, new LinkProperties (mLinkProperties));
3265        if (bssid != null)
3266            intent.putExtra(WifiManager.EXTRA_BSSID, bssid);
3267        if (mNetworkInfo.getDetailedState() == DetailedState.VERIFYING_POOR_LINK ||
3268                mNetworkInfo.getDetailedState() == DetailedState.CONNECTED) {
3269            intent.putExtra(WifiManager.EXTRA_WIFI_INFO, new WifiInfo(mWifiInfo));
3270        }
3271        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
3272    }
3273
3274    private void sendLinkConfigurationChangedBroadcast() {
3275        Intent intent = new Intent(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION);
3276        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
3277        intent.putExtra(WifiManager.EXTRA_LINK_PROPERTIES, new LinkProperties(mLinkProperties));
3278        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
3279    }
3280
3281    private void sendSupplicantConnectionChangedBroadcast(boolean connected) {
3282        Intent intent = new Intent(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
3283        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
3284        intent.putExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, connected);
3285        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
3286    }
3287
3288    /**
3289     * Record the detailed state of a network.
3290     * @param state the new {@code DetailedState}
3291     */
3292    private void setNetworkDetailedState(NetworkInfo.DetailedState state) {
3293        if (DBG) {
3294            log("setDetailed state, old ="
3295                    + mNetworkInfo.getDetailedState() + " and new state=" + state);
3296        }
3297
3298        if (state != mNetworkInfo.getDetailedState()) {
3299            mNetworkInfo.setDetailedState(state, null, mWifiInfo.getSSID());
3300            if (mNetworkAgent != null) {
3301                if (!isRoaming() ||
3302                        (state != DetailedState.DISCONNECTED && state != DetailedState.DISCONNECTING) ) {
3303                    // Don't tell the Network agent that we are doing a disconnect-roam
3304                    mNetworkAgent.sendNetworkInfo(mNetworkInfo);
3305                }
3306            }
3307        }
3308    }
3309
3310    private DetailedState getNetworkDetailedState() {
3311        return mNetworkInfo.getDetailedState();
3312    }
3313
3314
3315    private SupplicantState handleSupplicantStateChange(Message message) {
3316        StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
3317        SupplicantState state = stateChangeResult.state;
3318        // Supplicant state change
3319        // [31-13] Reserved for future use
3320        // [8 - 0] Supplicant state (as defined in SupplicantState.java)
3321        // 50023 supplicant_state_changed (custom|1|5)
3322        mWifiInfo.setSupplicantState(state);
3323        // Network id is only valid when we start connecting
3324        if (SupplicantState.isConnecting(state)) {
3325            mWifiInfo.setNetworkId(stateChangeResult.networkId);
3326        } else {
3327            mWifiInfo.setNetworkId(WifiConfiguration.INVALID_NETWORK_ID);
3328        }
3329
3330        mWifiInfo.setBSSID(stateChangeResult.BSSID);
3331        mWifiInfo.setSSID(stateChangeResult.wifiSsid);
3332
3333        mSupplicantStateTracker.sendMessage(Message.obtain(message));
3334
3335        return state;
3336    }
3337
3338    /**
3339     * Resets the Wi-Fi Connections by clearing any state, resetting any sockets
3340     * using the interface, stopping DHCP & disabling interface
3341     */
3342    private void handleNetworkDisconnect() {
3343        if (DBG) log("handleNetworkDisconnect: Stopping DHCP and clearing IP"
3344                + " stack:" + Thread.currentThread().getStackTrace()[2].getMethodName()
3345                +" - "+ Thread.currentThread().getStackTrace()[3].getMethodName()
3346                +" - "+ Thread.currentThread().getStackTrace()[4].getMethodName()
3347                +" - "+ Thread.currentThread().getStackTrace()[5].getMethodName());
3348
3349        stopDhcp();
3350
3351        try {
3352            mNwService.clearInterfaceAddresses(mInterfaceName);
3353            mNwService.disableIpv6(mInterfaceName);
3354        } catch (Exception e) {
3355            loge("Failed to clear addresses or disable ipv6" + e);
3356        }
3357
3358        /* Reset data structures */
3359        mWifiInfo.reset();
3360
3361        setNetworkDetailedState(DetailedState.DISCONNECTED);
3362        if (mNetworkAgent != null) {
3363            mNetworkAgent.sendNetworkInfo(mNetworkInfo);
3364            mNetworkAgent = null;
3365        }
3366        mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.DISCONNECTED);
3367
3368        /* Clear network properties */
3369        clearLinkProperties();
3370
3371        /* Cend event to CM & network change broadcast */
3372        sendNetworkStateChangeBroadcast(mLastBssid);
3373
3374        /* Cancel auto roam requests */
3375        autoRoamSetBSSID(mLastNetworkId, "any");
3376
3377        /* Reset roaming parameters */
3378        mAutoRoaming = WifiAutoJoinController.AUTO_JOIN_IDLE;
3379        fullBandConnectedTimeIntervalMilli = 20 * 1000; // Start scans at 20 seconds interval
3380
3381        mLastBssid= null;
3382        registerDisconnected();
3383        mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
3384    }
3385
3386    private void handleSupplicantConnectionLoss() {
3387        /* Socket connection can be lost when we do a graceful shutdown
3388        * or when the driver is hung. Ensure supplicant is stopped here.
3389        */
3390        mWifiMonitor.killSupplicant(mP2pSupported);
3391        mWifiNative.closeSupplicantConnection();
3392        sendSupplicantConnectionChangedBroadcast(false);
3393        setWifiState(WIFI_STATE_DISABLED);
3394    }
3395
3396    void handlePreDhcpSetup() {
3397        mDhcpActive = true;
3398        if (!mBluetoothConnectionActive) {
3399            /*
3400             * There are problems setting the Wi-Fi driver's power
3401             * mode to active when bluetooth coexistence mode is
3402             * enabled or sense.
3403             * <p>
3404             * We set Wi-Fi to active mode when
3405             * obtaining an IP address because we've found
3406             * compatibility issues with some routers with low power
3407             * mode.
3408             * <p>
3409             * In order for this active power mode to properly be set,
3410             * we disable coexistence mode until we're done with
3411             * obtaining an IP address.  One exception is if we
3412             * are currently connected to a headset, since disabling
3413             * coexistence would interrupt that connection.
3414             */
3415            // Disable the coexistence mode
3416            mWifiNative.setBluetoothCoexistenceMode(
3417                    mWifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED);
3418        }
3419
3420        // Disable power save and suspend optimizations during DHCP
3421        // Note: The order here is important for now. Brcm driver changes
3422        // power settings when we control suspend mode optimizations.
3423        // TODO: Remove this comment when the driver is fixed.
3424        setSuspendOptimizationsNative(SUSPEND_DUE_TO_DHCP, false);
3425        mWifiNative.setPowerSave(false);
3426
3427        stopBatchedScan();
3428        WifiNative.pauseScan();
3429
3430        /* P2p discovery breaks dhcp, shut it down in order to get through this */
3431        Message msg = new Message();
3432        msg.what = WifiP2pServiceImpl.BLOCK_DISCOVERY;
3433        msg.arg1 = WifiP2pServiceImpl.ENABLED;
3434        msg.arg2 = DhcpStateMachine.CMD_PRE_DHCP_ACTION_COMPLETE;
3435        msg.obj = mDhcpStateMachine;
3436        mWifiP2pChannel.sendMessage(msg);
3437    }
3438
3439
3440    void startDhcp() {
3441        if (mDhcpStateMachine == null) {
3442            mDhcpStateMachine = DhcpStateMachine.makeDhcpStateMachine(
3443                    mContext, WifiStateMachine.this, mInterfaceName);
3444
3445        }
3446        mDhcpStateMachine.registerForPreDhcpNotification();
3447        mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_START_DHCP);
3448    }
3449
3450    void renewDhcp() {
3451        if (mDhcpStateMachine == null) {
3452            mDhcpStateMachine = DhcpStateMachine.makeDhcpStateMachine(
3453                    mContext, WifiStateMachine.this, mInterfaceName);
3454
3455        }
3456        mDhcpStateMachine.registerForPreDhcpNotification();
3457        mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_RENEW_DHCP);
3458    }
3459
3460    void stopDhcp() {
3461        if (mDhcpStateMachine != null) {
3462            /* In case we were in middle of DHCP operation restore back powermode */
3463            handlePostDhcpSetup();
3464            mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_STOP_DHCP);
3465        }
3466    }
3467
3468    void handlePostDhcpSetup() {
3469        /* Restore power save and suspend optimizations */
3470        setSuspendOptimizationsNative(SUSPEND_DUE_TO_DHCP, true);
3471        mWifiNative.setPowerSave(true);
3472
3473        mWifiP2pChannel.sendMessage(WifiP2pServiceImpl.BLOCK_DISCOVERY, WifiP2pServiceImpl.DISABLED);
3474
3475        // Set the coexistence mode back to its default value
3476        mWifiNative.setBluetoothCoexistenceMode(
3477                mWifiNative.BLUETOOTH_COEXISTENCE_MODE_SENSE);
3478
3479        mDhcpActive = false;
3480
3481        startBatchedScan();
3482        WifiNative.restartScan();
3483    }
3484
3485    private void handleIPv4Success(DhcpResults dhcpResults, int reason) {
3486
3487        if (PDBG) {
3488            loge("wifistatemachine handleIPv4Success <" + dhcpResults.toString()
3489                    + "> linkaddress num " + dhcpResults.linkProperties.getLinkAddresses().size());
3490            for (LinkAddress linkAddress : dhcpResults.linkProperties.getLinkAddresses()) {
3491                loge("link address " + linkAddress.toString());
3492            }
3493        }
3494
3495        synchronized (mDhcpResultsLock) {
3496            mDhcpResults = dhcpResults;
3497        }
3498        LinkProperties linkProperties = dhcpResults.linkProperties;
3499        mWifiConfigStore.setLinkProperties(mLastNetworkId, new LinkProperties(linkProperties));
3500        InetAddress addr = null;
3501        Iterator<InetAddress> addrs = linkProperties.getAddresses().iterator();
3502        if (addrs.hasNext()) {
3503            addr = addrs.next();
3504        }
3505
3506        if (isRoaming()) {
3507            if (addr instanceof Inet4Address) {
3508                int previousAddress = mWifiInfo.getIpAddress();
3509                int newAddress = NetworkUtils.inetAddressToInt((Inet4Address)addr);
3510                if (previousAddress != newAddress) {
3511                    loge("handleIPv4Success, roaming and address changed" +
3512                            mWifiInfo + " got: " + addr);
3513                } else {
3514
3515                }
3516            } else {
3517                loge("handleIPv4Success, roaming and didnt get an IPv4 address" +
3518                        addr.toString());
3519
3520
3521            }
3522        }
3523        mWifiInfo.setInetAddress(addr);
3524        mWifiInfo.setMeteredHint(dhcpResults.hasMeteredHint());
3525        updateLinkProperties(reason);
3526    }
3527
3528    private void handleSuccessfulIpConfiguration() {
3529        mLastSignalLevel = -1; // Force update of signal strength
3530        WifiConfiguration c = getCurrentWifiConfiguration();
3531        // Reset IP failure tracking
3532        if (c != null) {
3533            c.numConnectionFailures = 0;
3534        }
3535    }
3536
3537    private void handleIPv4Failure(int reason) {
3538        synchronized(mDhcpResultsLock) {
3539             if (mDhcpResults != null && mDhcpResults.linkProperties != null) {
3540                 mDhcpResults.linkProperties.clear();
3541             }
3542        }
3543        if (PDBG) {
3544            loge("wifistatemachine handleIPv4Failure");
3545        }
3546        updateLinkProperties(reason);
3547    }
3548
3549    private void handleIpConfigurationLost() {
3550        mWifiInfo.setInetAddress(null);
3551        mWifiInfo.setMeteredHint(false);
3552
3553        mWifiConfigStore.handleSSIDStateChange(mLastNetworkId, false, "DHCP FAILURE");
3554
3555        /* DHCP times out after about 30 seconds, we do a
3556         * disconnect thru supplicant, we will let autojoin retry connecting to the network
3557         */
3558        mWifiNative.disconnect();
3559    }
3560
3561    /* Current design is to not set the config on a running hostapd but instead
3562     * stop and start tethering when user changes config on a running access point
3563     *
3564     * TODO: Add control channel setup through hostapd that allows changing config
3565     * on a running daemon
3566     */
3567    private void startSoftApWithConfig(final WifiConfiguration config) {
3568        // Start hostapd on a separate thread
3569        new Thread(new Runnable() {
3570            public void run() {
3571                try {
3572                    mNwService.startAccessPoint(config, mInterfaceName);
3573                } catch (Exception e) {
3574                    loge("Exception in softap start " + e);
3575                    try {
3576                        mNwService.stopAccessPoint(mInterfaceName);
3577                        mNwService.startAccessPoint(config, mInterfaceName);
3578                    } catch (Exception e1) {
3579                        loge("Exception in softap re-start " + e1);
3580                        sendMessage(CMD_START_AP_FAILURE);
3581                        return;
3582                    }
3583                }
3584                if (DBG) log("Soft AP start successful");
3585                sendMessage(CMD_START_AP_SUCCESS);
3586            }
3587        }).start();
3588    }
3589
3590
3591    /*
3592     * Read a MAC address in /proc/arp/table, used by WifistateMachine
3593     * so as to record MAC address of default gateway.
3594     **/
3595    private String macAddressFromRoute(String ipAddress) {
3596        String macAddress = null;
3597        BufferedReader reader = null;
3598        try {
3599            reader = new BufferedReader(new FileReader("/proc/net/arp"));
3600
3601            // Skip over the line bearing colum titles
3602            String line = reader.readLine();
3603
3604            while ((line = reader.readLine()) != null) {
3605                String[] tokens = line.split("[ ]+");
3606                if (tokens.length < 6) {
3607                    continue;
3608                }
3609
3610                // ARP column format is
3611                // Address HWType HWAddress Flags Mask IFace
3612                String ip = tokens[0];
3613                String mac = tokens[3];
3614
3615                if (ipAddress.equals(ip)) {
3616                    macAddress = mac;
3617                    break;
3618                }
3619            }
3620
3621            if (macAddress == null) {
3622                loge("Did not find remoteAddress {" + ipAddress + "} in " +
3623                        "/proc/net/arp");
3624            }
3625
3626        } catch (FileNotFoundException e) {
3627            loge("Could not open /proc/net/arp to lookup mac address");
3628        } catch (IOException e) {
3629            loge("Could not read /proc/net/arp to lookup mac address");
3630        } finally {
3631            try {
3632                if (reader != null) {
3633                    reader.close();
3634                }
3635            } catch (IOException e) {
3636                // Do nothing
3637            }
3638        }
3639        return macAddress;
3640
3641    }
3642
3643    private class WifiNetworkFactory extends NetworkFactory {
3644        public WifiNetworkFactory(Looper l, Context c, String TAG, NetworkCapabilities f) {
3645            super(l, c, TAG, f);
3646        }
3647        protected void startNetwork() {
3648            // TODO
3649            // Enter association mode.
3650        }
3651        protected void stopNetwork() {
3652            // TODO
3653            // Stop associating.
3654        }
3655    }
3656    /********************************************************
3657     * HSM states
3658     *******************************************************/
3659
3660    class DefaultState extends State {
3661        @Override
3662        public boolean processMessage(Message message) {
3663            logStateAndMessage(message, getClass().getSimpleName());
3664
3665            switch (message.what) {
3666                case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: {
3667                    AsyncChannel ac = (AsyncChannel) message.obj;
3668                    if (ac == mWifiP2pChannel) {
3669                        if (message.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
3670                            mWifiP2pChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
3671                        } else {
3672                            loge("WifiP2pService connection failure, error=" + message.arg1);
3673                        }
3674                    } else {
3675                        loge("got HALF_CONNECTED for unknown channel");
3676                    }
3677                    break;
3678                }
3679                case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
3680                    AsyncChannel ac = (AsyncChannel) message.obj;
3681                    if (ac == mWifiP2pChannel) {
3682                        loge("WifiP2pService channel lost, message.arg1 =" + message.arg1);
3683                        //TODO: Re-establish connection to state machine after a delay
3684                        // mWifiP2pChannel.connect(mContext, getHandler(),
3685                        // mWifiP2pManager.getMessenger());
3686                    }
3687                    break;
3688                }
3689                case CMD_BLUETOOTH_ADAPTER_STATE_CHANGE:
3690                    mBluetoothConnectionActive = (message.arg1 !=
3691                            BluetoothAdapter.STATE_DISCONNECTED);
3692                    break;
3693                    /* Synchronous call returns */
3694                case CMD_PING_SUPPLICANT:
3695                case CMD_ENABLE_NETWORK:
3696                case CMD_ADD_OR_UPDATE_NETWORK:
3697                case CMD_REMOVE_NETWORK:
3698                case CMD_SAVE_CONFIG:
3699                    replyToMessage(message, message.what, FAILURE);
3700                    break;
3701                case CMD_GET_CAPABILITY_FREQ:
3702                    replyToMessage(message, message.what, null);
3703                    break;
3704                case CMD_GET_CONFIGURED_NETWORKS:
3705                    replyToMessage(message, message.what, (List<WifiConfiguration>) null);
3706                    break;
3707                case CMD_GET_PRIVILEGED_CONFIGURED_NETWORKS:
3708                    replyToMessage(message, message.what, (List<WifiConfiguration>) null);
3709                    break;
3710                case CMD_ENABLE_RSSI_POLL:
3711                    mEnableRssiPolling = (message.arg1 == 1);
3712                    break;
3713                case CMD_ENABLE_BACKGROUND_SCAN:
3714                    mEnableBackgroundScan = (message.arg1 == 1);
3715                    break;
3716                case CMD_SET_HIGH_PERF_MODE:
3717                    if (message.arg1 == 1) {
3718                        setSuspendOptimizations(SUSPEND_DUE_TO_HIGH_PERF, false);
3719                    } else {
3720                        setSuspendOptimizations(SUSPEND_DUE_TO_HIGH_PERF, true);
3721                    }
3722                    break;
3723                case CMD_BOOT_COMPLETED:
3724                    String countryCode = mPersistedCountryCode;
3725                    if (TextUtils.isEmpty(countryCode) == false) {
3726                        Settings.Global.putString(mContext.getContentResolver(),
3727                                Settings.Global.WIFI_COUNTRY_CODE,
3728                                countryCode);
3729                        // It may be that the state transition that should send this info
3730                        // to the driver happened between mPersistedCountryCode getting set
3731                        // and now, so simply persisting it here would mean we have sent
3732                        // nothing to the driver.  Send the cmd so it might be set now.
3733                        int sequenceNum = mCountryCodeSequence.incrementAndGet();
3734                        sendMessageAtFrontOfQueue(CMD_SET_COUNTRY_CODE,
3735                                sequenceNum, 0, countryCode);
3736                    }
3737
3738                    checkAndSetConnectivityInstance();
3739                    mNetworkFactory = new WifiNetworkFactory(getHandler().getLooper(), mContext,
3740                            NETWORKTYPE, mNetworkCapabilitiesFilter);
3741                    mNetworkFactory.setScoreFilter(60);
3742                    mCm.registerNetworkFactory(new Messenger(mNetworkFactory), NETWORKTYPE);
3743                    break;
3744                case CMD_SET_BATCHED_SCAN:
3745                    recordBatchedScanSettings(message.arg1, message.arg2, (Bundle)message.obj);
3746                    break;
3747                case CMD_POLL_BATCHED_SCAN:
3748                    handleBatchedScanPollRequest();
3749                    break;
3750                case CMD_START_NEXT_BATCHED_SCAN:
3751                    startNextBatchedScan();
3752                    break;
3753                    /* Discard */
3754                case CMD_START_SCAN:
3755                case CMD_START_SUPPLICANT:
3756                case CMD_STOP_SUPPLICANT:
3757                case CMD_STOP_SUPPLICANT_FAILED:
3758                case CMD_START_DRIVER:
3759                case CMD_STOP_DRIVER:
3760                case CMD_DELAYED_STOP_DRIVER:
3761                case CMD_DRIVER_START_TIMED_OUT:
3762                case CMD_START_AP:
3763                case CMD_START_AP_SUCCESS:
3764                case CMD_START_AP_FAILURE:
3765                case CMD_STOP_AP:
3766                case CMD_TETHER_STATE_CHANGE:
3767                case CMD_TETHER_NOTIFICATION_TIMED_OUT:
3768                case CMD_DISCONNECT:
3769                case CMD_RECONNECT:
3770                case CMD_REASSOCIATE:
3771                case CMD_RELOAD_TLS_AND_RECONNECT:
3772                case WifiMonitor.SUP_CONNECTION_EVENT:
3773                case WifiMonitor.SUP_DISCONNECTION_EVENT:
3774                case WifiMonitor.NETWORK_CONNECTION_EVENT:
3775                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
3776                case WifiMonitor.SCAN_RESULTS_EVENT:
3777                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
3778                case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
3779                case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
3780                case WifiMonitor.WPS_OVERLAP_EVENT:
3781                case CMD_BLACKLIST_NETWORK:
3782                case CMD_CLEAR_BLACKLIST:
3783                case CMD_SET_OPERATIONAL_MODE:
3784                case CMD_SET_COUNTRY_CODE:
3785                case CMD_SET_FREQUENCY_BAND:
3786                case CMD_RSSI_POLL:
3787                case CMD_ENABLE_ALL_NETWORKS:
3788                case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
3789                case DhcpStateMachine.CMD_POST_DHCP_ACTION:
3790                /* Handled by WifiApConfigStore */
3791                case CMD_SET_AP_CONFIG:
3792                case CMD_SET_AP_CONFIG_COMPLETED:
3793                case CMD_REQUEST_AP_CONFIG:
3794                case CMD_RESPONSE_AP_CONFIG:
3795                case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
3796                case WifiWatchdogStateMachine.GOOD_LINK_DETECTED:
3797                case CMD_NO_NETWORKS_PERIODIC_SCAN:
3798                case CMD_DISABLE_P2P_RSP:
3799                case WifiMonitor.SUP_REQUEST_IDENTITY:
3800                    break;
3801                case DhcpStateMachine.CMD_ON_QUIT:
3802                    mDhcpStateMachine = null;
3803                    break;
3804                case CMD_SET_SUSPEND_OPT_ENABLED:
3805                    if (message.arg1 == 1) {
3806                        mSuspendWakeLock.release();
3807                        setSuspendOptimizations(SUSPEND_DUE_TO_SCREEN, true);
3808                    } else {
3809                        setSuspendOptimizations(SUSPEND_DUE_TO_SCREEN, false);
3810                    }
3811                    break;
3812                case WifiMonitor.DRIVER_HUNG_EVENT:
3813                    setSupplicantRunning(false);
3814                    setSupplicantRunning(true);
3815                    break;
3816                case WifiManager.CONNECT_NETWORK:
3817                    replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
3818                            WifiManager.BUSY);
3819                    break;
3820                case WifiManager.FORGET_NETWORK:
3821                    replyToMessage(message, WifiManager.FORGET_NETWORK_FAILED,
3822                            WifiManager.BUSY);
3823                    break;
3824                case WifiManager.SAVE_NETWORK:
3825                    replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED,
3826                            WifiManager.BUSY);
3827                    break;
3828                case WifiManager.START_WPS:
3829                    replyToMessage(message, WifiManager.WPS_FAILED,
3830                            WifiManager.BUSY);
3831                    break;
3832                case WifiManager.CANCEL_WPS:
3833                    replyToMessage(message, WifiManager.CANCEL_WPS_FAILED,
3834                            WifiManager.BUSY);
3835                    break;
3836                case WifiManager.DISABLE_NETWORK:
3837                    replyToMessage(message, WifiManager.DISABLE_NETWORK_FAILED,
3838                            WifiManager.BUSY);
3839                    break;
3840                case WifiManager.RSSI_PKTCNT_FETCH:
3841                    replyToMessage(message, WifiManager.RSSI_PKTCNT_FETCH_FAILED,
3842                            WifiManager.BUSY);
3843                    break;
3844                case WifiP2pServiceImpl.P2P_CONNECTION_CHANGED:
3845                    NetworkInfo info = (NetworkInfo) message.obj;
3846                    mP2pConnected.set(info.isConnected());
3847                    break;
3848                case WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST:
3849                    mTemporarilyDisconnectWifi = (message.arg1 == 1);
3850                    replyToMessage(message, WifiP2pServiceImpl.DISCONNECT_WIFI_RESPONSE);
3851                    break;
3852                /* Link configuration (IP address, DNS, ...) changes notified via netlink */
3853                case CMD_UPDATE_LINKPROPERTIES:
3854                    updateLinkProperties(CMD_UPDATE_LINKPROPERTIES);
3855                    break;
3856                case CMD_IP_CONFIGURATION_SUCCESSFUL:
3857                case CMD_IP_CONFIGURATION_LOST:
3858                    break;
3859                case CMD_GET_CONNECTION_STATISTICS:
3860                    replyToMessage(message, message.what, mWifiConnectionStatistics);
3861                    break;
3862                default:
3863                    loge("Error! unhandled message" + message);
3864                    break;
3865            }
3866            return HANDLED;
3867        }
3868    }
3869
3870    class InitialState extends State {
3871        @Override
3872        public void enter() {
3873            mWifiNative.unloadDriver();
3874
3875            if (mWifiP2pChannel == null) {
3876                mWifiP2pChannel = new AsyncChannel();
3877                mWifiP2pChannel.connect(mContext, getHandler(),
3878                    mWifiP2pServiceImpl.getP2pStateMachineMessenger());
3879            }
3880
3881            if (mWifiApConfigChannel == null) {
3882                mWifiApConfigChannel = new AsyncChannel();
3883                WifiApConfigStore wifiApConfigStore = WifiApConfigStore.makeWifiApConfigStore(
3884                        mContext, getHandler());
3885                wifiApConfigStore.loadApConfiguration();
3886                mWifiApConfigChannel.connectSync(mContext, getHandler(),
3887                        wifiApConfigStore.getMessenger());
3888            }
3889        }
3890        @Override
3891        public boolean processMessage(Message message) {
3892            logStateAndMessage(message, getClass().getSimpleName());
3893            switch (message.what) {
3894                case CMD_START_SUPPLICANT:
3895                    if (mWifiNative.loadDriver()) {
3896                        try {
3897                            mNwService.wifiFirmwareReload(mInterfaceName, "STA");
3898                        } catch (Exception e) {
3899                            loge("Failed to reload STA firmware " + e);
3900                            // Continue
3901                        }
3902
3903                        try {
3904                            // A runtime crash can leave the interface up and
3905                            // IP addresses configured, and this affects
3906                            // connectivity when supplicant starts up.
3907                            // Ensure interface is down and we have no IP
3908                            // addresses before a supplicant start.
3909                            mNwService.setInterfaceDown(mInterfaceName);
3910                            mNwService.clearInterfaceAddresses(mInterfaceName);
3911
3912                            // Set privacy extensions
3913                            mNwService.setInterfaceIpv6PrivacyExtensions(mInterfaceName, true);
3914
3915                           // IPv6 is enabled only as long as access point is connected since:
3916                           // - IPv6 addresses and routes stick around after disconnection
3917                           // - kernel is unaware when connected and fails to start IPv6 negotiation
3918                           // - kernel can start autoconfiguration when 802.1x is not complete
3919                            mNwService.disableIpv6(mInterfaceName);
3920                        } catch (RemoteException re) {
3921                            loge("Unable to change interface settings: " + re);
3922                        } catch (IllegalStateException ie) {
3923                            loge("Unable to change interface settings: " + ie);
3924                        }
3925
3926                       /* Stop a running supplicant after a runtime restart
3927                        * Avoids issues with drivers that do not handle interface down
3928                        * on a running supplicant properly.
3929                        */
3930                        mWifiMonitor.killSupplicant(mP2pSupported);
3931                        if(mWifiNative.startSupplicant(mP2pSupported)) {
3932                            setWifiState(WIFI_STATE_ENABLING);
3933                            if (DBG) log("Supplicant start successful");
3934                            mWifiMonitor.startMonitoring();
3935                            transitionTo(mSupplicantStartingState);
3936                        } else {
3937                            loge("Failed to start supplicant!");
3938                        }
3939                    } else {
3940                        loge("Failed to load driver");
3941                    }
3942                    break;
3943                case CMD_START_AP:
3944                    if (mWifiNative.loadDriver()) {
3945                        setWifiApState(WIFI_AP_STATE_ENABLING);
3946                        transitionTo(mSoftApStartingState);
3947                    } else {
3948                        loge("Failed to load driver for softap");
3949                    }
3950                default:
3951                    return NOT_HANDLED;
3952            }
3953            return HANDLED;
3954        }
3955    }
3956
3957    class SupplicantStartingState extends State {
3958        private void initializeWpsDetails() {
3959            String detail;
3960            detail = SystemProperties.get("ro.product.name", "");
3961            if (!mWifiNative.setDeviceName(detail)) {
3962                loge("Failed to set device name " +  detail);
3963            }
3964            detail = SystemProperties.get("ro.product.manufacturer", "");
3965            if (!mWifiNative.setManufacturer(detail)) {
3966                loge("Failed to set manufacturer " + detail);
3967            }
3968            detail = SystemProperties.get("ro.product.model", "");
3969            if (!mWifiNative.setModelName(detail)) {
3970                loge("Failed to set model name " + detail);
3971            }
3972            detail = SystemProperties.get("ro.product.model", "");
3973            if (!mWifiNative.setModelNumber(detail)) {
3974                loge("Failed to set model number " + detail);
3975            }
3976            detail = SystemProperties.get("ro.serialno", "");
3977            if (!mWifiNative.setSerialNumber(detail)) {
3978                loge("Failed to set serial number " + detail);
3979            }
3980            if (!mWifiNative.setConfigMethods("physical_display virtual_push_button")) {
3981                loge("Failed to set WPS config methods");
3982            }
3983            if (!mWifiNative.setDeviceType(mPrimaryDeviceType)) {
3984                loge("Failed to set primary device type " + mPrimaryDeviceType);
3985            }
3986        }
3987
3988        @Override
3989        public boolean processMessage(Message message) {
3990            logStateAndMessage(message, getClass().getSimpleName());
3991
3992            switch(message.what) {
3993                case WifiMonitor.SUP_CONNECTION_EVENT:
3994                    if (DBG) log("Supplicant connection established");
3995                    setWifiState(WIFI_STATE_ENABLED);
3996                    mSupplicantRestartCount = 0;
3997                    /* Reset the supplicant state to indicate the supplicant
3998                     * state is not known at this time */
3999                    mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
4000                    /* Initialize data structures */
4001                    mLastBssid = null;
4002                    mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
4003                    mLastSignalLevel = -1;
4004
4005                    mWifiInfo.setMacAddress(mWifiNative.getMacAddress());
4006                    mWifiNative.enableSaveConfig();
4007                    mWifiConfigStore.loadAndEnableAllNetworks();
4008                    initializeWpsDetails();
4009
4010                    sendSupplicantConnectionChangedBroadcast(true);
4011                    transitionTo(mDriverStartedState);
4012                    break;
4013                case WifiMonitor.SUP_DISCONNECTION_EVENT:
4014                    if (++mSupplicantRestartCount <= SUPPLICANT_RESTART_TRIES) {
4015                        loge("Failed to setup control channel, restart supplicant");
4016                        mWifiMonitor.killSupplicant(mP2pSupported);
4017                        transitionTo(mInitialState);
4018                        sendMessageDelayed(CMD_START_SUPPLICANT, SUPPLICANT_RESTART_INTERVAL_MSECS);
4019                    } else {
4020                        loge("Failed " + mSupplicantRestartCount +
4021                                " times to start supplicant, unload driver");
4022                        mSupplicantRestartCount = 0;
4023                        setWifiState(WIFI_STATE_UNKNOWN);
4024                        transitionTo(mInitialState);
4025                    }
4026                    break;
4027                case CMD_START_SUPPLICANT:
4028                case CMD_STOP_SUPPLICANT:
4029                case CMD_START_AP:
4030                case CMD_STOP_AP:
4031                case CMD_START_DRIVER:
4032                case CMD_STOP_DRIVER:
4033                case CMD_SET_OPERATIONAL_MODE:
4034                case CMD_SET_COUNTRY_CODE:
4035                case CMD_SET_FREQUENCY_BAND:
4036                case CMD_START_PACKET_FILTERING:
4037                case CMD_STOP_PACKET_FILTERING:
4038                    deferMessage(message);
4039                    break;
4040                default:
4041                    return NOT_HANDLED;
4042            }
4043            return HANDLED;
4044        }
4045    }
4046
4047    class SupplicantStartedState extends State {
4048        @Override
4049        public void enter() {
4050            /* Wifi is available as long as we have a connection to supplicant */
4051            mNetworkInfo.setIsAvailable(true);
4052            if (mNetworkAgent != null) mNetworkAgent.sendNetworkInfo(mNetworkInfo);
4053
4054            int defaultInterval = mContext.getResources().getInteger(
4055                    R.integer.config_wifi_supplicant_scan_interval);
4056
4057            mSupplicantScanIntervalMs = Settings.Global.getLong(mContext.getContentResolver(),
4058                    Settings.Global.WIFI_SUPPLICANT_SCAN_INTERVAL_MS,
4059                    defaultInterval);
4060
4061            mWifiNative.setScanInterval((int)mSupplicantScanIntervalMs / 1000);
4062
4063            if (mFrameworkAutoJoin.get()) {
4064                mWifiNative.enableAutoConnect(false);
4065            }
4066
4067        }
4068        @Override
4069        public boolean processMessage(Message message) {
4070            logStateAndMessage(message, getClass().getSimpleName());
4071
4072            switch(message.what) {
4073                case CMD_STOP_SUPPLICANT:   /* Supplicant stopped by user */
4074                    if (mP2pSupported) {
4075                        transitionTo(mWaitForP2pDisableState);
4076                    } else {
4077                        transitionTo(mSupplicantStoppingState);
4078                    }
4079                    break;
4080                case WifiMonitor.SUP_DISCONNECTION_EVENT:  /* Supplicant connection lost */
4081                    loge("Connection lost, restart supplicant");
4082                    handleSupplicantConnectionLoss();
4083                    handleNetworkDisconnect();
4084                    mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
4085                    if (mP2pSupported) {
4086                        transitionTo(mWaitForP2pDisableState);
4087                    } else {
4088                        transitionTo(mInitialState);
4089                    }
4090                    sendMessageDelayed(CMD_START_SUPPLICANT, SUPPLICANT_RESTART_INTERVAL_MSECS);
4091                    break;
4092                case WifiMonitor.SCAN_RESULTS_EVENT:
4093                    setScanResults();
4094                    sendScanResultsAvailableBroadcast();
4095                    mIsScanOngoing = false;
4096                    mIsFullScanOngoing = false;
4097                    if (mBufferedScanMsg.size() > 0)
4098                        sendMessage(mBufferedScanMsg.remove());
4099                    break;
4100                case CMD_PING_SUPPLICANT:
4101                    boolean ok = mWifiNative.ping();
4102                    replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
4103                    break;
4104                case CMD_GET_CAPABILITY_FREQ:
4105                    String freqs = mWifiNative.getFreqCapability();
4106                    replyToMessage(message, message.what, freqs);
4107                    break;
4108                case CMD_START_AP:
4109                    /* Cannot start soft AP while in client mode */
4110                    loge("Failed to start soft AP with a running supplicant");
4111                    setWifiApState(WIFI_AP_STATE_FAILED);
4112                    break;
4113                case CMD_SET_OPERATIONAL_MODE:
4114                    mOperationalMode = message.arg1;
4115                    break;
4116                case CMD_GET_ADAPTORS:
4117                    if (WifiNative.startHal()) {
4118                        List<WifiAdapter> adaptors = new ArrayList<WifiAdapter>();
4119                        int featureSet = WifiNative.getSupportedFeatureSet();
4120                        /* TODO: Get capabilities from adaptors themselves */
4121                        for (int i = 0; i < WifiNative.getInterfaces(); i++) {
4122                            String name = WifiNative.getInterfaceName(i);
4123                            WifiAdapter adaptor;
4124                            if (name.startsWith("wlan")) {
4125                                adaptor = new WifiAdapter(
4126                                        name, featureSet & ~WifiAdapter.WIFI_FEATURE_P2P);
4127                            } else if (name.startsWith("p2p")) {
4128                                adaptor = new WifiAdapter(
4129                                        name, featureSet & WifiAdapter.WIFI_FEATURE_P2P);
4130                            } else {
4131                                logd("Ignoring adaptor with name" + name);
4132                                continue;
4133                            }
4134                            adaptors.add(adaptor);
4135                        }
4136                        replyToMessage(message, message.what, adaptors);
4137                    } else {
4138                        List<WifiAdapter> adaptors = new ArrayList<WifiAdapter>();
4139                        replyToMessage(message, message.what, adaptors);
4140                    }
4141                default:
4142                    return NOT_HANDLED;
4143            }
4144            return HANDLED;
4145        }
4146
4147        @Override
4148        public void exit() {
4149            mNetworkInfo.setIsAvailable(false);
4150            if (mNetworkAgent != null) mNetworkAgent.sendNetworkInfo(mNetworkInfo);
4151        }
4152    }
4153
4154    class SupplicantStoppingState extends State {
4155        @Override
4156        public void enter() {
4157            /* Send any reset commands to supplicant before shutting it down */
4158            handleNetworkDisconnect();
4159            if (mDhcpStateMachine != null) {
4160                mDhcpStateMachine.doQuit();
4161            }
4162
4163            if (DBG) log("stopping supplicant");
4164            mWifiMonitor.stopSupplicant();
4165
4166            /* Send ourselves a delayed message to indicate failure after a wait time */
4167            sendMessageDelayed(obtainMessage(CMD_STOP_SUPPLICANT_FAILED,
4168                    ++mSupplicantStopFailureToken, 0), SUPPLICANT_RESTART_INTERVAL_MSECS);
4169            setWifiState(WIFI_STATE_DISABLING);
4170            mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
4171        }
4172        @Override
4173        public boolean processMessage(Message message) {
4174            logStateAndMessage(message, getClass().getSimpleName());
4175
4176            switch(message.what) {
4177                case WifiMonitor.SUP_CONNECTION_EVENT:
4178                    loge("Supplicant connection received while stopping");
4179                    break;
4180                case WifiMonitor.SUP_DISCONNECTION_EVENT:
4181                    if (DBG) log("Supplicant connection lost");
4182                    handleSupplicantConnectionLoss();
4183                    transitionTo(mInitialState);
4184                    break;
4185                case CMD_STOP_SUPPLICANT_FAILED:
4186                    if (message.arg1 == mSupplicantStopFailureToken) {
4187                        loge("Timed out on a supplicant stop, kill and proceed");
4188                        handleSupplicantConnectionLoss();
4189                        transitionTo(mInitialState);
4190                    }
4191                    break;
4192                case CMD_START_SUPPLICANT:
4193                case CMD_STOP_SUPPLICANT:
4194                case CMD_START_AP:
4195                case CMD_STOP_AP:
4196                case CMD_START_DRIVER:
4197                case CMD_STOP_DRIVER:
4198                case CMD_SET_OPERATIONAL_MODE:
4199                case CMD_SET_COUNTRY_CODE:
4200                case CMD_SET_FREQUENCY_BAND:
4201                case CMD_START_PACKET_FILTERING:
4202                case CMD_STOP_PACKET_FILTERING:
4203                    deferMessage(message);
4204                    break;
4205                default:
4206                    return NOT_HANDLED;
4207            }
4208            return HANDLED;
4209        }
4210    }
4211
4212    class DriverStartingState extends State {
4213        private int mTries;
4214        @Override
4215        public void enter() {
4216            mTries = 1;
4217            /* Send ourselves a delayed message to start driver a second time */
4218            sendMessageDelayed(obtainMessage(CMD_DRIVER_START_TIMED_OUT,
4219                        ++mDriverStartToken, 0), DRIVER_START_TIME_OUT_MSECS);
4220        }
4221        @Override
4222        public boolean processMessage(Message message) {
4223            logStateAndMessage(message, getClass().getSimpleName());
4224
4225            switch(message.what) {
4226               case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
4227                    SupplicantState state = handleSupplicantStateChange(message);
4228                    /* If suplicant is exiting out of INTERFACE_DISABLED state into
4229                     * a state that indicates driver has started, it is ready to
4230                     * receive driver commands
4231                     */
4232                    if (SupplicantState.isDriverActive(state)) {
4233                        transitionTo(mDriverStartedState);
4234                    }
4235                    break;
4236                case CMD_DRIVER_START_TIMED_OUT:
4237                    if (message.arg1 == mDriverStartToken) {
4238                        if (mTries >= 2) {
4239                            loge("Failed to start driver after " + mTries);
4240                            transitionTo(mDriverStoppedState);
4241                        } else {
4242                            loge("Driver start failed, retrying");
4243                            mWakeLock.acquire();
4244                            mWifiNative.startDriver();
4245                            mWakeLock.release();
4246
4247                            ++mTries;
4248                            /* Send ourselves a delayed message to start driver again */
4249                            sendMessageDelayed(obtainMessage(CMD_DRIVER_START_TIMED_OUT,
4250                                        ++mDriverStartToken, 0), DRIVER_START_TIME_OUT_MSECS);
4251                        }
4252                    }
4253                    break;
4254                    /* Queue driver commands & connection events */
4255                case CMD_START_DRIVER:
4256                case CMD_STOP_DRIVER:
4257                case WifiMonitor.NETWORK_CONNECTION_EVENT:
4258                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
4259                case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
4260                case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
4261                case WifiMonitor.WPS_OVERLAP_EVENT:
4262                case CMD_SET_COUNTRY_CODE:
4263                case CMD_SET_FREQUENCY_BAND:
4264                case CMD_START_PACKET_FILTERING:
4265                case CMD_STOP_PACKET_FILTERING:
4266                case CMD_START_SCAN:
4267                case CMD_DISCONNECT:
4268                case CMD_REASSOCIATE:
4269                case CMD_RECONNECT:
4270                    deferMessage(message);
4271                    break;
4272                default:
4273                    return NOT_HANDLED;
4274            }
4275            return HANDLED;
4276        }
4277    }
4278
4279    class DriverStartedState extends State {
4280        @Override
4281        public void enter() {
4282
4283            if (PDBG) {
4284                loge("Driverstarted State enter");
4285            }
4286            mIsRunning = true;
4287            mInDelayedStop = false;
4288            mDelayedStopCounter++;
4289            updateBatteryWorkSource(null);
4290            /**
4291             * Enable bluetooth coexistence scan mode when bluetooth connection is active.
4292             * When this mode is on, some of the low-level scan parameters used by the
4293             * driver are changed to reduce interference with bluetooth
4294             */
4295            mWifiNative.setBluetoothCoexistenceScanMode(mBluetoothConnectionActive);
4296            /* set country code */
4297            setCountryCode();
4298            /* set frequency band of operation */
4299            setFrequencyBand();
4300            /* initialize network state */
4301            setNetworkDetailedState(DetailedState.DISCONNECTED);
4302
4303            /* Remove any filtering on Multicast v6 at start */
4304            mWifiNative.stopFilteringMulticastV6Packets();
4305
4306            /* Reset Multicast v4 filtering state */
4307            if (mFilteringMulticastV4Packets.get()) {
4308                mWifiNative.startFilteringMulticastV4Packets();
4309            } else {
4310                mWifiNative.stopFilteringMulticastV4Packets();
4311            }
4312
4313            mDhcpActive = false;
4314
4315            startBatchedScan();
4316
4317            if (mOperationalMode != CONNECT_MODE) {
4318                mWifiNative.disconnect();
4319                mWifiConfigStore.disableAllNetworks();
4320                if (mOperationalMode == SCAN_ONLY_WITH_WIFI_OFF_MODE) {
4321                    setWifiState(WIFI_STATE_DISABLED);
4322                }
4323                transitionTo(mScanModeState);
4324            } else {
4325                /* Driver stop may have disabled networks, enable right after start */
4326                mWifiConfigStore.enableAllNetworks();
4327
4328                if (DBG) loge("Attempting to reconnect to wifi network ..");
4329                mWifiNative.reconnect();
4330
4331                // Status pulls in the current supplicant state and network connection state
4332                // events over the monitor connection. This helps framework sync up with
4333                // current supplicant state
4334                mWifiNative.status();
4335                transitionTo(mDisconnectedState);
4336            }
4337
4338            // We may have missed screen update at boot
4339            if (mScreenBroadcastReceived.get() == false) {
4340                PowerManager powerManager = (PowerManager)mContext.getSystemService(
4341                        Context.POWER_SERVICE);
4342                handleScreenStateChanged(powerManager.isScreenOn());
4343            } else {
4344                // Set the right suspend mode settings
4345                mWifiNative.setSuspendOptimizations(mSuspendOptNeedsDisabled == 0
4346                        && mUserWantsSuspendOpt.get());
4347            }
4348            mWifiNative.setPowerSave(true);
4349
4350            if (mP2pSupported) {
4351                if (mOperationalMode == CONNECT_MODE) {
4352                    mWifiP2pChannel.sendMessage(WifiStateMachine.CMD_ENABLE_P2P);
4353                } else {
4354                    // P2P statemachine starts in disabled state, and is not enabled until
4355                    // CMD_ENABLE_P2P is sent from here; so, nothing needs to be done to
4356                    // keep it disabled.
4357                }
4358            }
4359
4360            final Intent intent = new Intent(WifiManager.WIFI_SCAN_AVAILABLE);
4361            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
4362            intent.putExtra(WifiManager.EXTRA_SCAN_AVAILABLE, WIFI_STATE_ENABLED);
4363            mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
4364
4365            if (PDBG) {
4366                loge("Driverstarted State enter done");
4367            }
4368        }
4369
4370        @Override
4371        public boolean processMessage(Message message) {
4372            logStateAndMessage(message, getClass().getSimpleName());
4373
4374            switch(message.what) {
4375                case CMD_START_SCAN:
4376                    handleScanRequest(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, message);
4377                    break;
4378                case CMD_SET_BATCHED_SCAN:
4379                    if (recordBatchedScanSettings(message.arg1, message.arg2,
4380                            (Bundle)message.obj)) {
4381                        if (mBatchedScanSettings != null) {
4382                            startBatchedScan();
4383                        } else {
4384                            stopBatchedScan();
4385                        }
4386                    }
4387                    break;
4388                case CMD_SET_COUNTRY_CODE:
4389                    String country = (String) message.obj;
4390                    final boolean persist = (message.arg2 == 1);
4391                    final int sequence = message.arg1;
4392                    if (sequence != mCountryCodeSequence.get()) {
4393                        if (DBG) log("set country code ignored due to sequnce num");
4394                        break;
4395                    }
4396                    if (DBG) log("set country code " + country);
4397                    if (persist) {
4398                        mPersistedCountryCode = country;
4399                        Settings.Global.putString(mContext.getContentResolver(),
4400                                Settings.Global.WIFI_COUNTRY_CODE,
4401                                country);
4402                    }
4403                    country = country.toUpperCase(Locale.ROOT);
4404                    if (mLastSetCountryCode == null
4405                            || country.equals(mLastSetCountryCode) == false) {
4406                        if (mWifiNative.setCountryCode(country)) {
4407                            mLastSetCountryCode = country;
4408                        } else {
4409                            loge("Failed to set country code " + country);
4410                        }
4411                    }
4412                    mWifiP2pChannel.sendMessage(WifiP2pServiceImpl.SET_COUNTRY_CODE, country);
4413                    break;
4414                case CMD_SET_FREQUENCY_BAND:
4415                    int band =  message.arg1;
4416                    if (DBG) log("set frequency band " + band);
4417                    if (mWifiNative.setBand(band)) {
4418
4419                        if (PDBG)  loge("did set frequency band " + band);
4420
4421                        mFrequencyBand.set(band);
4422                        // Flush old data - like scan results
4423                        mWifiNative.bssFlush();
4424                        // Fetch the latest scan results when frequency band is set
4425                        if (mFrameworkAutoJoin.get())
4426                            startScanNative(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, null);
4427                        else
4428                            startScanNative(WifiNative.SCAN_WITH_CONNECTION_SETUP, null);
4429                        if (PDBG)  loge("done set frequency band " + band);
4430
4431                    } else {
4432                        loge("Failed to set frequency band " + band);
4433                    }
4434                    break;
4435                case CMD_BLUETOOTH_ADAPTER_STATE_CHANGE:
4436                    mBluetoothConnectionActive = (message.arg1 !=
4437                            BluetoothAdapter.STATE_DISCONNECTED);
4438                    mWifiNative.setBluetoothCoexistenceScanMode(mBluetoothConnectionActive);
4439                    break;
4440                case CMD_STOP_DRIVER:
4441                    int mode = message.arg1;
4442
4443                    /* Already doing a delayed stop */
4444                    if (mInDelayedStop) {
4445                        if (DBG) log("Already in delayed stop");
4446                        break;
4447                    }
4448                    /* disconnect right now, but leave the driver running for a bit */
4449                    mWifiConfigStore.disableAllNetworks();
4450
4451                    mInDelayedStop = true;
4452                    mDelayedStopCounter++;
4453                    if (DBG) log("Delayed stop message " + mDelayedStopCounter);
4454
4455                    /* send regular delayed shut down */
4456                    Intent driverStopIntent = new Intent(ACTION_DELAYED_DRIVER_STOP, null);
4457                    driverStopIntent.putExtra(DELAYED_STOP_COUNTER, mDelayedStopCounter);
4458                    mDriverStopIntent = PendingIntent.getBroadcast(mContext,
4459                            DRIVER_STOP_REQUEST, driverStopIntent,
4460                            PendingIntent.FLAG_UPDATE_CURRENT);
4461
4462                    mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()
4463                            + mDriverStopDelayMs, mDriverStopIntent);
4464                    break;
4465                case CMD_START_DRIVER:
4466                    if (mInDelayedStop) {
4467                        mInDelayedStop = false;
4468                        mDelayedStopCounter++;
4469                        mAlarmManager.cancel(mDriverStopIntent);
4470                        if (DBG) log("Delayed stop ignored due to start");
4471                        if (mOperationalMode == CONNECT_MODE) {
4472                            mWifiConfigStore.enableAllNetworks();
4473                        }
4474                    }
4475                    break;
4476                case CMD_DELAYED_STOP_DRIVER:
4477                    if (DBG) log("delayed stop " + message.arg1 + " " + mDelayedStopCounter);
4478                    if (message.arg1 != mDelayedStopCounter) break;
4479                    if (getCurrentState() != mDisconnectedState) {
4480                        mWifiNative.disconnect();
4481                        handleNetworkDisconnect();
4482                    }
4483                    mWakeLock.acquire();
4484                    mWifiNative.stopDriver();
4485                    mWakeLock.release();
4486                    if (mP2pSupported) {
4487                        transitionTo(mWaitForP2pDisableState);
4488                    } else {
4489                        transitionTo(mDriverStoppingState);
4490                    }
4491                    break;
4492                case CMD_START_PACKET_FILTERING:
4493                    if (message.arg1 == MULTICAST_V6) {
4494                        mWifiNative.startFilteringMulticastV6Packets();
4495                    } else if (message.arg1 == MULTICAST_V4) {
4496                        mWifiNative.startFilteringMulticastV4Packets();
4497                    } else {
4498                        loge("Illegal arugments to CMD_START_PACKET_FILTERING");
4499                    }
4500                    break;
4501                case CMD_STOP_PACKET_FILTERING:
4502                    if (message.arg1 == MULTICAST_V6) {
4503                        mWifiNative.stopFilteringMulticastV6Packets();
4504                    } else if (message.arg1 == MULTICAST_V4) {
4505                        mWifiNative.stopFilteringMulticastV4Packets();
4506                    } else {
4507                        loge("Illegal arugments to CMD_STOP_PACKET_FILTERING");
4508                    }
4509                    break;
4510                case CMD_SET_SUSPEND_OPT_ENABLED:
4511                    if (message.arg1 == 1) {
4512                        setSuspendOptimizationsNative(SUSPEND_DUE_TO_SCREEN, true);
4513                        mSuspendWakeLock.release();
4514                    } else {
4515                        setSuspendOptimizationsNative(SUSPEND_DUE_TO_SCREEN, false);
4516                    }
4517                    break;
4518                case CMD_SET_HIGH_PERF_MODE:
4519                    if (message.arg1 == 1) {
4520                        setSuspendOptimizationsNative(SUSPEND_DUE_TO_HIGH_PERF, false);
4521                    } else {
4522                        setSuspendOptimizationsNative(SUSPEND_DUE_TO_HIGH_PERF, true);
4523                    }
4524                    break;
4525                case CMD_ENABLE_TDLS:
4526                    if (message.obj != null) {
4527                        String remoteAddress = (String) message.obj;
4528                        boolean enable = (message.arg1 == 1);
4529                        mWifiNative.startTdls(remoteAddress, enable);
4530                    }
4531                    break;
4532                default:
4533                    return NOT_HANDLED;
4534            }
4535            return HANDLED;
4536        }
4537        @Override
4538        public void exit() {
4539            mIsRunning = false;
4540            updateBatteryWorkSource(null);
4541            mScanResults = new ArrayList<ScanResult>();
4542
4543            stopBatchedScan();
4544
4545            final Intent intent = new Intent(WifiManager.WIFI_SCAN_AVAILABLE);
4546            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
4547            intent.putExtra(WifiManager.EXTRA_SCAN_AVAILABLE, WIFI_STATE_DISABLED);
4548            mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
4549            noteScanEnd(); // wrap up any pending request.
4550            mBufferedScanMsg.clear();
4551
4552            mLastSetCountryCode = null;
4553        }
4554    }
4555
4556    class WaitForP2pDisableState extends State {
4557        private State mTransitionToState;
4558        @Override
4559        public void enter() {
4560            switch (getCurrentMessage().what) {
4561                case WifiMonitor.SUP_DISCONNECTION_EVENT:
4562                    mTransitionToState = mInitialState;
4563                    break;
4564                case CMD_DELAYED_STOP_DRIVER:
4565                    mTransitionToState = mDriverStoppingState;
4566                    break;
4567                case CMD_STOP_SUPPLICANT:
4568                    mTransitionToState = mSupplicantStoppingState;
4569                    break;
4570                default:
4571                    mTransitionToState = mDriverStoppingState;
4572                    break;
4573            }
4574            mWifiP2pChannel.sendMessage(WifiStateMachine.CMD_DISABLE_P2P_REQ);
4575        }
4576        @Override
4577        public boolean processMessage(Message message) {
4578            logStateAndMessage(message, getClass().getSimpleName());
4579
4580            switch(message.what) {
4581                case WifiStateMachine.CMD_DISABLE_P2P_RSP:
4582                    transitionTo(mTransitionToState);
4583                    break;
4584                /* Defer wifi start/shut and driver commands */
4585                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
4586                case CMD_START_SUPPLICANT:
4587                case CMD_STOP_SUPPLICANT:
4588                case CMD_START_AP:
4589                case CMD_STOP_AP:
4590                case CMD_START_DRIVER:
4591                case CMD_STOP_DRIVER:
4592                case CMD_SET_OPERATIONAL_MODE:
4593                case CMD_SET_COUNTRY_CODE:
4594                case CMD_SET_FREQUENCY_BAND:
4595                case CMD_START_PACKET_FILTERING:
4596                case CMD_STOP_PACKET_FILTERING:
4597                case CMD_START_SCAN:
4598                case CMD_DISCONNECT:
4599                case CMD_REASSOCIATE:
4600                case CMD_RECONNECT:
4601                    deferMessage(message);
4602                    break;
4603                default:
4604                    return NOT_HANDLED;
4605            }
4606            return HANDLED;
4607        }
4608    }
4609
4610    class DriverStoppingState extends State {
4611        @Override
4612        public boolean processMessage(Message message) {
4613            logStateAndMessage(message, getClass().getSimpleName());
4614
4615            switch(message.what) {
4616                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
4617                    SupplicantState state = handleSupplicantStateChange(message);
4618                    if (state == SupplicantState.INTERFACE_DISABLED) {
4619                        transitionTo(mDriverStoppedState);
4620                    }
4621                    break;
4622                    /* Queue driver commands */
4623                case CMD_START_DRIVER:
4624                case CMD_STOP_DRIVER:
4625                case CMD_SET_COUNTRY_CODE:
4626                case CMD_SET_FREQUENCY_BAND:
4627                case CMD_START_PACKET_FILTERING:
4628                case CMD_STOP_PACKET_FILTERING:
4629                case CMD_START_SCAN:
4630                case CMD_DISCONNECT:
4631                case CMD_REASSOCIATE:
4632                case CMD_RECONNECT:
4633                    deferMessage(message);
4634                    break;
4635                default:
4636                    return NOT_HANDLED;
4637            }
4638            return HANDLED;
4639        }
4640    }
4641
4642    class DriverStoppedState extends State {
4643        @Override
4644        public boolean processMessage(Message message) {
4645            logStateAndMessage(message, getClass().getSimpleName());
4646            switch (message.what) {
4647                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
4648                    StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
4649                    SupplicantState state = stateChangeResult.state;
4650                    // A WEXT bug means that we can be back to driver started state
4651                    // unexpectedly
4652                    if (SupplicantState.isDriverActive(state)) {
4653                        transitionTo(mDriverStartedState);
4654                    }
4655                    break;
4656                case CMD_START_DRIVER:
4657                    mWakeLock.acquire();
4658                    mWifiNative.startDriver();
4659                    mWakeLock.release();
4660                    transitionTo(mDriverStartingState);
4661                    break;
4662                default:
4663                    return NOT_HANDLED;
4664            }
4665            return HANDLED;
4666        }
4667    }
4668
4669    class ScanModeState extends State {
4670        private int mLastOperationMode;
4671        @Override
4672        public void enter() {
4673            mLastOperationMode = mOperationalMode;
4674        }
4675        @Override
4676        public boolean processMessage(Message message) {
4677            logStateAndMessage(message, getClass().getSimpleName());
4678
4679            switch(message.what) {
4680                case CMD_SET_OPERATIONAL_MODE:
4681                    if (message.arg1 == CONNECT_MODE) {
4682
4683                        if (mLastOperationMode == SCAN_ONLY_WITH_WIFI_OFF_MODE) {
4684                            setWifiState(WIFI_STATE_ENABLED);
4685                            // Load and re-enable networks when going back to enabled state
4686                            // This is essential for networks to show up after restore
4687                            mWifiConfigStore.loadAndEnableAllNetworks();
4688                            mWifiP2pChannel.sendMessage(CMD_ENABLE_P2P);
4689                        } else {
4690                            mWifiConfigStore.enableAllNetworks();
4691                        }
4692
4693                        mWifiNative.reconnect();
4694
4695                        mOperationalMode = CONNECT_MODE;
4696                        transitionTo(mDisconnectedState);
4697                    } else {
4698                        // Nothing to do
4699                        return HANDLED;
4700                    }
4701                    break;
4702                // Handle scan. All the connection related commands are
4703                // handled only in ConnectModeState
4704                case CMD_START_SCAN:
4705                    handleScanRequest(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, message);
4706                    break;
4707                default:
4708                    return NOT_HANDLED;
4709            }
4710            return HANDLED;
4711        }
4712    }
4713
4714
4715    String smToString(Message message) {
4716        return smToString(message.what);
4717    }
4718
4719    String smToString(int what) {
4720        String s = "unknown";
4721        switch (what) {
4722            case WifiMonitor.DRIVER_HUNG_EVENT:
4723                s = "DRIVER_HUNG_EVENT";
4724                break;
4725            case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
4726                s = "AsyncChannel.CMD_CHANNEL_HALF_CONNECTED";
4727                break;
4728            case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
4729                s = "AsyncChannel.CMD_CHANNEL_DISCONNECTED";
4730                break;
4731            case CMD_SET_FREQUENCY_BAND:
4732                s = "CMD_SET_FREQUENCY_BAND";
4733                break;
4734            case CMD_START_DRIVER:
4735                s = "CMD_START_DRIVER";
4736                break;
4737            case CMD_STOP_DRIVER:
4738                s = "CMD_STOP_DRIVER";
4739                break;
4740            case CMD_STOP_SUPPLICANT:
4741                s = "CMD_STOP_SUPPLICANT";
4742                break;
4743            case CMD_START_SUPPLICANT:
4744                s = "CMD_START_SUPPLICANT";
4745                break;
4746            case CMD_REQUEST_AP_CONFIG:
4747                s = "CMD_REQUEST_AP_CONFIG";
4748                break;
4749            case CMD_RESPONSE_AP_CONFIG:
4750                s = "CMD_RESPONSE_AP_CONFIG";
4751                break;
4752            case CMD_TETHER_STATE_CHANGE:
4753                s = "CMD_TETHER_STATE_CHANGE";
4754                break;
4755            case CMD_TETHER_NOTIFICATION_TIMED_OUT:
4756                s = "CMD_TETHER_NOTIFICATION_TIMED_OUT";
4757                break;
4758            case CMD_BLUETOOTH_ADAPTER_STATE_CHANGE:
4759                s = "CMD_BLUETOOTH_ADAPTER_STATE_CHANGE";
4760                break;
4761            case CMD_ADD_OR_UPDATE_NETWORK:
4762                s = "CMD_ADD_OR_UPDATE_NETWORK";
4763                break;
4764            case CMD_REMOVE_NETWORK:
4765                s = "CMD_REMOVE_NETWORK";
4766                break;
4767            case CMD_ENABLE_NETWORK:
4768                s = "CMD_ENABLE_NETWORK";
4769                break;
4770            case CMD_ENABLE_ALL_NETWORKS:
4771                s = "CMD_ENABLE_ALL_NETWORKS";
4772                break;
4773            case CMD_AUTO_CONNECT:
4774                s = "CMD_AUTO_CONNECT";
4775                break;
4776            case CMD_AUTO_ROAM:
4777                s = "CMD_AUTO_ROAM";
4778                break;
4779            case CMD_BOOT_COMPLETED:
4780                s = "CMD_BOOT_COMPLETED";
4781                break;
4782            case DhcpStateMachine.CMD_START_DHCP:
4783                s = "CMD_START_DHCP";
4784                break;
4785            case DhcpStateMachine.CMD_STOP_DHCP:
4786                s = "CMD_STOP_DHCP";
4787                break;
4788            case DhcpStateMachine.CMD_RENEW_DHCP:
4789                s = "CMD_RENEW_DHCP";
4790                break;
4791            case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
4792                s = "CMD_PRE_DHCP_ACTION";
4793                break;
4794            case DhcpStateMachine.CMD_POST_DHCP_ACTION:
4795                s = "CMD_POST_DHCP_ACTION";
4796                break;
4797            case DhcpStateMachine.CMD_PRE_DHCP_ACTION_COMPLETE:
4798                s = "CMD_PRE_DHCP_ACTION_COMPLETE";
4799                break;
4800            case DhcpStateMachine.CMD_ON_QUIT:
4801                s = "CMD_ON_QUIT";
4802                break;
4803            case WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST:
4804                s = "WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST";
4805                break;
4806            case WifiManager.DISABLE_NETWORK:
4807                s = "WifiManager.DISABLE_NETWORK";
4808                break;
4809            case CMD_BLACKLIST_NETWORK:
4810                s = "CMD_BLACKLIST_NETWORK";
4811                break;
4812            case CMD_CLEAR_BLACKLIST:
4813                s = "CMD_CLEAR_BLACKLIST";
4814                break;
4815            case CMD_SAVE_CONFIG:
4816                s = "CMD_SAVE_CONFIG";
4817                break;
4818            case CMD_GET_CONFIGURED_NETWORKS:
4819                s = "CMD_GET_CONFIGURED_NETWORKS";
4820                break;
4821            case CMD_GET_ADAPTORS:
4822                s = "CMD_GET_ADAPTORS";
4823            case CMD_GET_PRIVILEGED_CONFIGURED_NETWORKS:
4824                s = "CMD_GET_PRIVILEGED_CONFIGURED_NETWORKS";
4825                break;
4826            case CMD_DISCONNECT:
4827                s = "CMD_DISCONNECT";
4828                break;
4829            case CMD_RECONNECT:
4830                s = "CMD_RECONNECT";
4831                break;
4832            case CMD_REASSOCIATE:
4833                s = "CMD_REASSOCIATE";
4834                break;
4835            case CMD_GET_CONNECTION_STATISTICS:
4836                s = "CMD_GET_CONNECTION_STATISTICS";
4837                break;
4838            case CMD_SET_HIGH_PERF_MODE:
4839                s = "CMD_SET_HIGH_PERF_MODE";
4840                break;
4841            case CMD_SET_COUNTRY_CODE:
4842                s = "CMD_SET_COUNTRY_CODE";
4843                break;
4844            case CMD_ENABLE_RSSI_POLL:
4845                s = "CMD_ENABLE_RSSI_POLL";
4846                break;
4847            case CMD_RSSI_POLL:
4848                s = "CMD_RSSI_POLL";
4849                break;
4850            case CMD_START_PACKET_FILTERING:
4851                s = "CMD_START_PACKET_FILTERING";
4852                break;
4853            case CMD_STOP_PACKET_FILTERING:
4854                s = "CMD_STOP_PACKET_FILTERING";
4855                break;
4856            case CMD_SET_SUSPEND_OPT_ENABLED:
4857                s = "CMD_SET_SUSPEND_OPT_ENABLED";
4858                break;
4859            case CMD_NO_NETWORKS_PERIODIC_SCAN:
4860                s = "CMD_NO_NETWORKS_PERIODIC_SCAN";
4861                break;
4862            case CMD_SET_BATCHED_SCAN:
4863                s = "CMD_SET_BATCHED_SCAN";
4864                break;
4865            case CMD_START_NEXT_BATCHED_SCAN:
4866                s = "CMD_START_NEXT_BATCHED_SCAN";
4867                break;
4868            case CMD_POLL_BATCHED_SCAN:
4869                s = "CMD_POLL_BATCHED_SCAN";
4870                break;
4871            case CMD_UPDATE_LINKPROPERTIES:
4872                s = "CMD_UPDATE_LINKPROPERTIES";
4873                break;
4874            case CMD_RELOAD_TLS_AND_RECONNECT:
4875                s = "CMD_RELOAD_TLS_AND_RECONNECT";
4876                break;
4877            case WifiManager.CONNECT_NETWORK:
4878                s = "CONNECT_NETWORK";
4879                break;
4880            case WifiManager.SAVE_NETWORK:
4881                s = "SAVE_NETWORK";
4882                break;
4883            case WifiManager.FORGET_NETWORK:
4884                s = "FORGET_NETWORK";
4885                break;
4886            case WifiMonitor.SUP_CONNECTION_EVENT:
4887                s = "SUP_CONNECTION_EVENT";
4888                break;
4889            case WifiMonitor.SUP_DISCONNECTION_EVENT:
4890                s = "SUP_DISCONNECTION_EVENT";
4891                break;
4892            case WifiMonitor.SCAN_RESULTS_EVENT:
4893                s = "SCAN_RESULTS_EVENT";
4894                break;
4895            case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
4896                s = "SUPPLICANT_STATE_CHANGE_EVENT";
4897                break;
4898            case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
4899                s = "AUTHENTICATION_FAILURE_EVENT";
4900                break;
4901            case WifiMonitor.SSID_TEMP_DISABLED:
4902                s = "SSID_TEMP_DISABLED";
4903                break;
4904            case WifiMonitor.SSID_REENABLED:
4905                s = "SSID_REENABLED";
4906                break;
4907            case WifiMonitor.WPS_SUCCESS_EVENT:
4908                s = "WPS_SUCCESS_EVENT";
4909                break;
4910            case WifiMonitor.WPS_FAIL_EVENT:
4911                s = "WPS_FAIL_EVENT";
4912                break;
4913            case WifiMonitor.SUP_REQUEST_IDENTITY:
4914                s = "SUP_REQUEST_IDENTITY";
4915                break;
4916            case WifiMonitor.NETWORK_CONNECTION_EVENT:
4917                s = "NETWORK_CONNECTION_EVENT";
4918                break;
4919            case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
4920                s = "NETWORK_DISCONNECTION_EVENT";
4921                break;
4922            case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
4923                s = "ASSOCIATION_REJECTION_EVENT";
4924                break;
4925            case CMD_SET_OPERATIONAL_MODE:
4926                s = "CMD_SET_OPERATIONAL_MODE";
4927                break;
4928            case CMD_START_SCAN:
4929                s = "CMD_START_SCAN";
4930                break;
4931            case CMD_ENABLE_BACKGROUND_SCAN:
4932                s = "CMD_ENABLE_BACKGROUND_SCAN";
4933                break;
4934            case CMD_DISABLE_P2P_RSP:
4935                s = "CMD_DISABLE_P2P_RSP";
4936                break;
4937            case CMD_DISABLE_P2P_REQ:
4938                s = "CMD_DISABLE_P2P_REQ";
4939                break;
4940            case WifiWatchdogStateMachine.GOOD_LINK_DETECTED:
4941                s = "GOOD_LINK_DETECTED";
4942                break;
4943            case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
4944                s = "POOR_LINK_DETECTED";
4945                break;
4946            case WifiP2pServiceImpl.GROUP_CREATING_TIMED_OUT:
4947                s = "GROUP_CREATING_TIMED_OUT";
4948                break;
4949            case WifiP2pServiceImpl.P2P_CONNECTION_CHANGED:
4950                s = "P2P_CONNECTION_CHANGED";
4951                break;
4952            case WifiP2pServiceImpl.DISCONNECT_WIFI_RESPONSE:
4953                s = "P2P.DISCONNECT_WIFI_RESPONSE";
4954                break;
4955            case WifiP2pServiceImpl.SET_MIRACAST_MODE:
4956                s = "P2P.SET_MIRACAST_MODE";
4957                break;
4958            case WifiP2pServiceImpl.BLOCK_DISCOVERY:
4959                s = "P2P.BLOCK_DISCOVERY";
4960                break;
4961            case WifiP2pServiceImpl.SET_COUNTRY_CODE:
4962                s = "P2P.SET_COUNTRY_CODE";
4963                break;
4964            case WifiManager.CANCEL_WPS:
4965                s = "CANCEL_WPS";
4966                break;
4967            case WifiManager.CANCEL_WPS_FAILED:
4968                s = "CANCEL_WPS_FAILED";
4969                break;
4970            case WifiManager.CANCEL_WPS_SUCCEDED:
4971                s = "CANCEL_WPS_SUCCEDED";
4972                break;
4973            case WifiManager.START_WPS:
4974                s = "START_WPS";
4975                break;
4976            case WifiManager.START_WPS_SUCCEEDED:
4977                s = "START_WPS_SUCCEEDED";
4978                break;
4979            case WifiManager.WPS_FAILED:
4980                s = "WPS_FAILED";
4981                break;
4982            case WifiManager.WPS_COMPLETED:
4983                s = "WPS_COMPLETED";
4984                break;
4985            case WifiManager.RSSI_PKTCNT_FETCH:
4986                s = "RSSI_PKTCNT_FETCH";
4987                break;
4988            case CMD_IP_CONFIGURATION_LOST:
4989                s = "CMD_IP_CONFIGURATION_LOST";
4990                break;
4991            case CMD_IP_CONFIGURATION_SUCCESSFUL:
4992                s = "CMD_IP_CONFIGURATION_SUCCESSFUL";
4993                break;
4994            case CMD_STATIC_IP_SUCCESS:
4995                s = "CMD_STATIC_IP_SUCCESSFUL";
4996                break;
4997            case CMD_STATIC_IP_FAILURE:
4998                s = "CMD_STATIC_IP_FAILURE";
4999                break;
5000            case DhcpStateMachine.DHCP_SUCCESS:
5001                s = "DHCP_SUCCESS";
5002                break;
5003            case DhcpStateMachine.DHCP_FAILURE:
5004                s = "DHCP_FAILURE";
5005                break;
5006            default:
5007                s = "what:" + Integer.toString(what);
5008                break;
5009        }
5010        return s;
5011    }
5012
5013    void registerConnected() {
5014       if (mLastNetworkId != WifiConfiguration.INVALID_NETWORK_ID) {
5015           long now_ms = System.currentTimeMillis();
5016           // We are switching away from this configuration,
5017           // hence record the time we were connected last
5018           WifiConfiguration config = mWifiConfigStore.getWifiConfiguration(mLastNetworkId);
5019           if (config != null) {
5020               config.lastConnected = System.currentTimeMillis();
5021           }
5022       }
5023    }
5024
5025    void registerDisconnected() {
5026        if (mLastNetworkId != WifiConfiguration.INVALID_NETWORK_ID) {
5027            long now_ms = System.currentTimeMillis();
5028            // We are switching away from this configuration,
5029            // hence record the time we were connected last
5030            WifiConfiguration config = mWifiConfigStore.getWifiConfiguration(mLastNetworkId);
5031            if (config != null) {
5032                config.lastDisconnected = System.currentTimeMillis();
5033            }
5034        }
5035    }
5036
5037    WifiConfiguration getCurrentWifiConfiguration() {
5038        if (mLastNetworkId == WifiConfiguration.INVALID_NETWORK_ID) {
5039            return null;
5040        }
5041        return mWifiConfigStore.getWifiConfiguration(mLastNetworkId);
5042    }
5043
5044    String getCurrentBSSID() {
5045        return mLastBssid;
5046    }
5047
5048    class ConnectModeState extends State {
5049        @Override
5050        public boolean processMessage(Message message) {
5051            WifiConfiguration config;
5052            int netId;
5053            boolean ok;
5054            String bssid;
5055            String ssid;
5056            NetworkUpdateResult result;
5057            logStateAndMessage(message, getClass().getSimpleName());
5058
5059            switch(message.what) {
5060                case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
5061                    didBlackListBSSID = false;
5062                    bssid = (String)message.obj;
5063                    if (bssid == null || TextUtils.isEmpty(bssid)) {
5064                        // If BSSID is null, use the target roam BSSID
5065                        bssid = mTargetRoamBSSID;
5066                    }
5067                    if (bssid != null) {
5068                        // If we have a BSSID, tell configStore to black list it
5069                        didBlackListBSSID = mWifiConfigStore.handleBSSIDBlackList(mLastNetworkId, bssid, false);
5070                    }
5071                    mSupplicantStateTracker.sendMessage(WifiMonitor.ASSOCIATION_REJECTION_EVENT);
5072                    break;
5073                case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
5074                    mSupplicantStateTracker.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT);
5075                    break;
5076                case WifiMonitor.SSID_TEMP_DISABLED:
5077                case WifiMonitor.SSID_REENABLED:
5078                    String substr = (String)message.obj;
5079                    String en = message.what == WifiMonitor.SSID_TEMP_DISABLED ?
5080                             "temp-disabled" : "re-enabled";
5081                    loge("ConnectModeState SSID state=" + en + " nid="
5082                            + Integer.toString(message.arg1) + " [" + substr + "]");
5083                    mWifiConfigStore.handleSSIDStateChange(message.arg1, message.what ==
5084                            WifiMonitor.SSID_REENABLED, substr);
5085                    break;
5086                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
5087                    SupplicantState state = handleSupplicantStateChange(message);
5088                    // A driver/firmware hang can now put the interface in a down state.
5089                    // We detect the interface going down and recover from it
5090                    if (!SupplicantState.isDriverActive(state)) {
5091                        if (mNetworkInfo.getState() != NetworkInfo.State.DISCONNECTED) {
5092                            handleNetworkDisconnect();
5093                        }
5094                        log("Detected an interface down, restart driver");
5095                        transitionTo(mDriverStoppedState);
5096                        sendMessage(CMD_START_DRIVER);
5097                        break;
5098                    }
5099
5100                    // Supplicant can fail to report a NETWORK_DISCONNECTION_EVENT
5101                    // when authentication times out after a successful connection,
5102                    // we can figure this from the supplicant state. If supplicant
5103                    // state is DISCONNECTED, but the mNetworkInfo says we are not
5104                    // disconnected, we need to handle a disconnection
5105                    if (state == SupplicantState.DISCONNECTED &&
5106                            mNetworkInfo.getState() != NetworkInfo.State.DISCONNECTED) {
5107                        if (DBG) log("Missed CTRL-EVENT-DISCONNECTED, disconnect");
5108                        handleNetworkDisconnect();
5109                        transitionTo(mDisconnectedState);
5110                    }
5111                    break;
5112                case WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST:
5113                    if (message.arg1 == 1) {
5114                        mWifiNative.disconnect();
5115                        mTemporarilyDisconnectWifi = true;
5116                    } else {
5117                        mWifiNative.reconnect();
5118                        mTemporarilyDisconnectWifi = false;
5119                    }
5120                    break;
5121                case CMD_ADD_OR_UPDATE_NETWORK:
5122                    config = (WifiConfiguration) message.obj;
5123                    replyToMessage(message, CMD_ADD_OR_UPDATE_NETWORK,
5124                            mWifiConfigStore.addOrUpdateNetwork(config));
5125                    break;
5126                case CMD_REMOVE_NETWORK:
5127                    ok = mWifiConfigStore.removeNetwork(message.arg1);
5128                    replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
5129                    break;
5130                case CMD_ENABLE_NETWORK:
5131                    boolean others = message.arg2 == 1;
5132                    // Tell autojoin the user did try to select to that network
5133                    // However, do NOT persist the choice by bumping the priority of the network
5134                    if (others) {
5135                        mWifiAutoJoinController.
5136                                updateConfigurationHistory(message.arg1, true, false);
5137                        // Set the last selected configuration so as to allow the system to
5138                        // stick the last user choice without persisting the choice
5139                        mWifiConfigStore.setLastSelectedConfiguration(message.arg1);
5140
5141                        // Remember time of last connection attempt
5142                        lastConnectAttempt = System.currentTimeMillis();
5143
5144                        mWifiConnectionStatistics.numWifiManagerJoinAttempt++;
5145                    }
5146                    // Cancel auto roam requests
5147                    autoRoamSetBSSID(message.arg1, "any");
5148
5149                    ok = mWifiConfigStore.enableNetwork(message.arg1, message.arg2 == 1);
5150                    replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
5151                    break;
5152                case CMD_ENABLE_ALL_NETWORKS:
5153                    long time =  android.os.SystemClock.elapsedRealtime();
5154                    if (time - mLastEnableAllNetworksTime > MIN_INTERVAL_ENABLE_ALL_NETWORKS_MS) {
5155                        mWifiConfigStore.enableAllNetworks();
5156                        mLastEnableAllNetworksTime = time;
5157                    }
5158                    break;
5159                case WifiManager.DISABLE_NETWORK:
5160                    if (mWifiConfigStore.disableNetwork(message.arg1,
5161                            WifiConfiguration.DISABLED_UNKNOWN_REASON) == true) {
5162                        replyToMessage(message, WifiManager.DISABLE_NETWORK_SUCCEEDED);
5163                    } else {
5164                        replyToMessage(message, WifiManager.DISABLE_NETWORK_FAILED,
5165                                WifiManager.ERROR);
5166                    }
5167                    break;
5168                case CMD_BLACKLIST_NETWORK:
5169                    mWifiNative.addToBlacklist((String)message.obj);
5170                    break;
5171                case CMD_CLEAR_BLACKLIST:
5172                    mWifiNative.clearBlacklist();
5173                    break;
5174                case CMD_SAVE_CONFIG:
5175                    ok = mWifiConfigStore.saveConfig();
5176
5177                    if (DBG) loge("wifistatemachine did save config " + ok);
5178                    replyToMessage(message, CMD_SAVE_CONFIG, ok ? SUCCESS : FAILURE);
5179
5180                    // Inform the backup manager about a data change
5181                    IBackupManager ibm = IBackupManager.Stub.asInterface(
5182                            ServiceManager.getService(Context.BACKUP_SERVICE));
5183                    if (ibm != null) {
5184                        try {
5185                            ibm.dataChanged("com.android.providers.settings");
5186                        } catch (Exception e) {
5187                            // Try again later
5188                        }
5189                    }
5190                    break;
5191                case CMD_GET_CONFIGURED_NETWORKS:
5192                    replyToMessage(message, message.what,
5193                            mWifiConfigStore.getConfiguredNetworks());
5194                    break;
5195                case WifiMonitor.SUP_REQUEST_IDENTITY:
5196                    // Supplicant lacks credentials to connect to that network, hence black list
5197                    ssid = (String) message.obj;
5198
5199                    if (targetWificonfiguration != null && ssid != null
5200                            && targetWificonfiguration.SSID != null
5201                            && targetWificonfiguration.SSID.equals("\"" + ssid + "\"")) {
5202                        mWifiConfigStore.handleSSIDStateChange(targetWificonfiguration.networkId,
5203                                false, "AUTH_FAILED no identity");
5204                    }
5205                    // Disconnect now, as we don't have any way to fullfill the  supplicant request.
5206                    mWifiConfigStore.setLastSelectedConfiguration
5207                            (WifiConfiguration.INVALID_NETWORK_ID);
5208                    mWifiNative.disconnect();
5209                    break;
5210                case CMD_GET_PRIVILEGED_CONFIGURED_NETWORKS:
5211                    replyToMessage(message, message.what,
5212                            mWifiConfigStore.getPrivilegedConfiguredNetworks());
5213                    break;
5214                    /* Do a redundant disconnect without transition */
5215                case CMD_DISCONNECT:
5216                    mWifiConfigStore.setLastSelectedConfiguration
5217                            (WifiConfiguration.INVALID_NETWORK_ID);
5218                    mWifiNative.disconnect();
5219                    break;
5220                case CMD_RECONNECT:
5221                    lastConnectAttempt = System.currentTimeMillis();
5222                    mWifiNative.reconnect();
5223                    break;
5224                case CMD_REASSOCIATE:
5225                    lastConnectAttempt = System.currentTimeMillis();
5226                    mWifiNative.reassociate();
5227                    break;
5228                case CMD_RELOAD_TLS_AND_RECONNECT:
5229                    if (mWifiConfigStore.needsUnlockedKeyStore()) {
5230                        logd("Reconnecting to give a chance to un-connected TLS networks");
5231                        mWifiNative.disconnect();
5232                        lastConnectAttempt = System.currentTimeMillis();
5233                        mWifiNative.reconnect();
5234                    }
5235                    break;
5236                case CMD_AUTO_ROAM:
5237                    return HANDLED;
5238                case CMD_AUTO_CONNECT:
5239                    /* Work Around: wpa_supplicant can get in a bad state where it returns a non
5240                     * associated status thus the STATUS command but somehow-someplace still thinks
5241                     * it is associated and thus will ignore select/reconnect command with
5242                     * following message:
5243                     * "Already associated with the selected network - do nothing"
5244                     *
5245                     * Hence, sends a disconnect to supplicant first.
5246                     */
5247                    mWifiNative.disconnect();
5248
5249                    /* connect command coming from auto-join */
5250                    config = (WifiConfiguration) message.obj;
5251                    netId = message.arg1;
5252                    int roam = message.arg2;
5253                    loge("CMD_AUTO_CONNECT sup state "
5254                            + mSupplicantStateTracker.getSupplicantStateName()
5255                            + " my state " + getCurrentState().getName()
5256                            + " nid=" + Integer.toString(netId)
5257                            + " roam=" + Integer.toString(roam));
5258                    if (config == null) {
5259                        loge("AUTO_CONNECT and no config, bail out...");
5260                        break;
5261                    }
5262
5263                    /* Make sure we cancel any previous roam request */
5264                    autoRoamSetBSSID(config, "any");
5265
5266                    /* Save the network config */
5267                    loge("CMD_AUTO_CONNECT will save config -> " + config.SSID
5268                                + " nid=" + Integer.toString(netId));
5269                    result = mWifiConfigStore.saveNetwork(config);
5270                    netId = result.getNetworkId();
5271                    loge("CMD_AUTO_CONNECT did save config -> "
5272                                + " nid=" + Integer.toString(netId));
5273
5274                    if (mWifiConfigStore.selectNetwork(netId) &&
5275                            mWifiNative.reconnect()) {
5276                        lastConnectAttempt = System.currentTimeMillis();
5277                        targetWificonfiguration = mWifiConfigStore.getWifiConfiguration(netId);
5278                        // We selected a better config,
5279                        // maybe because we could not see the last user
5280                        // selection, then forget it. We will remember the selection
5281                        // only if it was persisted.
5282                        mWifiConfigStore.
5283                                setLastSelectedConfiguration(WifiConfiguration.INVALID_NETWORK_ID);
5284
5285                        /* The state tracker handles enabling networks upon completion/failure */
5286                        mSupplicantStateTracker.sendMessage(WifiManager.CONNECT_NETWORK);
5287                        //replyToMessage(message, WifiManager.CONNECT_NETWORK_SUCCEEDED);
5288                        mAutoRoaming = roam;
5289                        if (isRoaming()) {
5290                            transitionTo(mRoamingState);
5291                        } else {
5292                            transitionTo(mDisconnectingState);
5293                        }
5294                    } else {
5295                        loge("Failed to connect config: " + config + " netId: " + netId);
5296                        replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
5297                                WifiManager.ERROR);
5298                        break;
5299                    }
5300                    break;
5301                case WifiManager.CONNECT_NETWORK:
5302                    /**
5303                     *  The connect message can contain a network id passed as arg1 on message or
5304                     * or a config passed as obj on message.
5305                     * For a new network, a config is passed to create and connect.
5306                     * For an existing network, a network id is passed
5307                     */
5308                    netId = message.arg1;
5309                    config = (WifiConfiguration) message.obj;
5310                    mWifiConnectionStatistics.numWifiManagerJoinAttempt++;
5311
5312                    if (config == null) {
5313                        loge("CONNECT_NETWORK id=" + Integer.toString(netId) + " "
5314                                + mSupplicantStateTracker.getSupplicantStateName() + " my state "
5315                                + getCurrentState().getName());
5316                    } else {
5317                        loge("CONNECT_NETWORK id=" + Integer.toString(netId)
5318                                + " config=" + config.SSID
5319                                + " cnid=" + config.networkId
5320                                + " supstate=" + mSupplicantStateTracker.getSupplicantStateName()
5321                                + " my state " + getCurrentState().getName());
5322                    }
5323
5324                    autoRoamSetBSSID(netId, "any");
5325                    mAutoRoaming = WifiAutoJoinController.AUTO_JOIN_IDLE;
5326
5327                    /* Save the network config */
5328                    if (config != null) {
5329                        result = mWifiConfigStore.saveNetwork(config);
5330                        netId = result.getNetworkId();
5331                    }
5332                    if (mFrameworkAutoJoin.get()) {
5333                        /* Tell autojoin the user did try to connect to that network */
5334                        mWifiAutoJoinController.updateConfigurationHistory(netId, true, true);
5335                    }
5336                    mWifiConfigStore.setLastSelectedConfiguration(netId);
5337
5338                    if (mLastNetworkId != WifiConfiguration.INVALID_NETWORK_ID
5339                            && mLastNetworkId != netId) {
5340                        /** Supplicant will ignore the reconnect if we are currently associated,
5341                         * hence trigger a disconnect
5342                         */
5343                        mWifiNative.disconnect();
5344                    }
5345
5346                    if (mWifiConfigStore.selectNetwork(netId) &&
5347                            mWifiNative.reconnect()) {
5348                        lastConnectAttempt = System.currentTimeMillis();
5349                        targetWificonfiguration = mWifiConfigStore.getWifiConfiguration(netId);
5350
5351                        /* The state tracker handles enabling networks upon completion/failure */
5352                        mSupplicantStateTracker.sendMessage(WifiManager.CONNECT_NETWORK);
5353                        replyToMessage(message, WifiManager.CONNECT_NETWORK_SUCCEEDED);
5354                        /* Expect a disconnection from the old connection */
5355                        transitionTo(mDisconnectingState);
5356                    } else {
5357                        loge("Failed to connect config: " + config + " netId: " + netId);
5358                        replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
5359                                WifiManager.ERROR);
5360                        break;
5361                    }
5362                    break;
5363                case WifiManager.SAVE_NETWORK:
5364                    config = (WifiConfiguration) message.obj;
5365                    int nid = config.networkId;
5366                    if (config == null) {
5367                        loge("SAVE_NETWORK id=" + Integer.toString(nid)
5368                                + " " + mSupplicantStateTracker.getSupplicantStateName()
5369                                + " my state " + getCurrentState().getName());
5370                    } else {
5371                        loge("SAVE_NETWORK id=" + Integer.toString(nid)
5372                                + " config=" + config.SSID
5373                                + " cnid=" + config.networkId
5374                                + " supstate=" + mSupplicantStateTracker.getSupplicantStateName()
5375                                + " my state " + getCurrentState().getName());
5376                    }
5377                    mWifiConnectionStatistics.numWifiManagerJoinAttempt++;
5378
5379                    result = mWifiConfigStore.saveNetwork(config);
5380                    if (result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID) {
5381                        replyToMessage(message, WifiManager.SAVE_NETWORK_SUCCEEDED);
5382                        broadcastWifiCredentialChanged(WifiManager.WIFI_CREDENTIAL_SAVED, config);
5383                        if (VDBG) {
5384                            loge("Success save network nid="
5385                                    + Integer.toString(result.getNetworkId())
5386                                    + " autojoin " + mFrameworkAutoJoin.get());
5387                        }
5388                        if (mFrameworkAutoJoin.get()) {
5389                            /* Tell autojoin the user did try to modify and save that network,
5390                             * and interpret the SAVE_NETWORK as a request to connect */
5391                            mWifiAutoJoinController.updateConfigurationHistory(result.getNetworkId()
5392                                    ,true, true);
5393                            mWifiAutoJoinController.attemptAutoJoin();
5394                        }
5395                    } else {
5396                        loge("Failed to save network");
5397                        replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED,
5398                                WifiManager.ERROR);
5399                    }
5400                    break;
5401                case WifiManager.FORGET_NETWORK:
5402                    if (mWifiConfigStore.forgetNetwork(message.arg1)) {
5403                        replyToMessage(message, WifiManager.FORGET_NETWORK_SUCCEEDED);
5404                        broadcastWifiCredentialChanged(WifiManager.WIFI_CREDENTIAL_FORGOT,
5405                                (WifiConfiguration) message.obj);
5406                    } else {
5407                        loge("Failed to forget network");
5408                        replyToMessage(message, WifiManager.FORGET_NETWORK_FAILED,
5409                                WifiManager.ERROR);
5410                    }
5411                    break;
5412                case WifiManager.START_WPS:
5413                    WpsInfo wpsInfo = (WpsInfo) message.obj;
5414                    WpsResult wpsResult;
5415                    switch (wpsInfo.setup) {
5416                        case WpsInfo.PBC:
5417                            wpsResult = mWifiConfigStore.startWpsPbc(wpsInfo);
5418                            break;
5419                        case WpsInfo.KEYPAD:
5420                            wpsResult = mWifiConfigStore.startWpsWithPinFromAccessPoint(wpsInfo);
5421                            break;
5422                        case WpsInfo.DISPLAY:
5423                            wpsResult = mWifiConfigStore.startWpsWithPinFromDevice(wpsInfo);
5424                            break;
5425                        default:
5426                            wpsResult = new WpsResult(Status.FAILURE);
5427                            loge("Invalid setup for WPS");
5428                            break;
5429                    }
5430                    mWifiConfigStore.setLastSelectedConfiguration
5431                            (WifiConfiguration.INVALID_NETWORK_ID);
5432                    if (wpsResult.status == Status.SUCCESS) {
5433                        replyToMessage(message, WifiManager.START_WPS_SUCCEEDED, wpsResult);
5434                        transitionTo(mWpsRunningState);
5435                    } else {
5436                        loge("Failed to start WPS with config " + wpsInfo.toString());
5437                        replyToMessage(message, WifiManager.WPS_FAILED, WifiManager.ERROR);
5438                    }
5439                    break;
5440                case WifiMonitor.NETWORK_CONNECTION_EVENT:
5441                    if (DBG) log("Network connection established");
5442                    mLastNetworkId = message.arg1;
5443                    mLastBssid = (String) message.obj;
5444
5445                    mWifiInfo.setBSSID(mLastBssid);
5446                    mWifiInfo.setNetworkId(mLastNetworkId);
5447                    // Send event to CM & network change broadcast
5448                    setNetworkDetailedState(DetailedState.OBTAINING_IPADDR);
5449                    sendNetworkStateChangeBroadcast(mLastBssid);
5450                    transitionTo(mObtainingIpState);
5451                    break;
5452                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
5453                    if (DBG) log("ConnectModeState: Network connection lost ");
5454                    handleNetworkDisconnect();
5455                    transitionTo(mDisconnectedState);
5456                    break;
5457                default:
5458                    return NOT_HANDLED;
5459            }
5460            return HANDLED;
5461        }
5462    }
5463
5464    private class WifiNetworkAgent extends NetworkAgent {
5465        public WifiNetworkAgent(Looper l, Context c, String TAG, NetworkInfo ni,
5466                NetworkCapabilities nc, LinkProperties lp, int score) {
5467            super(l, c, TAG, ni, nc, lp, score);
5468        }
5469        protected void unwanted() {
5470            // Ignore if we're not the current networkAgent.
5471            if (this != mNetworkAgent) return;
5472            // TODO - don't want this network.  What to do?
5473            if (DBG) log("WifiNetworkAgent -> Wifi unwanted score "
5474                    + Integer.toString(mWifiInfo.score));
5475            unwantedNetwork();
5476        }
5477    }
5478
5479    void unwantedNetwork() {
5480        sendMessage(CMD_UNWANTED_NETWORK);
5481    }
5482
5483    class L2ConnectedState extends State {
5484        @Override
5485        public void enter() {
5486            mRssiPollToken++;
5487            if (mEnableRssiPolling) {
5488                sendMessage(CMD_RSSI_POLL, mRssiPollToken, 0);
5489            }
5490            if (mNetworkAgent != null) {
5491                loge("Have NetworkAgent when entering L2Connected");
5492                setNetworkDetailedState(DetailedState.DISCONNECTED);
5493            }
5494            setNetworkDetailedState(DetailedState.CONNECTING);
5495            mNetworkAgent = new WifiNetworkAgent(getHandler().getLooper(), mContext,
5496                    "WifiNetworkAgent", mNetworkInfo, mNetworkCapabilitiesFilter,
5497                    mLinkProperties, 60);
5498        }
5499
5500        @Override
5501        public void exit() {
5502            // This is handled by receiving a NETWORK_DISCONNECTION_EVENT in ConnectModeState
5503            // Bug: 15347363
5504            // For paranoia's sake, call handleNetworkDisconnect only if BSSID is null or last networkId
5505            // is not invalid.
5506            if (mLastBssid != null || mLastNetworkId != WifiConfiguration.INVALID_NETWORK_ID) {
5507                handleNetworkDisconnect();
5508            }
5509        }
5510
5511        @Override
5512        public boolean processMessage(Message message) {
5513            logStateAndMessage(message, getClass().getSimpleName());
5514
5515            switch (message.what) {
5516              case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
5517                  handlePreDhcpSetup();
5518                  break;
5519              case DhcpStateMachine.CMD_POST_DHCP_ACTION:
5520                  handlePostDhcpSetup();
5521                  if (message.arg1 == DhcpStateMachine.DHCP_SUCCESS) {
5522                      if (DBG) log("WifiStateMachine DHCP successful");
5523                      handleIPv4Success((DhcpResults) message.obj, DhcpStateMachine.DHCP_SUCCESS);
5524                      // We advance to mVerifyingLinkState because handleIPv4Success will call
5525                      // updateLinkProperties, which then sends CMD_IP_CONFIGURATION_SUCCESSFUL.
5526                  } else if (message.arg1 == DhcpStateMachine.DHCP_FAILURE) {
5527                      if (DBG) {
5528                          int count = -1;
5529                          WifiConfiguration config = getCurrentWifiConfiguration();
5530                          if (config != null) {
5531                              count = config.numConnectionFailures;
5532                          }
5533                          log("WifiStateMachine DHCP failure count=" + count);
5534                      }
5535                      handleIPv4Failure(DhcpStateMachine.DHCP_FAILURE);
5536                      // As above, we transition to mDisconnectingState via updateLinkProperties.
5537                  }
5538                  break;
5539                case CMD_IP_CONFIGURATION_SUCCESSFUL:
5540                    handleSuccessfulIpConfiguration();
5541                    sendConnectedState();
5542                    transitionTo(mConnectedState);
5543                    break;
5544                case CMD_IP_CONFIGURATION_LOST:
5545                    handleIpConfigurationLost();
5546                    transitionTo(mDisconnectingState);
5547                    break;
5548                case CMD_DISCONNECT:
5549                    mWifiNative.disconnect();
5550                    transitionTo(mDisconnectingState);
5551                    break;
5552                case WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST:
5553                    if (message.arg1 == 1) {
5554                        mWifiNative.disconnect();
5555                        mTemporarilyDisconnectWifi = true;
5556                        transitionTo(mDisconnectingState);
5557                    }
5558                    break;
5559                case CMD_SET_OPERATIONAL_MODE:
5560                    if (message.arg1 != CONNECT_MODE) {
5561                        sendMessage(CMD_DISCONNECT);
5562                        deferMessage(message);
5563                    }
5564                    break;
5565                case CMD_SET_COUNTRY_CODE:
5566                    deferMessage(message);
5567                    break;
5568                case CMD_START_SCAN:
5569                    if (DBG) {
5570                        loge("WifiStateMachine CMD_START_SCAN source " + message.arg1
5571                              + " txSuccessRate="+String.format( "%.2f", mWifiInfo.txSuccessRate)
5572                              + " rxSuccessRate="+String.format( "%.2f", mWifiInfo.rxSuccessRate)
5573                              + " BSSID=" + mTargetRoamBSSID
5574                              + " RSSI=" + mWifiInfo.getRssi());
5575                    }
5576                    if (message.arg1 == SCAN_ALARM_SOURCE) {
5577                        boolean tryFullBandScan = false;
5578                        boolean restrictChannelList = false;
5579                        long now_ms = System.currentTimeMillis();
5580                        if (mWifiInfo != null) {
5581                            if ((now_ms - lastFullBandConnectedTimeMilli)
5582                                    > fullBandConnectedTimeIntervalMilli) {
5583                                if (DBG) {
5584                                    loge("WifiStateMachine CMD_START_SCAN try full band scan age="
5585                                          + Long.toString(now_ms - lastFullBandConnectedTimeMilli)
5586                                          + " interval=" + fullBandConnectedTimeIntervalMilli);
5587                                }
5588                                tryFullBandScan = true;
5589                            }
5590
5591                            if (mWifiInfo.txSuccessRate > 5 || mWifiInfo.rxSuccessRate > 30) {
5592                                // Too much traffic at the interface, hence no full band scan
5593                                if (DBG) {
5594                                    loge("WifiStateMachine CMD_START_SCAN " +
5595                                            "prevent full band scan due to pkt rate");
5596                                }
5597                                tryFullBandScan = false;
5598                            }
5599
5600                            if (mWifiInfo.txSuccessRate > 50 || mWifiInfo.rxSuccessRate > 100) {
5601                                // Don't scan if lots of packets are being sent
5602                                restrictChannelList = true;
5603                                if (mAllowScansWithTraffic == 0) {
5604                                    if (DBG) {
5605                                     loge("WifiStateMachine CMD_START_SCAN source " + message.arg1
5606                                        + " ...and ignore scans"
5607                                        + " tx=" + String.format("%.2f", mWifiInfo.txSuccessRate)
5608                                        + " rx=" + String.format("%.2f", mWifiInfo.rxSuccessRate));
5609                                    }
5610                                    return HANDLED;
5611                                }
5612                            }
5613                        }
5614
5615                        WifiConfiguration currentConfiguration = getCurrentWifiConfiguration();
5616                        if (currentConfiguration != null) {
5617                            if (tryFullBandScan) {
5618                                lastFullBandConnectedTimeMilli = now_ms;
5619                                if (fullBandConnectedTimeIntervalMilli < 20 * 1000) {
5620                                    // Paranoia, make sure interval is not less than 20 seconds
5621                                    fullBandConnectedTimeIntervalMilli = 20 * 1000;
5622                                }
5623                                if (fullBandConnectedTimeIntervalMilli
5624                                        < maxFullBandConnectedTimeIntervalMilli) {
5625                                    // Increase the interval
5626                                    fullBandConnectedTimeIntervalMilli
5627                                            = fullBandConnectedTimeIntervalMilli * 3 / 2;
5628                                }
5629                                handleScanRequest(
5630                                        WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, message);
5631                            } else {
5632                                HashSet<Integer> channels
5633                                        = mWifiConfigStore.makeChannelList(currentConfiguration,
5634                                        ONE_HOUR_MILLI, restrictChannelList);
5635                                if (channels != null && channels.size() != 0) {
5636                                    StringBuilder freqs = new StringBuilder();
5637                                    boolean first = true;
5638                                    for (Integer channel : channels) {
5639                                        if (!first)
5640                                            freqs.append(",");
5641                                        freqs.append(channel.toString());
5642                                        first = false;
5643                                    }
5644                                    if (DBG) {
5645                                        loge("WifiStateMachine starting scan with " + freqs);
5646                                    }
5647                                    // Call wifi native to start the scan
5648                                    if (startScanNative(
5649                                            WifiNative.SCAN_WITHOUT_CONNECTION_SETUP,
5650                                            freqs.toString())) {
5651                                        // Only count battery consumption if scan request is accepted
5652                                        noteScanStart(SCAN_ALARM_SOURCE, null);
5653                                    }
5654                                } else {
5655                                    if (DBG) {
5656                                     loge("WifiStateMachine starting scan, did not find channels");
5657                                    }
5658                                    handleScanRequest(
5659                                            WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, message);
5660                                }
5661                            }
5662                        }
5663                    } else {
5664                        handleScanRequest(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, message);
5665                    }
5666                    break;
5667                    /* Ignore connection to same network */
5668                case WifiManager.CONNECT_NETWORK:
5669                    int netId = message.arg1;
5670                    if (mWifiInfo.getNetworkId() == netId) {
5671                        break;
5672                    }
5673                    return NOT_HANDLED;
5674                case WifiManager.SAVE_NETWORK:
5675                    WifiConfiguration config = (WifiConfiguration) message.obj;
5676                    int nid = config.networkId;
5677                    if (config == null) {
5678                        loge("SAVE_NETWORK-L2 id=" + Integer.toString(nid)
5679                                + " " + mSupplicantStateTracker.getSupplicantStateName()
5680                                + " my state " + getCurrentState().getName());
5681                    } else {
5682                        loge("SAVE_NETWORK-L2 id=" + Integer.toString(nid)
5683                                + " SSID=" + config.SSID
5684                                + " cnid=" + config.networkId
5685                                + " autojoin=" + Integer.toString(config.autoJoinStatus)
5686                                + " supstate=" + mSupplicantStateTracker.getSupplicantStateName()
5687                                + " my state " + getCurrentState().getName()
5688                                + " uid " + Integer.toString(config.creatorUid));
5689                    }
5690
5691                    NetworkUpdateResult result = mWifiConfigStore.saveNetwork(config);
5692                    if (mWifiInfo.getNetworkId() == result.getNetworkId()) {
5693                        if (result.hasIpChanged()) {
5694                            // We switched from DHCP to static or from static to DHCP, or the
5695                            // static IP address has changed.
5696                            log("Reconfiguring IP on connection");
5697                            // TODO: clear addresses and disable IPv6 to simplify obtainingIpState.
5698                            transitionTo(mObtainingIpState);
5699                        }
5700                        if (result.hasProxyChanged()) {
5701                            log("Reconfiguring proxy on connection");
5702                            updateLinkProperties(CMD_UPDATE_LINKPROPERTIES);
5703                        }
5704                    }
5705
5706                    if (result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID) {
5707                        replyToMessage(message, WifiManager.SAVE_NETWORK_SUCCEEDED);
5708                        broadcastWifiCredentialChanged(WifiManager.WIFI_CREDENTIAL_SAVED, config);
5709                        if (VDBG) {
5710                            loge("Success save network l2 nid="
5711                                    + Integer.toString(result.getNetworkId())
5712                                    + " autojoin " + mFrameworkAutoJoin.get());
5713                        }
5714                        if (mFrameworkAutoJoin.get()) {
5715                            /* Tell autojoin the user did try to modify and save that network.
5716                             * and interpret the SAVE network command as a manual request to connect */
5717                            mWifiAutoJoinController.updateConfigurationHistory(config.networkId,
5718                                    true, true);
5719                            mWifiAutoJoinController.attemptAutoJoin();
5720                        }
5721                    } else {
5722                        loge("Failed to save network");
5723                        replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED,
5724                                WifiManager.ERROR);
5725                    }
5726                    break;
5727                    /* Ignore */
5728                case WifiMonitor.NETWORK_CONNECTION_EVENT:
5729                    break;
5730                case CMD_RSSI_POLL:
5731                    if (message.arg1 == mRssiPollToken) {
5732                        WifiLinkLayerStats stats = null;
5733                        // Try a reading L2 stats a couple of time, allow for a few failures
5734                        // in case the HAL/drivers are not completely initialized once we get there
5735                        if (mWifiLinkLayerStatsSupported > 0) {
5736                            stats = mWifiNative.getWifiLinkLayerStats();
5737                            if (stats == null && mWifiLinkLayerStatsSupported > 0) {
5738                                mWifiLinkLayerStatsSupported -= 1;
5739                            }
5740                        }
5741                        // Get Info and continue polling
5742                        fetchRssiLinkSpeedAndFrequencyNative();
5743                        calculateWifiScore(stats);
5744                        sendMessageDelayed(obtainMessage(CMD_RSSI_POLL,
5745                                mRssiPollToken, 0), POLL_RSSI_INTERVAL_MSECS);
5746                    } else {
5747                        // Polling has completed
5748                    }
5749                    break;
5750                case CMD_ENABLE_RSSI_POLL:
5751                    mEnableRssiPolling = (message.arg1 == 1);
5752                    mRssiPollToken++;
5753                    if (mEnableRssiPolling) {
5754                        // First poll
5755                        fetchRssiLinkSpeedAndFrequencyNative();
5756                        sendMessageDelayed(obtainMessage(CMD_RSSI_POLL,
5757                                mRssiPollToken, 0), POLL_RSSI_INTERVAL_MSECS);
5758                    }
5759                    break;
5760                case WifiManager.RSSI_PKTCNT_FETCH:
5761                    RssiPacketCountInfo info = new RssiPacketCountInfo();
5762                    fetchRssiLinkSpeedAndFrequencyNative();
5763                    info.rssi = mWifiInfo.getRssi();
5764                    fetchPktcntNative(info);
5765                    replyToMessage(message, WifiManager.RSSI_PKTCNT_FETCH_SUCCEEDED, info);
5766                    break;
5767                case CMD_UNWANTED_NETWORK:
5768                    // mWifiConfigStore.handleBadNetworkDisconnectReport(mLastNetworkId, mWifiInfo);
5769                    // disconnecting is probably too rough and reduce the chance we recover quickly.
5770                    // we should not have to disconnect, instead rely on network stack to send data
5771                    // traffic somewhere else but remember that this network is roamable with a
5772                    // low wifi score threshold
5773                    sendMessage(CMD_DISCONNECT);
5774                    break;
5775                default:
5776                    return NOT_HANDLED;
5777            }
5778
5779            return HANDLED;
5780        }
5781    }
5782
5783    class ObtainingIpState extends State {
5784        @Override
5785        public void enter() {
5786            if (DBG) {
5787                String key = "";
5788                if (getCurrentWifiConfiguration() != null) {
5789                    key = getCurrentWifiConfiguration().configKey();
5790                }
5791                log("enter ObtainingIpState netId=" + Integer.toString(mLastNetworkId)
5792                        + " " + key + " "
5793                        + " roam=" + mAutoRoaming
5794                        + " static=" + mWifiConfigStore.isUsingStaticIp(mLastNetworkId));
5795            }
5796
5797            try {
5798                mNwService.enableIpv6(mInterfaceName);
5799            } catch (RemoteException re) {
5800                loge("Failed to enable IPv6: " + re);
5801            } catch (IllegalStateException e) {
5802                loge("Failed to enable IPv6: " + e);
5803            }
5804
5805            if (!mWifiConfigStore.isUsingStaticIp(mLastNetworkId)) {
5806                // TODO: If we're switching between static IP configuration and DHCP, remove the
5807                // static configuration first.
5808                if (isRoaming()) {
5809                    renewDhcp();
5810                } else {
5811                    startDhcp();
5812                }
5813            } else {
5814                // stop any running dhcp before assigning static IP
5815                stopDhcp();
5816                DhcpResults dhcpResults = new DhcpResults(
5817                        mWifiConfigStore.getLinkProperties(mLastNetworkId));
5818                InterfaceConfiguration ifcg = new InterfaceConfiguration();
5819                Iterator<LinkAddress> addrs =
5820                        dhcpResults.linkProperties.getLinkAddresses().iterator();
5821                if (!addrs.hasNext()) {
5822                    loge("Static IP lacks address");
5823                    sendMessage(CMD_STATIC_IP_FAILURE);
5824                } else {
5825                    ifcg.setLinkAddress(addrs.next());
5826                    ifcg.setInterfaceUp();
5827                    try {
5828                        mNwService.setInterfaceConfig(mInterfaceName, ifcg);
5829                        if (DBG) log("Static IP configuration succeeded");
5830                        sendMessage(CMD_STATIC_IP_SUCCESS, dhcpResults);
5831                    } catch (RemoteException re) {
5832                        loge("Static IP configuration failed: " + re);
5833                        sendMessage(CMD_STATIC_IP_FAILURE);
5834                    } catch (IllegalStateException e) {
5835                        loge("Static IP configuration failed: " + e);
5836                        sendMessage(CMD_STATIC_IP_FAILURE);
5837                    }
5838                }
5839            }
5840        }
5841      @Override
5842      public boolean processMessage(Message message) {
5843          logStateAndMessage(message, getClass().getSimpleName());
5844
5845          switch(message.what) {
5846            case CMD_STATIC_IP_SUCCESS:
5847                  handleIPv4Success((DhcpResults) message.obj, CMD_STATIC_IP_SUCCESS);
5848                  break;
5849              case CMD_STATIC_IP_FAILURE:
5850                  handleIPv4Failure(CMD_STATIC_IP_FAILURE);
5851                  break;
5852             case WifiManager.SAVE_NETWORK:
5853                  deferMessage(message);
5854                  break;
5855                  /* Defer any power mode changes since we must keep active power mode at DHCP */
5856              case CMD_SET_HIGH_PERF_MODE:
5857                  deferMessage(message);
5858                  break;
5859                  /* Defer scan request since we should not switch to other channels at DHCP */
5860              case CMD_START_SCAN:
5861                  deferMessage(message);
5862                  break;
5863              default:
5864                  return NOT_HANDLED;
5865          }
5866          return HANDLED;
5867      }
5868    }
5869
5870    class VerifyingLinkState extends State {
5871        @Override
5872        public void enter() {
5873            log(getName() + " enter");
5874            setNetworkDetailedState(DetailedState.VERIFYING_POOR_LINK);
5875            mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.VERIFYING_POOR_LINK);
5876            sendNetworkStateChangeBroadcast(mLastBssid);
5877            mAutoRoaming = WifiAutoJoinController.AUTO_JOIN_IDLE;
5878        }
5879        @Override
5880        public boolean processMessage(Message message) {
5881            logStateAndMessage(message, getClass().getSimpleName());
5882
5883            switch (message.what) {
5884                case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
5885                    // Stay here
5886                    log(getName() + " POOR_LINK_DETECTED: no transition");
5887                    break;
5888                case WifiWatchdogStateMachine.GOOD_LINK_DETECTED:
5889                    log(getName() + " GOOD_LINK_DETECTED: transition to captive portal check");
5890
5891                    log(getName() + " GOOD_LINK_DETECTED: transition to CONNECTED");
5892                    sendConnectedState();
5893                    transitionTo(mConnectedState);
5894                    break;
5895                case CMD_START_SCAN:
5896                    deferMessage(message);
5897                    break;
5898                default:
5899                    if (DBG) log(getName() + " what=" + message.what + " NOT_HANDLED");
5900                    return NOT_HANDLED;
5901            }
5902            return HANDLED;
5903        }
5904    }
5905
5906    private void sendConnectedState() {
5907        // Send out a broadcast with the CAPTIVE_PORTAL_CHECK to preserve
5908        // existing behaviour. The captive portal check really happens after we
5909        // transition into DetailedState.CONNECTED.
5910        setNetworkDetailedState(DetailedState.CAPTIVE_PORTAL_CHECK);
5911        mWifiConfigStore.updateStatus(mLastNetworkId,
5912        DetailedState.CAPTIVE_PORTAL_CHECK);
5913        sendNetworkStateChangeBroadcast(mLastBssid);
5914
5915        setNetworkDetailedState(DetailedState.CONNECTED);
5916        mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.CONNECTED);
5917        sendNetworkStateChangeBroadcast(mLastBssid);
5918    }
5919
5920    class RoamingState extends State {
5921        @Override
5922        public void enter() {
5923            if (DBG) {
5924                log("RoamingState Enter"
5925                        + " mScreenOn=" + mScreenOn );
5926            }
5927            setScanAlarm(false);
5928            //TODO: actually implement an alarm, but so as to disconnect if roaming fails
5929        }
5930        @Override
5931        public boolean processMessage(Message message) {
5932            logStateAndMessage(message, getClass().getSimpleName());
5933
5934            switch (message.what) {
5935               case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
5936                    if (DBG) log("Roaming and Watchdog reports poor link -> ignore");
5937                    return HANDLED;
5938               case CMD_UNWANTED_NETWORK:
5939                    if (DBG) log("Roaming and CS doesnt want the network -> ignore");
5940                    return HANDLED;
5941               case CMD_SET_OPERATIONAL_MODE:
5942                    if (message.arg1 != CONNECT_MODE) {
5943                        deferMessage(message);
5944                    }
5945                    break;
5946               case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
5947                    /**
5948                     *  If we get a SUPPLICANT_STATE_CHANGE_EVENT before NETWORK_DISCONNECTION_EVENT
5949                     * we have missed the network disconnection, transition to mDisconnectedState
5950                     * and handle the rest of the events there
5951                     */
5952                    StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
5953                    setNetworkDetailedState(WifiInfo.getDetailedStateOf(stateChangeResult.state));
5954                    break;
5955               case WifiMonitor.NETWORK_CONNECTION_EVENT:
5956                   if (DBG) log("roaming and Network connection established");
5957                   mLastNetworkId = message.arg1;
5958                   mLastBssid = (String) message.obj;
5959
5960                   mWifiInfo.setBSSID(mLastBssid);
5961                   mWifiInfo.setNetworkId(mLastNetworkId);
5962                   mWifiConfigStore.handleBSSIDBlackList(mLastNetworkId, mLastBssid, true);
5963                    /* send event to CM & network change broadcast */
5964                   setNetworkDetailedState(DetailedState.OBTAINING_IPADDR);
5965                   sendNetworkStateChangeBroadcast(mLastBssid);
5966                   transitionTo(mObtainingIpState);
5967                   break;
5968               case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
5969                   // Throw away but only if it correspond to the network we're roaming to
5970                   String bssid = (String)message.obj;
5971                   if (DBG) {
5972                       log("NETWORK_DISCONNECTION_EVENT in roaming state"
5973                               + " BSSID=" + bssid );
5974                   }
5975                   if (bssid != null && bssid.equals(mTargetRoamBSSID)) {
5976                       handleNetworkDisconnect();
5977                   }
5978                   break;
5979                case WifiMonitor.SSID_TEMP_DISABLED:
5980                    // Auth error while roaming
5981                    if (message.arg1 == mLastNetworkId) {
5982                        loge("DISABLED while roaming nid=" + Integer.toString(mLastNetworkId));
5983                        sendMessage(CMD_DISCONNECT);
5984                        transitionTo(mDisconnectingState);
5985                    }
5986                    return NOT_HANDLED;
5987                case CMD_START_SCAN:
5988                    deferMessage(message);
5989                    break;
5990                default:
5991                    return NOT_HANDLED;
5992            }
5993            return HANDLED;
5994        }
5995
5996        @Override
5997        public void exit() {
5998            loge("WifiStateMachine: Leaving Roaming state");
5999        }
6000    }
6001
6002    class ConnectedState extends State {
6003        @Override
6004        public void enter() {
6005            String address;
6006            updateDefaultRouteMacAddress(1000);
6007            if (DBG) {
6008                log("ConnectedState Enter autojoin=" + mFrameworkAutoJoin.get()
6009                        + " mScreenOn=" + mScreenOn
6010                        + " scanperiod=" + Integer.toString(mConnectedScanPeriodMs) );
6011            }
6012            if (mFrameworkAutoJoin.get() && mScreenOn) {
6013                mCurrentScanAlarmMs = mConnectedScanPeriodMs;
6014                setScanAlarm(true);
6015            } else {
6016                mCurrentScanAlarmMs = 0;
6017            }
6018            registerConnected();
6019            lastConnectAttempt = 0;
6020            targetWificonfiguration = null;
6021
6022        }
6023        @Override
6024        public boolean processMessage(Message message) {
6025            logStateAndMessage(message, getClass().getSimpleName());
6026
6027            switch (message.what) {
6028                case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
6029                    if (DBG) log("Watchdog reports poor link");
6030                    transitionTo(mVerifyingLinkState);
6031                    break;
6032                case CMD_UNWANTED_NETWORK:
6033                    mWifiConfigStore.handleBadNetworkDisconnectReport(mLastNetworkId, mWifiInfo);
6034                    // Disconnecting is probably too rough and reduce the chance we recover quickly.
6035                    // we should not have to disconnect, instead rely on network stack to send data
6036                    // traffic somewhere else but remember that this network is roam-able with a
6037                    // low wifi score threshold
6038                    sendMessage(CMD_DISCONNECT);
6039                    return HANDLED;
6040                case CMD_AUTO_ROAM:
6041                    /* This will happen similarly to an Auto_CONNECT, except we specify the BSSID */
6042                    /* Work Around: wpa_supplicant can get in a bad state where it returns a non
6043                     * associated status thus the STATUS command but somehow-someplace still thinks
6044                     * it is associated and thus will ignore select/reconnect command with
6045                     * following message:
6046                     * "Already associated with the selected network - do nothing"
6047                     *
6048                     * Hence, sends a disconnect to supplicant first.
6049                     */
6050                    //mWifiNative.disconnect();
6051
6052                    /* Connect command coming from auto-join */
6053                    ScanResult candidate = (ScanResult)message.obj;
6054                    String bssid = candidate.BSSID;
6055                    int netId = mLastNetworkId;
6056                    WifiConfiguration config = getCurrentWifiConfiguration();
6057
6058                    loge("CMD_AUTO_ROAM sup state "
6059                            + mSupplicantStateTracker.getSupplicantStateName()
6060                            + " my state " + getCurrentState().getName()
6061                            + " nid=" + Integer.toString(netId)
6062                            + " roam=" + Integer.toString(message.arg2)
6063                            + bssid);
6064                    if (config == null) {
6065                        loge("AUTO_ROAM and no config, bail out...");
6066                        break;
6067                    }
6068
6069                    /* Save the BSSID so as to lock it @ firmware */
6070                    autoRoamSetBSSID(config, bssid);
6071
6072                    /* Save the network config */
6073                    loge("CMD_AUTO_ROAM will save config -> " + config.SSID
6074                                + " nid=" + Integer.toString(netId));
6075                    NetworkUpdateResult result = mWifiConfigStore.saveNetwork(config);
6076                    netId = result.getNetworkId();
6077                    loge("CMD_AUTO_ROAM did save config -> "
6078                                + " nid=" + Integer.toString(netId));
6079
6080                    if (mWifiConfigStore.selectNetwork(netId) &&
6081                            mWifiNative.reassociate()) {
6082                        lastConnectAttempt = System.currentTimeMillis();
6083                        targetWificonfiguration = mWifiConfigStore.getWifiConfiguration(netId);
6084
6085                        /* The state tracker handles enabling networks upon completion/failure */
6086                        mSupplicantStateTracker.sendMessage(WifiManager.CONNECT_NETWORK);
6087                        // replyToMessage(message, WifiManager.CONNECT_NETWORK_SUCCEEDED);
6088                        mAutoRoaming = message.arg2;
6089                        transitionTo(mRoamingState);
6090
6091                    } else {
6092                        loge("Failed to connect config: " + config + " netId: " + netId);
6093                        replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
6094                                WifiManager.ERROR);
6095                        break;
6096                    }
6097                    break;
6098                default:
6099                    return NOT_HANDLED;
6100            }
6101            return HANDLED;
6102        }
6103
6104        @Override
6105        public void exit() {
6106            loge("WifiStateMachine: Leaving Connected state");
6107            setScanAlarm(false);
6108        }
6109    }
6110
6111    class DisconnectingState extends State {
6112
6113        @Override
6114        public void enter() {
6115            if (mFrameworkAutoJoin.get()) {
6116                mCurrentScanAlarmMs = mDisconnectedScanPeriodMs;
6117            } else {
6118
6119                mCurrentScanAlarmMs = mFrameworkScanIntervalMs;
6120            }
6121
6122            if (PDBG) {
6123                loge(" Enter DisconnectingState State scan interval " + mFrameworkScanIntervalMs
6124                        + " mEnableBackgroundScan= " + mEnableBackgroundScan
6125                        + " screenOn=" + mScreenOn);
6126            }
6127            if (mScreenOn)
6128                setScanAlarm(true);
6129        }
6130
6131        @Override
6132        public boolean processMessage(Message message) {
6133            logStateAndMessage(message, getClass().getSimpleName());
6134            switch (message.what) {
6135                case CMD_SET_OPERATIONAL_MODE:
6136                    if (message.arg1 != CONNECT_MODE) {
6137                        deferMessage(message);
6138                    }
6139                    break;
6140                case CMD_START_SCAN:
6141                    // Ignore scans while disconnecting
6142                    return HANDLED;
6143                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
6144                    /* If we get a SUPPLICANT_STATE_CHANGE_EVENT before NETWORK_DISCONNECTION_EVENT
6145                     * we have missed the network disconnection, transition to mDisconnectedState
6146                     * and handle the rest of the events there
6147                     */
6148                    deferMessage(message);
6149                    handleNetworkDisconnect();
6150                    transitionTo(mDisconnectedState);
6151                    break;
6152                default:
6153                    return NOT_HANDLED;
6154            }
6155            return HANDLED;
6156        }
6157
6158        @Override
6159        public void exit() {
6160            mCurrentScanAlarmMs = 0;
6161        }
6162    }
6163
6164    class DisconnectedState extends State {
6165        @Override
6166        public void enter() {
6167            // We dont scan frequently if this is a temporary disconnect
6168            // due to p2p
6169            if (mTemporarilyDisconnectWifi) {
6170                mWifiP2pChannel.sendMessage(WifiP2pServiceImpl.DISCONNECT_WIFI_RESPONSE);
6171                return;
6172            }
6173
6174            // Loose the last selection choice
6175            // mWifiAutoJoinController.setLastSelectedConfiguration
6176            // (WifiConfiguration.INVALID_NETWORK_ID);
6177
6178            mFrameworkScanIntervalMs = Settings.Global.getLong(mContext.getContentResolver(),
6179                    Settings.Global.WIFI_FRAMEWORK_SCAN_INTERVAL_MS,
6180                    mDefaultFrameworkScanIntervalMs);
6181
6182            if (mFrameworkAutoJoin.get()) {
6183                if (mScreenOn)
6184                    mCurrentScanAlarmMs = mDisconnectedScanPeriodMs;
6185            } else {
6186                mCurrentScanAlarmMs = mFrameworkScanIntervalMs;
6187            }
6188
6189            if (PDBG) {
6190                loge(" Enter disconnected State scan interval " + mFrameworkScanIntervalMs
6191                        + " mEnableBackgroundScan= " + mEnableBackgroundScan
6192                        + " screenOn=" + mScreenOn);
6193            }
6194
6195            /** clear the roaming state, if we were roaming, we failed */
6196            mAutoRoaming = WifiAutoJoinController.AUTO_JOIN_IDLE;
6197
6198            /**
6199             * mFrameworkAutoJoin is False: We initiate background scanning if it is enabled,
6200             * otherwise we initiate an infrequent scan that wakes up the device to ensure
6201             * a user connects to an access point on the move
6202             *
6203             * mFrameworkAutoJoin is True:
6204             * - screen dark and PNO supported => scan alarm disabled
6205             * - everything else => scan alarm enabled with mDefaultFrameworkScanIntervalMs period
6206             */
6207            if ((mScreenOn == false) && mEnableBackgroundScan) { //mEnableBackgroundScan) {
6208                /* If a regular scan result is pending, do not initiate background
6209                 * scan until the scan results are returned. This is needed because
6210                 * initiating a background scan will cancel the regular scan and
6211                 * scan results will not be returned until background scanning is
6212                 * cleared
6213                 */
6214                if (!mIsScanOngoing) {
6215                    mWifiNative.enableBackgroundScan(true);
6216                }
6217            } else {
6218                setScanAlarm(true);
6219            }
6220
6221            if (mFrameworkAutoJoin.get() && mScreenOn && isScanAllowed()) {
6222                startScanNative(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, null);
6223            }
6224
6225            /**
6226             * If we have no networks saved, the supplicant stops doing the periodic scan.
6227             * The scans are useful to notify the user of the presence of an open network.
6228             * Note that these are not wake up scans.
6229             */
6230            if (!mP2pConnected.get() && mWifiConfigStore.getConfiguredNetworks().size() == 0) {
6231                sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,
6232                        ++mPeriodicScanToken, 0), mSupplicantScanIntervalMs);
6233            }
6234        }
6235        @Override
6236        public boolean processMessage(Message message) {
6237            boolean ret = HANDLED;
6238
6239            logStateAndMessage(message, getClass().getSimpleName());
6240
6241            switch (message.what) {
6242                case CMD_NO_NETWORKS_PERIODIC_SCAN:
6243                    if (mP2pConnected.get()) break;
6244                    if (message.arg1 == mPeriodicScanToken &&
6245                            mWifiConfigStore.getConfiguredNetworks().size() == 0) {
6246                        startScan(UNKNOWN_SCAN_SOURCE, null, null);
6247                        sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,
6248                                    ++mPeriodicScanToken, 0), mSupplicantScanIntervalMs);
6249                    }
6250                    break;
6251                case WifiManager.FORGET_NETWORK:
6252                case CMD_REMOVE_NETWORK:
6253                    // Set up a delayed message here. After the forget/remove is handled
6254                    // the handled delayed message will determine if there is a need to
6255                    // scan and continue
6256                    sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,
6257                                ++mPeriodicScanToken, 0), mSupplicantScanIntervalMs);
6258                    ret = NOT_HANDLED;
6259                    break;
6260                case CMD_SET_OPERATIONAL_MODE:
6261                    if (message.arg1 != CONNECT_MODE) {
6262                        mOperationalMode = message.arg1;
6263
6264                        mWifiConfigStore.disableAllNetworks();
6265                        if (mOperationalMode == SCAN_ONLY_WITH_WIFI_OFF_MODE) {
6266                            mWifiP2pChannel.sendMessage(CMD_DISABLE_P2P_REQ);
6267                            setWifiState(WIFI_STATE_DISABLED);
6268                        }
6269
6270                        transitionTo(mScanModeState);
6271                    }
6272                    break;
6273                case CMD_ENABLE_BACKGROUND_SCAN:
6274                    mEnableBackgroundScan = (message.arg1 == 1);
6275                    loge("enableBackgroundScanCommand enabled=" + mEnableBackgroundScan
6276                            + " suppState:" + mSupplicantStateTracker.getSupplicantStateName());
6277
6278                    if (mEnableBackgroundScan) {
6279                        mWifiNative.enableBackgroundScan(true);
6280                        setScanAlarm(false);
6281                    } else {
6282                        if (mFrameworkAutoJoin.get()) {
6283                            // Tell supplicant to disconnect so as it doesnt start scanning
6284                            // for connection upon disabling background scan
6285                            mWifiNative.disconnect();
6286                        }
6287                        mWifiNative.enableBackgroundScan(false);
6288                        setScanAlarm(true);
6289                    }
6290                    break;
6291                    /* Ignore network disconnect */
6292                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
6293                    break;
6294                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
6295                    StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
6296                    setNetworkDetailedState(WifiInfo.getDetailedStateOf(stateChangeResult.state));
6297                    /* ConnectModeState does the rest of the handling */
6298                    ret = NOT_HANDLED;
6299                    break;
6300                case CMD_START_SCAN:
6301                    if (!isScanAllowed()) {
6302                        // Ignore the scan request
6303                        return HANDLED;
6304                    }
6305                    /* Disable background scan temporarily during a regular scan */
6306                    if (mEnableBackgroundScan) {
6307                        mWifiNative.enableBackgroundScan(false);
6308                    }
6309                    /* Handled in parent state */
6310                    ret = NOT_HANDLED;
6311                    break;
6312                case WifiMonitor.SCAN_RESULTS_EVENT:
6313                    /* Re-enable background scan when a pending scan result is received */
6314                    if (mEnableBackgroundScan && mIsScanOngoing) {
6315                        mWifiNative.enableBackgroundScan(true);
6316                    }
6317                    /* Handled in parent state */
6318                    ret = NOT_HANDLED;
6319                    break;
6320                case WifiP2pServiceImpl.P2P_CONNECTION_CHANGED:
6321                    NetworkInfo info = (NetworkInfo) message.obj;
6322                    mP2pConnected.set(info.isConnected());
6323                    if (mP2pConnected.get()) {
6324                        int defaultInterval = mContext.getResources().getInteger(
6325                                R.integer.config_wifi_scan_interval_p2p_connected);
6326                        long scanIntervalMs = Settings.Global.getLong(mContext.getContentResolver(),
6327                                Settings.Global.WIFI_SCAN_INTERVAL_WHEN_P2P_CONNECTED_MS,
6328                                defaultInterval);
6329                        mWifiNative.setScanInterval((int) scanIntervalMs/1000);
6330                    } else if (mWifiConfigStore.getConfiguredNetworks().size() == 0) {
6331                        if (DBG) log("Turn on scanning after p2p disconnected");
6332                        sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,
6333                                    ++mPeriodicScanToken, 0), mSupplicantScanIntervalMs);
6334                    }
6335                case CMD_RECONNECT:
6336                case CMD_REASSOCIATE:
6337                    if (mTemporarilyDisconnectWifi) {
6338                        // Drop a third party reconnect/reassociate if STA is
6339                        // temporarily disconnected for p2p
6340                        break;
6341                    } else {
6342                        // ConnectModeState handles it
6343                        ret = NOT_HANDLED;
6344                    }
6345                    break;
6346                default:
6347                    ret = NOT_HANDLED;
6348            }
6349            return ret;
6350        }
6351
6352        @Override
6353        public void exit() {
6354            /* No need for a background scan upon exit from a disconnected state */
6355            if (mEnableBackgroundScan) {
6356                mWifiNative.enableBackgroundScan(false);
6357            }
6358            mCurrentScanAlarmMs = 0;
6359            setScanAlarm(false);
6360        }
6361    }
6362
6363    class WpsRunningState extends State {
6364        // Tracks the source to provide a reply
6365        private Message mSourceMessage;
6366        @Override
6367        public void enter() {
6368            mSourceMessage = Message.obtain(getCurrentMessage());
6369        }
6370        @Override
6371        public boolean processMessage(Message message) {
6372            logStateAndMessage(message, getClass().getSimpleName());
6373
6374            switch (message.what) {
6375                case WifiMonitor.WPS_SUCCESS_EVENT:
6376                    // Ignore intermediate success, wait for full connection
6377                    break;
6378                case WifiMonitor.NETWORK_CONNECTION_EVENT:
6379                    replyToMessage(mSourceMessage, WifiManager.WPS_COMPLETED);
6380                    mSourceMessage.recycle();
6381                    mSourceMessage = null;
6382                    deferMessage(message);
6383                    transitionTo(mDisconnectedState);
6384                    break;
6385                case WifiMonitor.WPS_OVERLAP_EVENT:
6386                    replyToMessage(mSourceMessage, WifiManager.WPS_FAILED,
6387                            WifiManager.WPS_OVERLAP_ERROR);
6388                    mSourceMessage.recycle();
6389                    mSourceMessage = null;
6390                    transitionTo(mDisconnectedState);
6391                    break;
6392                case WifiMonitor.WPS_FAIL_EVENT:
6393                    // Arg1 has the reason for the failure
6394                    if ((message.arg1 != WifiManager.ERROR) || (message.arg2 != 0)) {
6395                        replyToMessage(mSourceMessage, WifiManager.WPS_FAILED, message.arg1);
6396                        mSourceMessage.recycle();
6397                        mSourceMessage = null;
6398                        transitionTo(mDisconnectedState);
6399                    } else {
6400                        if (DBG) log("Ignore unspecified fail event during WPS connection");
6401                    }
6402                    break;
6403                case WifiMonitor.WPS_TIMEOUT_EVENT:
6404                    replyToMessage(mSourceMessage, WifiManager.WPS_FAILED,
6405                            WifiManager.WPS_TIMED_OUT);
6406                    mSourceMessage.recycle();
6407                    mSourceMessage = null;
6408                    transitionTo(mDisconnectedState);
6409                    break;
6410                case WifiManager.START_WPS:
6411                    replyToMessage(message, WifiManager.WPS_FAILED, WifiManager.IN_PROGRESS);
6412                    break;
6413                case WifiManager.CANCEL_WPS:
6414                    if (mWifiNative.cancelWps()) {
6415                        replyToMessage(message, WifiManager.CANCEL_WPS_SUCCEDED);
6416                    } else {
6417                        replyToMessage(message, WifiManager.CANCEL_WPS_FAILED, WifiManager.ERROR);
6418                    }
6419                    transitionTo(mDisconnectedState);
6420                    break;
6421                /**
6422                 * Defer all commands that can cause connections to a different network
6423                 * or put the state machine out of connect mode
6424                 */
6425                case CMD_STOP_DRIVER:
6426                case CMD_SET_OPERATIONAL_MODE:
6427                case WifiManager.CONNECT_NETWORK:
6428                case CMD_ENABLE_NETWORK:
6429                case CMD_RECONNECT:
6430                case CMD_REASSOCIATE:
6431                    deferMessage(message);
6432                    break;
6433                case CMD_AUTO_CONNECT:
6434                case CMD_AUTO_ROAM:
6435                case CMD_START_SCAN:
6436                    return HANDLED;
6437                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
6438                    if (DBG) log("Network connection lost");
6439                    handleNetworkDisconnect();
6440                    break;
6441                case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
6442                    if (DBG) log("Ignore Assoc reject event during WPS Connection");
6443                    break;
6444                case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
6445                    // Disregard auth failure events during WPS connection. The
6446                    // EAP sequence is retried several times, and there might be
6447                    // failures (especially for wps pin). We will get a WPS_XXX
6448                    // event at the end of the sequence anyway.
6449                    if (DBG) log("Ignore auth failure during WPS connection");
6450                    break;
6451                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
6452                    // Throw away supplicant state changes when WPS is running.
6453                    // We will start getting supplicant state changes once we get
6454                    // a WPS success or failure
6455                    break;
6456                default:
6457                    return NOT_HANDLED;
6458            }
6459            return HANDLED;
6460        }
6461
6462        @Override
6463        public void exit() {
6464            mWifiConfigStore.enableAllNetworks();
6465            mWifiConfigStore.loadConfiguredNetworks();
6466        }
6467    }
6468
6469    class SoftApStartingState extends State {
6470        @Override
6471        public void enter() {
6472            final Message message = getCurrentMessage();
6473            if (message.what == CMD_START_AP) {
6474                final WifiConfiguration config = (WifiConfiguration) message.obj;
6475
6476                if (config == null) {
6477                    mWifiApConfigChannel.sendMessage(CMD_REQUEST_AP_CONFIG);
6478                } else {
6479                    mWifiApConfigChannel.sendMessage(CMD_SET_AP_CONFIG, config);
6480                    startSoftApWithConfig(config);
6481                }
6482            } else {
6483                throw new RuntimeException("Illegal transition to SoftApStartingState: " + message);
6484            }
6485        }
6486        @Override
6487        public boolean processMessage(Message message) {
6488            logStateAndMessage(message, getClass().getSimpleName());
6489
6490            switch(message.what) {
6491                case CMD_START_SUPPLICANT:
6492                case CMD_STOP_SUPPLICANT:
6493                case CMD_START_AP:
6494                case CMD_STOP_AP:
6495                case CMD_START_DRIVER:
6496                case CMD_STOP_DRIVER:
6497                case CMD_SET_OPERATIONAL_MODE:
6498                case CMD_SET_COUNTRY_CODE:
6499                case CMD_SET_FREQUENCY_BAND:
6500                case CMD_START_PACKET_FILTERING:
6501                case CMD_STOP_PACKET_FILTERING:
6502                case CMD_TETHER_STATE_CHANGE:
6503                    deferMessage(message);
6504                    break;
6505                case WifiStateMachine.CMD_RESPONSE_AP_CONFIG:
6506                    WifiConfiguration config = (WifiConfiguration) message.obj;
6507                    if (config != null) {
6508                        startSoftApWithConfig(config);
6509                    } else {
6510                        loge("Softap config is null!");
6511                        sendMessage(CMD_START_AP_FAILURE);
6512                    }
6513                    break;
6514                case CMD_START_AP_SUCCESS:
6515                    setWifiApState(WIFI_AP_STATE_ENABLED);
6516                    transitionTo(mSoftApStartedState);
6517                    break;
6518                case CMD_START_AP_FAILURE:
6519                    setWifiApState(WIFI_AP_STATE_FAILED);
6520                    transitionTo(mInitialState);
6521                    break;
6522                default:
6523                    return NOT_HANDLED;
6524            }
6525            return HANDLED;
6526        }
6527    }
6528
6529    class SoftApStartedState extends State {
6530        @Override
6531        public boolean processMessage(Message message) {
6532            logStateAndMessage(message, getClass().getSimpleName());
6533
6534            switch(message.what) {
6535                case CMD_STOP_AP:
6536                    if (DBG) log("Stopping Soft AP");
6537                    /* We have not tethered at this point, so we just shutdown soft Ap */
6538                    try {
6539                        mNwService.stopAccessPoint(mInterfaceName);
6540                    } catch(Exception e) {
6541                        loge("Exception in stopAccessPoint()");
6542                    }
6543                    setWifiApState(WIFI_AP_STATE_DISABLED);
6544                    transitionTo(mInitialState);
6545                    break;
6546                case CMD_START_AP:
6547                    // Ignore a start on a running access point
6548                    break;
6549                    // Fail client mode operation when soft AP is enabled
6550                case CMD_START_SUPPLICANT:
6551                    loge("Cannot start supplicant with a running soft AP");
6552                    setWifiState(WIFI_STATE_UNKNOWN);
6553                    break;
6554                case CMD_TETHER_STATE_CHANGE:
6555                    TetherStateChange stateChange = (TetherStateChange) message.obj;
6556                    if (startTethering(stateChange.available)) {
6557                        transitionTo(mTetheringState);
6558                    }
6559                    break;
6560                default:
6561                    return NOT_HANDLED;
6562            }
6563            return HANDLED;
6564        }
6565    }
6566
6567    class TetheringState extends State {
6568        @Override
6569        public void enter() {
6570            /* Send ourselves a delayed message to shut down if tethering fails to notify */
6571            sendMessageDelayed(obtainMessage(CMD_TETHER_NOTIFICATION_TIMED_OUT,
6572                    ++mTetherToken, 0), TETHER_NOTIFICATION_TIME_OUT_MSECS);
6573        }
6574        @Override
6575        public boolean processMessage(Message message) {
6576            logStateAndMessage(message, getClass().getSimpleName());
6577
6578            switch(message.what) {
6579                case CMD_TETHER_STATE_CHANGE:
6580                    TetherStateChange stateChange = (TetherStateChange) message.obj;
6581                    if (isWifiTethered(stateChange.active)) {
6582                        transitionTo(mTetheredState);
6583                    }
6584                    return HANDLED;
6585                case CMD_TETHER_NOTIFICATION_TIMED_OUT:
6586                    if (message.arg1 == mTetherToken) {
6587                        loge("Failed to get tether update, shutdown soft access point");
6588                        transitionTo(mSoftApStartedState);
6589                        // Needs to be first thing handled
6590                        sendMessageAtFrontOfQueue(CMD_STOP_AP);
6591                    }
6592                    break;
6593                case CMD_START_SUPPLICANT:
6594                case CMD_STOP_SUPPLICANT:
6595                case CMD_START_AP:
6596                case CMD_STOP_AP:
6597                case CMD_START_DRIVER:
6598                case CMD_STOP_DRIVER:
6599                case CMD_SET_OPERATIONAL_MODE:
6600                case CMD_SET_COUNTRY_CODE:
6601                case CMD_SET_FREQUENCY_BAND:
6602                case CMD_START_PACKET_FILTERING:
6603                case CMD_STOP_PACKET_FILTERING:
6604                    deferMessage(message);
6605                    break;
6606                default:
6607                    return NOT_HANDLED;
6608            }
6609            return HANDLED;
6610        }
6611    }
6612
6613    class TetheredState extends State {
6614        @Override
6615        public boolean processMessage(Message message) {
6616            logStateAndMessage(message, getClass().getSimpleName());
6617
6618            switch(message.what) {
6619                case CMD_TETHER_STATE_CHANGE:
6620                    TetherStateChange stateChange = (TetherStateChange) message.obj;
6621                    if (!isWifiTethered(stateChange.active)) {
6622                        loge("Tethering reports wifi as untethered!, shut down soft Ap");
6623                        setHostApRunning(null, false);
6624                        setHostApRunning(null, true);
6625                    }
6626                    return HANDLED;
6627                case CMD_STOP_AP:
6628                    if (DBG) log("Untethering before stopping AP");
6629                    setWifiApState(WIFI_AP_STATE_DISABLING);
6630                    stopTethering();
6631                    transitionTo(mUntetheringState);
6632                    // More work to do after untethering
6633                    deferMessage(message);
6634                    break;
6635                default:
6636                    return NOT_HANDLED;
6637            }
6638            return HANDLED;
6639        }
6640    }
6641
6642    class UntetheringState extends State {
6643        @Override
6644        public void enter() {
6645            /* Send ourselves a delayed message to shut down if tethering fails to notify */
6646            sendMessageDelayed(obtainMessage(CMD_TETHER_NOTIFICATION_TIMED_OUT,
6647                    ++mTetherToken, 0), TETHER_NOTIFICATION_TIME_OUT_MSECS);
6648
6649        }
6650        @Override
6651        public boolean processMessage(Message message) {
6652            logStateAndMessage(message, getClass().getSimpleName());
6653
6654            switch(message.what) {
6655                case CMD_TETHER_STATE_CHANGE:
6656                    TetherStateChange stateChange = (TetherStateChange) message.obj;
6657
6658                    /* Wait till wifi is untethered */
6659                    if (isWifiTethered(stateChange.active)) break;
6660
6661                    transitionTo(mSoftApStartedState);
6662                    break;
6663                case CMD_TETHER_NOTIFICATION_TIMED_OUT:
6664                    if (message.arg1 == mTetherToken) {
6665                        loge("Failed to get tether update, force stop access point");
6666                        transitionTo(mSoftApStartedState);
6667                    }
6668                    break;
6669                case CMD_START_SUPPLICANT:
6670                case CMD_STOP_SUPPLICANT:
6671                case CMD_START_AP:
6672                case CMD_STOP_AP:
6673                case CMD_START_DRIVER:
6674                case CMD_STOP_DRIVER:
6675                case CMD_SET_OPERATIONAL_MODE:
6676                case CMD_SET_COUNTRY_CODE:
6677                case CMD_SET_FREQUENCY_BAND:
6678                case CMD_START_PACKET_FILTERING:
6679                case CMD_STOP_PACKET_FILTERING:
6680                    deferMessage(message);
6681                    break;
6682                default:
6683                    return NOT_HANDLED;
6684            }
6685            return HANDLED;
6686        }
6687    }
6688
6689    /**
6690     * State machine initiated requests can have replyTo set to null indicating
6691     * there are no recepients, we ignore those reply actions.
6692     */
6693    private void replyToMessage(Message msg, int what) {
6694        if (msg.replyTo == null) return;
6695        Message dstMsg = obtainMessageWithWhatAndArg2(msg, what);
6696        mReplyChannel.replyToMessage(msg, dstMsg);
6697    }
6698
6699    private void replyToMessage(Message msg, int what, int arg1) {
6700        if (msg.replyTo == null) return;
6701        Message dstMsg = obtainMessageWithWhatAndArg2(msg, what);
6702        dstMsg.arg1 = arg1;
6703        mReplyChannel.replyToMessage(msg, dstMsg);
6704    }
6705
6706    private void replyToMessage(Message msg, int what, Object obj) {
6707        if (msg.replyTo == null) return;
6708        Message dstMsg = obtainMessageWithWhatAndArg2(msg, what);
6709        dstMsg.obj = obj;
6710        mReplyChannel.replyToMessage(msg, dstMsg);
6711    }
6712
6713    /**
6714     * arg2 on the source message has a unique id that needs to be retained in replies
6715     * to match the request
6716     * <p>see WifiManager for details
6717     */
6718    private Message obtainMessageWithWhatAndArg2(Message srcMsg, int what) {
6719        Message msg = Message.obtain();
6720        msg.what = what;
6721        msg.arg2 = srcMsg.arg2;
6722        return msg;
6723    }
6724
6725    /**
6726     * @param wifiCredentialEventType WIFI_CREDENTIAL_SAVED or WIFI_CREDENTIAL_FORGOT
6727     * @param msg Must have a WifiConfiguration obj to succeed
6728     */
6729    private void broadcastWifiCredentialChanged(int wifiCredentialEventType,
6730            WifiConfiguration config) {
6731        if (config != null && config.preSharedKey != null) {
6732            Intent intent = new Intent(WifiManager.WIFI_CREDENTIAL_CHANGED_ACTION);
6733            intent.putExtra(WifiManager.EXTRA_WIFI_CREDENTIAL_SSID, config.SSID);
6734            intent.putExtra(WifiManager.EXTRA_WIFI_CREDENTIAL_EVENT_TYPE,
6735                    wifiCredentialEventType);
6736            mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT,
6737                    android.Manifest.permission.RECEIVE_WIFI_CREDENTIAL_CHANGE);
6738        }
6739    }
6740}
6741