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