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