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