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