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