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