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