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