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