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