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