OSDN Git Service

Fix power issues around wifi scans.
[android-x86/frameworks-base.git] / wifi / java / android / net / wifi / WifiStateMachine.java
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
17 package android.net.wifi;
18
19 import static android.net.wifi.WifiManager.WIFI_STATE_DISABLED;
20 import static android.net.wifi.WifiManager.WIFI_STATE_DISABLING;
21 import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED;
22 import static android.net.wifi.WifiManager.WIFI_STATE_ENABLING;
23 import static android.net.wifi.WifiManager.WIFI_STATE_UNKNOWN;
24
25 /**
26  * TODO:
27  * Deprecate WIFI_STATE_UNKNOWN
28  */
29 import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED;
30 import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLING;
31 import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
32 import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLING;
33 import static android.net.wifi.WifiManager.WIFI_AP_STATE_FAILED;
34
35 import android.app.AlarmManager;
36 import android.app.PendingIntent;
37 import android.app.backup.IBackupManager;
38 import android.bluetooth.BluetoothAdapter;
39 import android.content.BroadcastReceiver;
40 import android.content.Context;
41 import android.content.Intent;
42 import android.content.IntentFilter;
43 import android.content.pm.PackageManager;
44 import android.database.ContentObserver;
45 import android.net.ConnectivityManager;
46 import android.net.DhcpResults;
47 import android.net.DhcpStateMachine;
48 import android.net.InterfaceConfiguration;
49 import android.net.LinkAddress;
50 import android.net.LinkProperties;
51 import android.net.NetworkInfo;
52 import android.net.NetworkInfo.DetailedState;
53 import android.net.NetworkUtils;
54 import android.net.wifi.WpsResult.Status;
55 import android.net.wifi.p2p.WifiP2pManager;
56 import android.net.wifi.p2p.WifiP2pService;
57 import android.os.Binder;
58 import android.os.IBinder;
59 import android.os.INetworkManagementService;
60 import android.os.Message;
61 import android.os.Messenger;
62 import android.os.PowerManager;
63 import android.os.Process;
64 import android.os.RemoteException;
65 import android.os.ServiceManager;
66 import android.os.SystemProperties;
67 import android.os.UserHandle;
68 import android.os.WorkSource;
69 import android.provider.Settings;
70 import android.util.LruCache;
71 import android.text.TextUtils;
72
73 import com.android.internal.R;
74 import com.android.internal.app.IBatteryStats;
75 import com.android.internal.util.AsyncChannel;
76 import com.android.internal.util.Protocol;
77 import com.android.internal.util.State;
78 import com.android.internal.util.StateMachine;
79
80 import java.io.FileDescriptor;
81 import java.io.PrintWriter;
82 import java.net.InetAddress;
83 import java.util.ArrayList;
84 import java.util.List;
85 import java.util.concurrent.atomic.AtomicInteger;
86 import java.util.concurrent.atomic.AtomicBoolean;
87 import java.util.Iterator;
88 import java.util.regex.Pattern;
89
90 /**
91  * Track the state of Wifi connectivity. All event handling is done here,
92  * and all changes in connectivity state are initiated here.
93  *
94  * Wi-Fi now supports three modes of operation: Client, SoftAp and p2p
95  * In the current implementation, we support concurrent wifi p2p and wifi operation.
96  * The WifiStateMachine handles SoftAp and Client operations while WifiP2pService
97  * handles p2p operation.
98  *
99  * @hide
100  */
101 public class WifiStateMachine extends StateMachine {
102
103     private static final String NETWORKTYPE = "WIFI";
104     private static final boolean DBG = false;
105
106     private WifiMonitor mWifiMonitor;
107     private WifiNative mWifiNative;
108     private WifiConfigStore mWifiConfigStore;
109     private INetworkManagementService mNwService;
110     private ConnectivityManager mCm;
111
112     private final boolean mP2pSupported;
113     private final AtomicBoolean mP2pConnected = new AtomicBoolean(false);
114     private boolean mTemporarilyDisconnectWifi = false;
115     private final String mPrimaryDeviceType;
116
117     /* Scan results handling */
118     private List<ScanResult> mScanResults = new ArrayList<ScanResult>();
119     private static final Pattern scanResultPattern = Pattern.compile("\t+");
120     private static final int SCAN_RESULT_CACHE_SIZE = 80;
121     private final LruCache<String, ScanResult> mScanResultCache;
122
123     /* Chipset supports background scan */
124     private final boolean mBackgroundScanSupported;
125
126     private String mInterfaceName;
127     /* Tethering interface could be separate from wlan interface */
128     private String mTetherInterfaceName;
129
130     private int mLastSignalLevel = -1;
131     private String mLastBssid;
132     private int mLastNetworkId;
133     private boolean mEnableRssiPolling = false;
134     private boolean mEnableBackgroundScan = false;
135     private int mRssiPollToken = 0;
136     private int mReconnectCount = 0;
137     /* 3 operational states for STA operation: CONNECT_MODE, SCAN_ONLY_MODE, SCAN_ONLY_WIFI_OFF_MODE
138     * In CONNECT_MODE, the STA can scan and connect to an access point
139     * In SCAN_ONLY_MODE, the STA can only scan for access points
140     * In SCAN_ONLY_WIFI_OFF_MODE, the STA can only scan for access points with wifi toggle being off
141     */
142     private int mOperationalMode = CONNECT_MODE;
143     private boolean mScanResultIsPending = false;
144     private WorkSource mScanWorkSource = null;
145     private static final int UNKNOWN_SCAN_SOURCE = -1;
146     /* Tracks if state machine has received any screen state change broadcast yet.
147      * We can miss one of these at boot.
148      */
149     private AtomicBoolean mScreenBroadcastReceived = new AtomicBoolean(false);
150
151     private boolean mBluetoothConnectionActive = false;
152
153     private PowerManager.WakeLock mSuspendWakeLock;
154
155     /**
156      * Interval in milliseconds between polling for RSSI
157      * and linkspeed information
158      */
159     private static final int POLL_RSSI_INTERVAL_MSECS = 3000;
160
161     /**
162      * Delay between supplicant restarts upon failure to establish connection
163      */
164     private static final int SUPPLICANT_RESTART_INTERVAL_MSECS = 5000;
165
166     /**
167      * Number of times we attempt to restart supplicant
168      */
169     private static final int SUPPLICANT_RESTART_TRIES = 5;
170
171     private int mSupplicantRestartCount = 0;
172     /* Tracks sequence number on stop failure message */
173     private int mSupplicantStopFailureToken = 0;
174
175     /**
176      * Tether state change notification time out
177      */
178     private static final int TETHER_NOTIFICATION_TIME_OUT_MSECS = 5000;
179
180     /* Tracks sequence number on a tether notification time out */
181     private int mTetherToken = 0;
182
183     /**
184      * Driver start time out.
185      */
186     private static final int DRIVER_START_TIME_OUT_MSECS = 10000;
187
188     /* Tracks sequence number on a driver time out */
189     private int mDriverStartToken = 0;
190
191     private LinkProperties mLinkProperties;
192
193     /* Tracks sequence number on a periodic scan message */
194     private int mPeriodicScanToken = 0;
195
196     // Wakelock held during wifi start/stop and driver load/unload
197     private PowerManager.WakeLock mWakeLock;
198
199     private Context mContext;
200
201     private final Object mDhcpResultsLock = new Object();
202     private DhcpResults mDhcpResults;
203     private WifiInfo mWifiInfo;
204     private NetworkInfo mNetworkInfo;
205     private SupplicantStateTracker mSupplicantStateTracker;
206     private DhcpStateMachine mDhcpStateMachine;
207
208     private AlarmManager mAlarmManager;
209     private PendingIntent mScanIntent;
210     private PendingIntent mDriverStopIntent;
211
212     /* Tracks current frequency mode */
213     private AtomicInteger mFrequencyBand = new AtomicInteger(WifiManager.WIFI_FREQUENCY_BAND_AUTO);
214
215     /* Tracks if we are filtering Multicast v4 packets. Default is to filter. */
216     private AtomicBoolean mFilteringMulticastV4Packets = new AtomicBoolean(true);
217
218     // Channel for sending replies.
219     private AsyncChannel mReplyChannel = new AsyncChannel();
220
221     private WifiP2pManager mWifiP2pManager;
222     //Used to initiate a connection with WifiP2pService
223     private AsyncChannel mWifiP2pChannel;
224     private AsyncChannel mWifiApConfigChannel;
225
226     /* The base for wifi message types */
227     static final int BASE = Protocol.BASE_WIFI;
228     /* Start the supplicant */
229     static final int CMD_START_SUPPLICANT                 = BASE + 11;
230     /* Stop the supplicant */
231     static final int CMD_STOP_SUPPLICANT                  = BASE + 12;
232     /* Start the driver */
233     static final int CMD_START_DRIVER                     = BASE + 13;
234     /* Stop the driver */
235     static final int CMD_STOP_DRIVER                      = BASE + 14;
236     /* Indicates Static IP succeeded */
237     static final int CMD_STATIC_IP_SUCCESS                = BASE + 15;
238     /* Indicates Static IP failed */
239     static final int CMD_STATIC_IP_FAILURE                = BASE + 16;
240     /* Indicates supplicant stop failed */
241     static final int CMD_STOP_SUPPLICANT_FAILED           = BASE + 17;
242     /* Delayed stop to avoid shutting down driver too quick*/
243     static final int CMD_DELAYED_STOP_DRIVER              = BASE + 18;
244     /* A delayed message sent to start driver when it fail to come up */
245     static final int CMD_DRIVER_START_TIMED_OUT           = BASE + 19;
246     /* Ready to switch to network as default */
247     static final int CMD_CAPTIVE_CHECK_COMPLETE           = BASE + 20;
248
249     /* Start the soft access point */
250     static final int CMD_START_AP                         = BASE + 21;
251     /* Indicates soft ap start succeeded */
252     static final int CMD_START_AP_SUCCESS                 = BASE + 22;
253     /* Indicates soft ap start failed */
254     static final int CMD_START_AP_FAILURE                 = BASE + 23;
255     /* Stop the soft access point */
256     static final int CMD_STOP_AP                          = BASE + 24;
257     /* Set the soft access point configuration */
258     static final int CMD_SET_AP_CONFIG                    = BASE + 25;
259     /* Soft access point configuration set completed */
260     static final int CMD_SET_AP_CONFIG_COMPLETED          = BASE + 26;
261     /* Request the soft access point configuration */
262     static final int CMD_REQUEST_AP_CONFIG                = BASE + 27;
263     /* Response to access point configuration request */
264     static final int CMD_RESPONSE_AP_CONFIG               = BASE + 28;
265     /* Invoked when getting a tether state change notification */
266     static final int CMD_TETHER_STATE_CHANGE              = BASE + 29;
267     /* A delayed message sent to indicate tether state change failed to arrive */
268     static final int CMD_TETHER_NOTIFICATION_TIMED_OUT    = BASE + 30;
269
270     static final int CMD_BLUETOOTH_ADAPTER_STATE_CHANGE   = BASE + 31;
271
272     /* Supplicant commands */
273     /* Is supplicant alive ? */
274     static final int CMD_PING_SUPPLICANT                  = BASE + 51;
275     /* Add/update a network configuration */
276     static final int CMD_ADD_OR_UPDATE_NETWORK            = BASE + 52;
277     /* Delete a network */
278     static final int CMD_REMOVE_NETWORK                   = BASE + 53;
279     /* Enable a network. The device will attempt a connection to the given network. */
280     static final int CMD_ENABLE_NETWORK                   = BASE + 54;
281     /* Enable all networks */
282     static final int CMD_ENABLE_ALL_NETWORKS              = BASE + 55;
283     /* Blacklist network. De-prioritizes the given BSSID for connection. */
284     static final int CMD_BLACKLIST_NETWORK                = BASE + 56;
285     /* Clear the blacklist network list */
286     static final int CMD_CLEAR_BLACKLIST                  = BASE + 57;
287     /* Save configuration */
288     static final int CMD_SAVE_CONFIG                      = BASE + 58;
289     /* Get configured networks*/
290     static final int CMD_GET_CONFIGURED_NETWORKS          = BASE + 59;
291
292     /* Supplicant commands after driver start*/
293     /* Initiate a scan */
294     static final int CMD_START_SCAN                       = BASE + 71;
295     /* Set operational mode. CONNECT, SCAN ONLY, SCAN_ONLY with Wi-Fi off mode */
296     static final int CMD_SET_OPERATIONAL_MODE             = BASE + 72;
297     /* Disconnect from a network */
298     static final int CMD_DISCONNECT                       = BASE + 73;
299     /* Reconnect to a network */
300     static final int CMD_RECONNECT                        = BASE + 74;
301     /* Reassociate to a network */
302     static final int CMD_REASSOCIATE                      = BASE + 75;
303     /* Controls suspend mode optimizations
304      *
305      * When high perf mode is enabled, suspend mode optimizations are disabled
306      *
307      * When high perf mode is disabled, suspend mode optimizations are enabled
308      *
309      * Suspend mode optimizations include:
310      * - packet filtering
311      * - turn off roaming
312      * - DTIM wake up settings
313      */
314     static final int CMD_SET_HIGH_PERF_MODE               = BASE + 77;
315     /* Set the country code */
316     static final int CMD_SET_COUNTRY_CODE                 = BASE + 80;
317     /* Enables RSSI poll */
318     static final int CMD_ENABLE_RSSI_POLL                 = BASE + 82;
319     /* RSSI poll */
320     static final int CMD_RSSI_POLL                        = BASE + 83;
321     /* Set up packet filtering */
322     static final int CMD_START_PACKET_FILTERING           = BASE + 84;
323     /* Clear packet filter */
324     static final int CMD_STOP_PACKET_FILTERING            = BASE + 85;
325     /* Enable suspend mode optimizations in the driver */
326     static final int CMD_SET_SUSPEND_OPT_ENABLED          = BASE + 86;
327     /* When there are no saved networks, we do a periodic scan to notify user of
328      * an open network */
329     static final int CMD_NO_NETWORKS_PERIODIC_SCAN        = BASE + 88;
330
331     /* arg1 values to CMD_STOP_PACKET_FILTERING and CMD_START_PACKET_FILTERING */
332     static final int MULTICAST_V6  = 1;
333     static final int MULTICAST_V4  = 0;
334
335    /* Set the frequency band */
336     static final int CMD_SET_FREQUENCY_BAND               = BASE + 90;
337     /* Enable background scan for configured networks */
338     static final int CMD_ENABLE_BACKGROUND_SCAN           = BASE + 91;
339
340     /* Commands from/to the SupplicantStateTracker */
341     /* Reset the supplicant state tracker */
342     static final int CMD_RESET_SUPPLICANT_STATE           = BASE + 111;
343
344     /* P2p commands */
345     /* We are ok with no response here since we wont do much with it anyway */
346     public static final int CMD_ENABLE_P2P                = BASE + 131;
347     /* In order to shut down supplicant cleanly, we wait till p2p has
348      * been disabled */
349     public static final int CMD_DISABLE_P2P_REQ           = BASE + 132;
350     public static final int CMD_DISABLE_P2P_RSP           = BASE + 133;
351
352     public static final int CMD_BOOT_COMPLETED            = BASE + 134;
353
354     public static final int CONNECT_MODE                   = 1;
355     public static final int SCAN_ONLY_MODE                 = 2;
356     public static final int SCAN_ONLY_WITH_WIFI_OFF_MODE   = 3;
357
358     private static final int SUCCESS = 1;
359     private static final int FAILURE = -1;
360
361     /**
362      * The maximum number of times we will retry a connection to an access point
363      * for which we have failed in acquiring an IP address from DHCP. A value of
364      * N means that we will make N+1 connection attempts in all.
365      * <p>
366      * See {@link Settings.Secure#WIFI_MAX_DHCP_RETRY_COUNT}. This is the default
367      * value if a Settings value is not present.
368      */
369     private static final int DEFAULT_MAX_DHCP_RETRIES = 9;
370
371     /* Tracks if suspend optimizations need to be disabled by DHCP,
372      * screen or due to high perf mode.
373      * When any of them needs to disable it, we keep the suspend optimizations
374      * disabled
375      */
376     private int mSuspendOptNeedsDisabled = 0;
377
378     private static final int SUSPEND_DUE_TO_DHCP       = 1;
379     private static final int SUSPEND_DUE_TO_HIGH_PERF  = 1<<1;
380     private static final int SUSPEND_DUE_TO_SCREEN     = 1<<2;
381
382     /* Tracks if user has enabled suspend optimizations through settings */
383     private AtomicBoolean mUserWantsSuspendOpt = new AtomicBoolean(true);
384
385     /**
386      * Default framework scan interval in milliseconds. This is used in the scenario in which
387      * wifi chipset does not support background scanning to set up a
388      * periodic wake up scan so that the device can connect to a new access
389      * point on the move. {@link Settings.Global#WIFI_FRAMEWORK_SCAN_INTERVAL_MS} can
390      * override this.
391      */
392     private final int mDefaultFrameworkScanIntervalMs;
393
394     /**
395      * Supplicant scan interval in milliseconds.
396      * Comes from {@link Settings.Global#WIFI_SUPPLICANT_SCAN_INTERVAL_MS} or
397      * from the default config if the setting is not set
398      */
399     private long mSupplicantScanIntervalMs;
400
401     /**
402      * Minimum time interval between enabling all networks.
403      * A device can end up repeatedly connecting to a bad network on screen on/off toggle
404      * due to enabling every time. We add a threshold to avoid this.
405      */
406     private static final int MIN_INTERVAL_ENABLE_ALL_NETWORKS_MS = 10 * 60 * 1000; /* 10 minutes */
407     private long mLastEnableAllNetworksTime;
408
409     /**
410      * Starting and shutting down driver too quick causes problems leading to driver
411      * being in a bad state. Delay driver stop.
412      */
413     private final int mDriverStopDelayMs;
414     private int mDelayedStopCounter;
415     private boolean mInDelayedStop = false;
416
417     // sometimes telephony gives us this data before boot is complete and we can't store it
418     // until after, so the write is deferred
419     private volatile String mPersistedCountryCode;
420
421     private static final int MIN_RSSI = -200;
422     private static final int MAX_RSSI = 256;
423
424     /* Default parent state */
425     private State mDefaultState = new DefaultState();
426     /* Temporary initial state */
427     private State mInitialState = new InitialState();
428     /* Driver loaded, waiting for supplicant to start */
429     private State mSupplicantStartingState = new SupplicantStartingState();
430     /* Driver loaded and supplicant ready */
431     private State mSupplicantStartedState = new SupplicantStartedState();
432     /* Waiting for supplicant to stop and monitor to exit */
433     private State mSupplicantStoppingState = new SupplicantStoppingState();
434     /* Driver start issued, waiting for completed event */
435     private State mDriverStartingState = new DriverStartingState();
436     /* Driver started */
437     private State mDriverStartedState = new DriverStartedState();
438     /* Wait until p2p is disabled
439      * This is a special state which is entered right after we exit out of DriverStartedState
440      * before transitioning to another state.
441      */
442     private State mWaitForP2pDisableState = new WaitForP2pDisableState();
443     /* Driver stopping */
444     private State mDriverStoppingState = new DriverStoppingState();
445     /* Driver stopped */
446     private State mDriverStoppedState = new DriverStoppedState();
447     /* Scan for networks, no connection will be established */
448     private State mScanModeState = new ScanModeState();
449     /* Connecting to an access point */
450     private State mConnectModeState = new ConnectModeState();
451     /* Connected at 802.11 (L2) level */
452     private State mL2ConnectedState = new L2ConnectedState();
453     /* fetching IP after connection to access point (assoc+auth complete) */
454     private State mObtainingIpState = new ObtainingIpState();
455     /* Waiting for link quality verification to be complete */
456     private State mVerifyingLinkState = new VerifyingLinkState();
457     /* Waiting for captive portal check to be complete */
458     private State mCaptivePortalCheckState = new CaptivePortalCheckState();
459     /* Connected with IP addr */
460     private State mConnectedState = new ConnectedState();
461     /* disconnect issued, waiting for network disconnect confirmation */
462     private State mDisconnectingState = new DisconnectingState();
463     /* Network is not connected, supplicant assoc+auth is not complete */
464     private State mDisconnectedState = new DisconnectedState();
465     /* Waiting for WPS to be completed*/
466     private State mWpsRunningState = new WpsRunningState();
467
468     /* Soft ap is starting up */
469     private State mSoftApStartingState = new SoftApStartingState();
470     /* Soft ap is running */
471     private State mSoftApStartedState = new SoftApStartedState();
472     /* Soft ap is running and we are waiting for tether notification */
473     private State mTetheringState = new TetheringState();
474     /* Soft ap is running and we are tethered through connectivity service */
475     private State mTetheredState = new TetheredState();
476     /* Waiting for untether confirmation before stopping soft Ap */
477     private State mUntetheringState = new UntetheringState();
478
479     private class TetherStateChange {
480         ArrayList<String> available;
481         ArrayList<String> active;
482         TetherStateChange(ArrayList<String> av, ArrayList<String> ac) {
483             available = av;
484             active = ac;
485         }
486     }
487
488
489     /**
490      * One of  {@link WifiManager#WIFI_STATE_DISABLED},
491      *         {@link WifiManager#WIFI_STATE_DISABLING},
492      *         {@link WifiManager#WIFI_STATE_ENABLED},
493      *         {@link WifiManager#WIFI_STATE_ENABLING},
494      *         {@link WifiManager#WIFI_STATE_UNKNOWN}
495      *
496      */
497     private final AtomicInteger mWifiState = new AtomicInteger(WIFI_STATE_DISABLED);
498
499     /**
500      * One of  {@link WifiManager#WIFI_AP_STATE_DISABLED},
501      *         {@link WifiManager#WIFI_AP_STATE_DISABLING},
502      *         {@link WifiManager#WIFI_AP_STATE_ENABLED},
503      *         {@link WifiManager#WIFI_AP_STATE_ENABLING},
504      *         {@link WifiManager#WIFI_AP_STATE_FAILED}
505      *
506      */
507     private final AtomicInteger mWifiApState = new AtomicInteger(WIFI_AP_STATE_DISABLED);
508
509     private static final int SCAN_REQUEST = 0;
510     private static final String ACTION_START_SCAN =
511         "com.android.server.WifiManager.action.START_SCAN";
512
513     private static final String DELAYED_STOP_COUNTER = "DelayedStopCounter";
514     private static final int DRIVER_STOP_REQUEST = 0;
515     private static final String ACTION_DELAYED_DRIVER_STOP =
516         "com.android.server.WifiManager.action.DELAYED_DRIVER_STOP";
517
518     /**
519      * Keep track of whether WIFI is running.
520      */
521     private boolean mIsRunning = false;
522
523     /**
524      * Keep track of whether we last told the battery stats we had started.
525      */
526     private boolean mReportedRunning = false;
527
528     /**
529      * Most recently set source of starting WIFI.
530      */
531     private final WorkSource mRunningWifiUids = new WorkSource();
532
533     /**
534      * The last reported UIDs that were responsible for starting WIFI.
535      */
536     private final WorkSource mLastRunningWifiUids = new WorkSource();
537
538     private final IBatteryStats mBatteryStats;
539
540     public WifiStateMachine(Context context, String wlanInterface) {
541         super("WifiStateMachine");
542
543         mContext = context;
544         mInterfaceName = wlanInterface;
545
546         mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, NETWORKTYPE, "");
547         mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo"));
548
549         IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
550         mNwService = INetworkManagementService.Stub.asInterface(b);
551
552         mP2pSupported = mContext.getPackageManager().hasSystemFeature(
553                 PackageManager.FEATURE_WIFI_DIRECT);
554
555         mWifiNative = new WifiNative(mInterfaceName);
556         mWifiConfigStore = new WifiConfigStore(context, mWifiNative);
557         mWifiMonitor = new WifiMonitor(this, mWifiNative);
558         mWifiInfo = new WifiInfo();
559         mSupplicantStateTracker = new SupplicantStateTracker(context, this, mWifiConfigStore,
560                 getHandler());
561         mLinkProperties = new LinkProperties();
562
563         mWifiP2pManager = (WifiP2pManager) mContext.getSystemService(Context.WIFI_P2P_SERVICE);
564
565         mNetworkInfo.setIsAvailable(false);
566         mLinkProperties.clear();
567         mLastBssid = null;
568         mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
569         mLastSignalLevel = -1;
570
571         mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
572         Intent scanIntent = new Intent(ACTION_START_SCAN, null);
573         mScanIntent = PendingIntent.getBroadcast(mContext, SCAN_REQUEST, scanIntent, 0);
574
575         mDefaultFrameworkScanIntervalMs = mContext.getResources().getInteger(
576                 R.integer.config_wifi_framework_scan_interval);
577
578         mDriverStopDelayMs = mContext.getResources().getInteger(
579                 R.integer.config_wifi_driver_stop_delay);
580
581         mBackgroundScanSupported = mContext.getResources().getBoolean(
582                 R.bool.config_wifi_background_scan_support);
583
584         mPrimaryDeviceType = mContext.getResources().getString(
585                 R.string.config_wifi_p2p_device_type);
586
587         mUserWantsSuspendOpt.set(Settings.Global.getInt(mContext.getContentResolver(),
588                     Settings.Global.WIFI_SUSPEND_OPTIMIZATIONS_ENABLED, 1) == 1);
589
590         mContext.registerReceiver(
591             new BroadcastReceiver() {
592                 @Override
593                 public void onReceive(Context context, Intent intent) {
594                     ArrayList<String> available = intent.getStringArrayListExtra(
595                             ConnectivityManager.EXTRA_AVAILABLE_TETHER);
596                     ArrayList<String> active = intent.getStringArrayListExtra(
597                             ConnectivityManager.EXTRA_ACTIVE_TETHER);
598                     sendMessage(CMD_TETHER_STATE_CHANGE, new TetherStateChange(available, active));
599                 }
600             },new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED));
601
602         mContext.registerReceiver(
603                 new BroadcastReceiver() {
604                     @Override
605                     public void onReceive(Context context, Intent intent) {
606                         startScan(UNKNOWN_SCAN_SOURCE);
607                     }
608                 },
609                 new IntentFilter(ACTION_START_SCAN));
610
611         IntentFilter screenFilter = new IntentFilter();
612         screenFilter.addAction(Intent.ACTION_SCREEN_ON);
613         screenFilter.addAction(Intent.ACTION_SCREEN_OFF);
614         BroadcastReceiver screenReceiver = new BroadcastReceiver() {
615             @Override
616             public void onReceive(Context context, Intent intent) {
617                 String action = intent.getAction();
618
619                 if (action.equals(Intent.ACTION_SCREEN_ON)) {
620                     handleScreenStateChanged(true);
621                 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
622                     handleScreenStateChanged(false);
623                 }
624             }
625         };
626         mContext.registerReceiver(screenReceiver, screenFilter);
627
628         mContext.registerReceiver(
629                 new BroadcastReceiver() {
630                     @Override
631                     public void onReceive(Context context, Intent intent) {
632                        int counter = intent.getIntExtra(DELAYED_STOP_COUNTER, 0);
633                        sendMessage(CMD_DELAYED_STOP_DRIVER, counter, 0);
634                     }
635                 },
636                 new IntentFilter(ACTION_DELAYED_DRIVER_STOP));
637
638         mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor(
639                 Settings.Global.WIFI_SUSPEND_OPTIMIZATIONS_ENABLED), false,
640                 new ContentObserver(getHandler()) {
641                     @Override
642                     public void onChange(boolean selfChange) {
643                         mUserWantsSuspendOpt.set(Settings.Global.getInt(mContext.getContentResolver(),
644                                 Settings.Global.WIFI_SUSPEND_OPTIMIZATIONS_ENABLED, 1) == 1);
645                     }
646                 });
647
648         mContext.registerReceiver(
649                 new BroadcastReceiver() {
650                     @Override
651                     public void onReceive(Context context, Intent intent) {
652                         sendMessage(CMD_BOOT_COMPLETED);
653                     }
654                 },
655                 new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
656
657         mScanResultCache = new LruCache<String, ScanResult>(SCAN_RESULT_CACHE_SIZE);
658
659         PowerManager powerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
660         mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, getName());
661
662         mSuspendWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "WifiSuspend");
663         mSuspendWakeLock.setReferenceCounted(false);
664
665         addState(mDefaultState);
666             addState(mInitialState, mDefaultState);
667             addState(mSupplicantStartingState, mDefaultState);
668             addState(mSupplicantStartedState, mDefaultState);
669                 addState(mDriverStartingState, mSupplicantStartedState);
670                 addState(mDriverStartedState, mSupplicantStartedState);
671                     addState(mScanModeState, mDriverStartedState);
672                     addState(mConnectModeState, mDriverStartedState);
673                         addState(mL2ConnectedState, mConnectModeState);
674                             addState(mObtainingIpState, mL2ConnectedState);
675                             addState(mVerifyingLinkState, mL2ConnectedState);
676                             addState(mCaptivePortalCheckState, mL2ConnectedState);
677                             addState(mConnectedState, mL2ConnectedState);
678                         addState(mDisconnectingState, mConnectModeState);
679                         addState(mDisconnectedState, mConnectModeState);
680                         addState(mWpsRunningState, mConnectModeState);
681                 addState(mWaitForP2pDisableState, mSupplicantStartedState);
682                 addState(mDriverStoppingState, mSupplicantStartedState);
683                 addState(mDriverStoppedState, mSupplicantStartedState);
684             addState(mSupplicantStoppingState, mDefaultState);
685             addState(mSoftApStartingState, mDefaultState);
686             addState(mSoftApStartedState, mDefaultState);
687                 addState(mTetheringState, mSoftApStartedState);
688                 addState(mTetheredState, mSoftApStartedState);
689                 addState(mUntetheringState, mSoftApStartedState);
690
691         setInitialState(mInitialState);
692
693         setLogRecSize(300);
694         setLogOnlyTransitions(false);
695         if (DBG) setDbg(true);
696
697         //start the state machine
698         start();
699
700         final Intent intent = new Intent(WifiManager.WIFI_SCAN_AVAILABLE);
701         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
702         intent.putExtra(WifiManager.EXTRA_SCAN_AVAILABLE, WIFI_STATE_DISABLED);
703         mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
704     }
705
706     /*********************************************************
707      * Methods exposed for public use
708      ********************************************************/
709
710     public Messenger getMessenger() {
711         return new Messenger(getHandler());
712     }
713     /**
714      * TODO: doc
715      */
716     public boolean syncPingSupplicant(AsyncChannel channel) {
717         Message resultMsg = channel.sendMessageSynchronously(CMD_PING_SUPPLICANT);
718         boolean result = (resultMsg.arg1 != FAILURE);
719         resultMsg.recycle();
720         return result;
721     }
722
723     /**
724      * TODO: doc
725      */
726     public void startScan(int callingUid) {
727         sendMessage(CMD_START_SCAN, callingUid);
728     }
729
730     private void noteScanStart(int callingUid) {
731         if (mScanWorkSource == null && callingUid != UNKNOWN_SCAN_SOURCE) {
732             mScanWorkSource = new WorkSource(callingUid);
733             try {
734                 mBatteryStats.noteWifiScanStartedFromSource(mScanWorkSource);
735             } catch (RemoteException e) {
736                 log(e.toString());
737             }
738         }
739     }
740
741     private void noteScanEnd() {
742         if (mScanWorkSource != null) {
743             try {
744                 mBatteryStats.noteWifiScanStoppedFromSource(mScanWorkSource);
745             } catch (RemoteException e) {
746                 log(e.toString());
747             } finally {
748                 mScanWorkSource = null;
749             }
750         }
751     }
752
753     private void startScanNative(int type) {
754         mWifiNative.scan(type);
755         mScanResultIsPending = true;
756     }
757
758     /**
759      * TODO: doc
760      */
761     public void setSupplicantRunning(boolean enable) {
762         if (enable) {
763             sendMessage(CMD_START_SUPPLICANT);
764         } else {
765             sendMessage(CMD_STOP_SUPPLICANT);
766         }
767     }
768
769     /**
770      * TODO: doc
771      */
772     public void setHostApRunning(WifiConfiguration wifiConfig, boolean enable) {
773         if (enable) {
774             sendMessage(CMD_START_AP, wifiConfig);
775         } else {
776             sendMessage(CMD_STOP_AP);
777         }
778     }
779
780     public void setWifiApConfiguration(WifiConfiguration config) {
781         mWifiApConfigChannel.sendMessage(CMD_SET_AP_CONFIG, config);
782     }
783
784     public WifiConfiguration syncGetWifiApConfiguration() {
785         Message resultMsg = mWifiApConfigChannel.sendMessageSynchronously(CMD_REQUEST_AP_CONFIG);
786         WifiConfiguration ret = (WifiConfiguration) resultMsg.obj;
787         resultMsg.recycle();
788         return ret;
789     }
790
791     /**
792      * TODO: doc
793      */
794     public int syncGetWifiState() {
795         return mWifiState.get();
796     }
797
798     /**
799      * TODO: doc
800      */
801     public String syncGetWifiStateByName() {
802         switch (mWifiState.get()) {
803             case WIFI_STATE_DISABLING:
804                 return "disabling";
805             case WIFI_STATE_DISABLED:
806                 return "disabled";
807             case WIFI_STATE_ENABLING:
808                 return "enabling";
809             case WIFI_STATE_ENABLED:
810                 return "enabled";
811             case WIFI_STATE_UNKNOWN:
812                 return "unknown state";
813             default:
814                 return "[invalid state]";
815         }
816     }
817
818     /**
819      * TODO: doc
820      */
821     public int syncGetWifiApState() {
822         return mWifiApState.get();
823     }
824
825     /**
826      * TODO: doc
827      */
828     public String syncGetWifiApStateByName() {
829         switch (mWifiApState.get()) {
830             case WIFI_AP_STATE_DISABLING:
831                 return "disabling";
832             case WIFI_AP_STATE_DISABLED:
833                 return "disabled";
834             case WIFI_AP_STATE_ENABLING:
835                 return "enabling";
836             case WIFI_AP_STATE_ENABLED:
837                 return "enabled";
838             case WIFI_AP_STATE_FAILED:
839                 return "failed";
840             default:
841                 return "[invalid state]";
842         }
843     }
844
845     /**
846      * Get status information for the current connection, if any.
847      * @return a {@link WifiInfo} object containing information about the current connection
848      *
849      */
850     public WifiInfo syncRequestConnectionInfo() {
851         return mWifiInfo;
852     }
853
854     public DhcpResults syncGetDhcpResults() {
855         synchronized (mDhcpResultsLock) {
856             return new DhcpResults(mDhcpResults);
857         }
858     }
859
860     /**
861      * TODO: doc
862      */
863     public void setDriverStart(boolean enable) {
864         if (enable) {
865             sendMessage(CMD_START_DRIVER);
866         } else {
867             sendMessage(CMD_STOP_DRIVER);
868         }
869     }
870
871     public void captivePortalCheckComplete() {
872         sendMessage(CMD_CAPTIVE_CHECK_COMPLETE);
873     }
874
875     /**
876      * TODO: doc
877      */
878     public void setOperationalMode(int mode) {
879         sendMessage(CMD_SET_OPERATIONAL_MODE, mode, 0);
880     }
881
882     /**
883      * TODO: doc
884      */
885     public List<ScanResult> syncGetScanResultsList() {
886         synchronized (mScanResultCache) {
887             List<ScanResult> scanList = new ArrayList<ScanResult>();
888             for(ScanResult result: mScanResults) {
889                 scanList.add(new ScanResult(result));
890             }
891             return scanList;
892         }
893     }
894
895     /**
896      * Disconnect from Access Point
897      */
898     public void disconnectCommand() {
899         sendMessage(CMD_DISCONNECT);
900     }
901
902     /**
903      * Initiate a reconnection to AP
904      */
905     public void reconnectCommand() {
906         sendMessage(CMD_RECONNECT);
907     }
908
909     /**
910      * Initiate a re-association to AP
911      */
912     public void reassociateCommand() {
913         sendMessage(CMD_REASSOCIATE);
914     }
915
916     /**
917      * Add a network synchronously
918      *
919      * @return network id of the new network
920      */
921     public int syncAddOrUpdateNetwork(AsyncChannel channel, WifiConfiguration config) {
922         Message resultMsg = channel.sendMessageSynchronously(CMD_ADD_OR_UPDATE_NETWORK, config);
923         int result = resultMsg.arg1;
924         resultMsg.recycle();
925         return result;
926     }
927
928     public List<WifiConfiguration> syncGetConfiguredNetworks(AsyncChannel channel) {
929         Message resultMsg = channel.sendMessageSynchronously(CMD_GET_CONFIGURED_NETWORKS);
930         List<WifiConfiguration> result = (List<WifiConfiguration>) resultMsg.obj;
931         resultMsg.recycle();
932         return result;
933     }
934
935     /**
936      * Delete a network
937      *
938      * @param networkId id of the network to be removed
939      */
940     public boolean syncRemoveNetwork(AsyncChannel channel, int networkId) {
941         Message resultMsg = channel.sendMessageSynchronously(CMD_REMOVE_NETWORK, networkId);
942         boolean result = (resultMsg.arg1 != FAILURE);
943         resultMsg.recycle();
944         return result;
945     }
946
947     /**
948      * Enable a network
949      *
950      * @param netId network id of the network
951      * @param disableOthers true, if all other networks have to be disabled
952      * @return {@code true} if the operation succeeds, {@code false} otherwise
953      */
954     public boolean syncEnableNetwork(AsyncChannel channel, int netId, boolean disableOthers) {
955         Message resultMsg = channel.sendMessageSynchronously(CMD_ENABLE_NETWORK, netId,
956                 disableOthers ? 1 : 0);
957         boolean result = (resultMsg.arg1 != FAILURE);
958         resultMsg.recycle();
959         return result;
960     }
961
962     /**
963      * Disable a network
964      *
965      * @param netId network id of the network
966      * @return {@code true} if the operation succeeds, {@code false} otherwise
967      */
968     public boolean syncDisableNetwork(AsyncChannel channel, int netId) {
969         Message resultMsg = channel.sendMessageSynchronously(WifiManager.DISABLE_NETWORK, netId);
970         boolean result = (resultMsg.arg1 != WifiManager.DISABLE_NETWORK_FAILED);
971         resultMsg.recycle();
972         return result;
973     }
974
975     /**
976      * Blacklist a BSSID. This will avoid the AP if there are
977      * alternate APs to connect
978      *
979      * @param bssid BSSID of the network
980      */
981     public void addToBlacklist(String bssid) {
982         sendMessage(CMD_BLACKLIST_NETWORK, bssid);
983     }
984
985     /**
986      * Clear the blacklist list
987      *
988      */
989     public void clearBlacklist() {
990         sendMessage(CMD_CLEAR_BLACKLIST);
991     }
992
993     public void enableRssiPolling(boolean enabled) {
994        sendMessage(CMD_ENABLE_RSSI_POLL, enabled ? 1 : 0, 0);
995     }
996
997     public void enableBackgroundScanCommand(boolean enabled) {
998        sendMessage(CMD_ENABLE_BACKGROUND_SCAN, enabled ? 1 : 0, 0);
999     }
1000
1001     public void enableAllNetworks() {
1002         sendMessage(CMD_ENABLE_ALL_NETWORKS);
1003     }
1004
1005     /**
1006      * Start filtering Multicast v4 packets
1007      */
1008     public void startFilteringMulticastV4Packets() {
1009         mFilteringMulticastV4Packets.set(true);
1010         sendMessage(CMD_START_PACKET_FILTERING, MULTICAST_V4, 0);
1011     }
1012
1013     /**
1014      * Stop filtering Multicast v4 packets
1015      */
1016     public void stopFilteringMulticastV4Packets() {
1017         mFilteringMulticastV4Packets.set(false);
1018         sendMessage(CMD_STOP_PACKET_FILTERING, MULTICAST_V4, 0);
1019     }
1020
1021     /**
1022      * Start filtering Multicast v4 packets
1023      */
1024     public void startFilteringMulticastV6Packets() {
1025         sendMessage(CMD_START_PACKET_FILTERING, MULTICAST_V6, 0);
1026     }
1027
1028     /**
1029      * Stop filtering Multicast v4 packets
1030      */
1031     public void stopFilteringMulticastV6Packets() {
1032         sendMessage(CMD_STOP_PACKET_FILTERING, MULTICAST_V6, 0);
1033     }
1034
1035     /**
1036      * Set high performance mode of operation.
1037      * Enabling would set active power mode and disable suspend optimizations;
1038      * disabling would set auto power mode and enable suspend optimizations
1039      * @param enable true if enable, false otherwise
1040      */
1041     public void setHighPerfModeEnabled(boolean enable) {
1042         sendMessage(CMD_SET_HIGH_PERF_MODE, enable ? 1 : 0, 0);
1043     }
1044
1045     /**
1046      * Set the country code
1047      * @param countryCode following ISO 3166 format
1048      * @param persist {@code true} if the setting should be remembered.
1049      */
1050     public void setCountryCode(String countryCode, boolean persist) {
1051         if (persist) {
1052             mPersistedCountryCode = countryCode;
1053             Settings.Global.putString(mContext.getContentResolver(),
1054                     Settings.Global.WIFI_COUNTRY_CODE,
1055                     countryCode);
1056         }
1057         sendMessage(CMD_SET_COUNTRY_CODE, countryCode);
1058     }
1059
1060     /**
1061      * Set the operational frequency band
1062      * @param band
1063      * @param persist {@code true} if the setting should be remembered.
1064      */
1065     public void setFrequencyBand(int band, boolean persist) {
1066         if (persist) {
1067             Settings.Global.putInt(mContext.getContentResolver(),
1068                     Settings.Global.WIFI_FREQUENCY_BAND,
1069                     band);
1070         }
1071         sendMessage(CMD_SET_FREQUENCY_BAND, band, 0);
1072     }
1073
1074     /**
1075      * Returns the operational frequency band
1076      */
1077     public int getFrequencyBand() {
1078         return mFrequencyBand.get();
1079     }
1080
1081     /**
1082      * Returns the wifi configuration file
1083      */
1084     public String getConfigFile() {
1085         return mWifiConfigStore.getConfigFile();
1086     }
1087
1088     /**
1089      * Send a message indicating bluetooth adapter connection state changed
1090      */
1091     public void sendBluetoothAdapterStateChange(int state) {
1092         sendMessage(CMD_BLUETOOTH_ADAPTER_STATE_CHANGE, state, 0);
1093     }
1094
1095     /**
1096      * Save configuration on supplicant
1097      *
1098      * @return {@code true} if the operation succeeds, {@code false} otherwise
1099      *
1100      * TODO: deprecate this
1101      */
1102     public boolean syncSaveConfig(AsyncChannel channel) {
1103         Message resultMsg = channel.sendMessageSynchronously(CMD_SAVE_CONFIG);
1104         boolean result = (resultMsg.arg1 != FAILURE);
1105         resultMsg.recycle();
1106         return result;
1107     }
1108
1109     public void updateBatteryWorkSource(WorkSource newSource) {
1110         synchronized (mRunningWifiUids) {
1111             try {
1112                 if (newSource != null) {
1113                     mRunningWifiUids.set(newSource);
1114                 }
1115                 if (mIsRunning) {
1116                     if (mReportedRunning) {
1117                         // If the work source has changed since last time, need
1118                         // to remove old work from battery stats.
1119                         if (mLastRunningWifiUids.diff(mRunningWifiUids)) {
1120                             mBatteryStats.noteWifiRunningChanged(mLastRunningWifiUids,
1121                                     mRunningWifiUids);
1122                             mLastRunningWifiUids.set(mRunningWifiUids);
1123                         }
1124                     } else {
1125                         // Now being started, report it.
1126                         mBatteryStats.noteWifiRunning(mRunningWifiUids);
1127                         mLastRunningWifiUids.set(mRunningWifiUids);
1128                         mReportedRunning = true;
1129                     }
1130                 } else {
1131                     if (mReportedRunning) {
1132                         // Last reported we were running, time to stop.
1133                         mBatteryStats.noteWifiStopped(mLastRunningWifiUids);
1134                         mLastRunningWifiUids.clear();
1135                         mReportedRunning = false;
1136                     }
1137                 }
1138                 mWakeLock.setWorkSource(newSource);
1139             } catch (RemoteException ignore) {
1140             }
1141         }
1142     }
1143
1144     @Override
1145     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1146         super.dump(fd, pw, args);
1147         mSupplicantStateTracker.dump(fd, pw, args);
1148         pw.println("mLinkProperties " + mLinkProperties);
1149         pw.println("mWifiInfo " + mWifiInfo);
1150         pw.println("mDhcpResults " + mDhcpResults);
1151         pw.println("mNetworkInfo " + mNetworkInfo);
1152         pw.println("mLastSignalLevel " + mLastSignalLevel);
1153         pw.println("mLastBssid " + mLastBssid);
1154         pw.println("mLastNetworkId " + mLastNetworkId);
1155         pw.println("mReconnectCount " + mReconnectCount);
1156         pw.println("mOperationalMode " + mOperationalMode);
1157         pw.println("mUserWantsSuspendOpt " + mUserWantsSuspendOpt);
1158         pw.println("mSuspendOptNeedsDisabled " + mSuspendOptNeedsDisabled);
1159         pw.println("Supplicant status " + mWifiNative.status());
1160         pw.println("mEnableBackgroundScan " + mEnableBackgroundScan);
1161         pw.println();
1162         mWifiConfigStore.dump(fd, pw, args);
1163     }
1164
1165     /*********************************************************
1166      * Internal private functions
1167      ********************************************************/
1168
1169     private void handleScreenStateChanged(boolean screenOn) {
1170         if (DBG) log("handleScreenStateChanged: " + screenOn);
1171         enableRssiPolling(screenOn);
1172         if (mBackgroundScanSupported) {
1173             enableBackgroundScanCommand(screenOn == false);
1174         }
1175
1176         if (screenOn) enableAllNetworks();
1177         if (mUserWantsSuspendOpt.get()) {
1178             if (screenOn) {
1179                 sendMessage(CMD_SET_SUSPEND_OPT_ENABLED, 0, 0);
1180             } else {
1181                 //Allow 2s for suspend optimizations to be set
1182                 mSuspendWakeLock.acquire(2000);
1183                 sendMessage(CMD_SET_SUSPEND_OPT_ENABLED, 1, 0);
1184             }
1185         }
1186         mScreenBroadcastReceived.set(true);
1187     }
1188
1189     private void checkAndSetConnectivityInstance() {
1190         if (mCm == null) {
1191             mCm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
1192         }
1193     }
1194
1195     private boolean startTethering(ArrayList<String> available) {
1196
1197         boolean wifiAvailable = false;
1198
1199         checkAndSetConnectivityInstance();
1200
1201         String[] wifiRegexs = mCm.getTetherableWifiRegexs();
1202
1203         for (String intf : available) {
1204             for (String regex : wifiRegexs) {
1205                 if (intf.matches(regex)) {
1206
1207                     InterfaceConfiguration ifcg = null;
1208                     try {
1209                         ifcg = mNwService.getInterfaceConfig(intf);
1210                         if (ifcg != null) {
1211                             /* IP/netmask: 192.168.43.1/255.255.255.0 */
1212                             ifcg.setLinkAddress(new LinkAddress(
1213                                     NetworkUtils.numericToInetAddress("192.168.43.1"), 24));
1214                             ifcg.setInterfaceUp();
1215
1216                             mNwService.setInterfaceConfig(intf, ifcg);
1217                         }
1218                     } catch (Exception e) {
1219                         loge("Error configuring interface " + intf + ", :" + e);
1220                         return false;
1221                     }
1222
1223                     if(mCm.tether(intf) != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
1224                         loge("Error tethering on " + intf);
1225                         return false;
1226                     }
1227                     mTetherInterfaceName = intf;
1228                     return true;
1229                 }
1230             }
1231         }
1232         // We found no interfaces to tether
1233         return false;
1234     }
1235
1236     private void stopTethering() {
1237
1238         checkAndSetConnectivityInstance();
1239
1240         /* Clear the interface config to allow dhcp correctly configure new
1241            ip settings */
1242         InterfaceConfiguration ifcg = null;
1243         try {
1244             ifcg = mNwService.getInterfaceConfig(mTetherInterfaceName);
1245             if (ifcg != null) {
1246                 ifcg.setLinkAddress(
1247                         new LinkAddress(NetworkUtils.numericToInetAddress("0.0.0.0"), 0));
1248                 mNwService.setInterfaceConfig(mTetherInterfaceName, ifcg);
1249             }
1250         } catch (Exception e) {
1251             loge("Error resetting interface " + mTetherInterfaceName + ", :" + e);
1252         }
1253
1254         if (mCm.untether(mTetherInterfaceName) != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
1255             loge("Untether initiate failed!");
1256         }
1257     }
1258
1259     private boolean isWifiTethered(ArrayList<String> active) {
1260
1261         checkAndSetConnectivityInstance();
1262
1263         String[] wifiRegexs = mCm.getTetherableWifiRegexs();
1264         for (String intf : active) {
1265             for (String regex : wifiRegexs) {
1266                 if (intf.matches(regex)) {
1267                     return true;
1268                 }
1269             }
1270         }
1271         // We found no interfaces that are tethered
1272         return false;
1273     }
1274
1275     /**
1276      * Set the country code from the system setting value, if any.
1277      */
1278     private void setCountryCode() {
1279         String countryCode = Settings.Global.getString(mContext.getContentResolver(),
1280                 Settings.Global.WIFI_COUNTRY_CODE);
1281         if (countryCode != null && !countryCode.isEmpty()) {
1282             setCountryCode(countryCode, false);
1283         } else {
1284             //use driver default
1285         }
1286     }
1287
1288     /**
1289      * Set the frequency band from the system setting value, if any.
1290      */
1291     private void setFrequencyBand() {
1292         int band = Settings.Global.getInt(mContext.getContentResolver(),
1293                 Settings.Global.WIFI_FREQUENCY_BAND, WifiManager.WIFI_FREQUENCY_BAND_AUTO);
1294         setFrequencyBand(band, false);
1295     }
1296
1297     private void setSuspendOptimizationsNative(int reason, boolean enabled) {
1298         if (DBG) log("setSuspendOptimizationsNative: " + reason + " " + enabled);
1299         if (enabled) {
1300             mSuspendOptNeedsDisabled &= ~reason;
1301             /* None of dhcp, screen or highperf need it disabled and user wants it enabled */
1302             if (mSuspendOptNeedsDisabled == 0 && mUserWantsSuspendOpt.get()) {
1303                 mWifiNative.setSuspendOptimizations(true);
1304             }
1305         } else {
1306             mSuspendOptNeedsDisabled |= reason;
1307             mWifiNative.setSuspendOptimizations(false);
1308         }
1309     }
1310
1311     private void setSuspendOptimizations(int reason, boolean enabled) {
1312         if (DBG) log("setSuspendOptimizations: " + reason + " " + enabled);
1313         if (enabled) {
1314             mSuspendOptNeedsDisabled &= ~reason;
1315         } else {
1316             mSuspendOptNeedsDisabled |= reason;
1317         }
1318         if (DBG) log("mSuspendOptNeedsDisabled " + mSuspendOptNeedsDisabled);
1319     }
1320
1321     private void setWifiState(int wifiState) {
1322         final int previousWifiState = mWifiState.get();
1323
1324         try {
1325             if (wifiState == WIFI_STATE_ENABLED) {
1326                 mBatteryStats.noteWifiOn();
1327             } else if (wifiState == WIFI_STATE_DISABLED) {
1328                 mBatteryStats.noteWifiOff();
1329             }
1330         } catch (RemoteException e) {
1331             loge("Failed to note battery stats in wifi");
1332         }
1333
1334         mWifiState.set(wifiState);
1335
1336         if (DBG) log("setWifiState: " + syncGetWifiStateByName());
1337
1338         final Intent intent = new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION);
1339         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1340         intent.putExtra(WifiManager.EXTRA_WIFI_STATE, wifiState);
1341         intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_STATE, previousWifiState);
1342         mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
1343     }
1344
1345     private void setWifiApState(int wifiApState) {
1346         final int previousWifiApState = mWifiApState.get();
1347
1348         try {
1349             if (wifiApState == WIFI_AP_STATE_ENABLED) {
1350                 mBatteryStats.noteWifiOn();
1351             } else if (wifiApState == WIFI_AP_STATE_DISABLED) {
1352                 mBatteryStats.noteWifiOff();
1353             }
1354         } catch (RemoteException e) {
1355             loge("Failed to note battery stats in wifi");
1356         }
1357
1358         // Update state
1359         mWifiApState.set(wifiApState);
1360
1361         if (DBG) log("setWifiApState: " + syncGetWifiApStateByName());
1362
1363         final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
1364         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1365         intent.putExtra(WifiManager.EXTRA_WIFI_AP_STATE, wifiApState);
1366         intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_AP_STATE, previousWifiApState);
1367         mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
1368     }
1369
1370     private static final String ID_STR = "id=";
1371     private static final String BSSID_STR = "bssid=";
1372     private static final String FREQ_STR = "freq=";
1373     private static final String LEVEL_STR = "level=";
1374     private static final String TSF_STR = "tsf=";
1375     private static final String FLAGS_STR = "flags=";
1376     private static final String SSID_STR = "ssid=";
1377     private static final String DELIMITER_STR = "====";
1378     private static final String END_STR = "####";
1379
1380     /**
1381      * Format:
1382      *
1383      * id=1
1384      * bssid=68:7f:76:d7:1a:6e
1385      * freq=2412
1386      * level=-44
1387      * tsf=1344626243700342
1388      * flags=[WPA2-PSK-CCMP][WPS][ESS]
1389      * ssid=zfdy
1390      * ====
1391      * id=2
1392      * bssid=68:5f:74:d7:1a:6f
1393      * freq=5180
1394      * level=-73
1395      * tsf=1344626243700373
1396      * flags=[WPA2-PSK-CCMP][WPS][ESS]
1397      * ssid=zuby
1398      * ====
1399      */
1400     private void setScanResults() {
1401         String bssid = "";
1402         int level = 0;
1403         int freq = 0;
1404         long tsf = 0;
1405         String flags = "";
1406         WifiSsid wifiSsid = null;
1407         String scanResults;
1408         String tmpResults;
1409         StringBuffer scanResultsBuf = new StringBuffer();
1410         int sid = 0;
1411
1412         while (true) {
1413             tmpResults = mWifiNative.scanResults(sid);
1414             if (TextUtils.isEmpty(tmpResults)) break;
1415             scanResultsBuf.append(tmpResults);
1416             scanResultsBuf.append("\n");
1417             String[] lines = tmpResults.split("\n");
1418             sid = -1;
1419             for (int i=lines.length - 1; i >= 0; i--) {
1420                 if (lines[i].startsWith(END_STR)) {
1421                     break;
1422                 } else if (lines[i].startsWith(ID_STR)) {
1423                     try {
1424                         sid = Integer.parseInt(lines[i].substring(ID_STR.length())) + 1;
1425                     } catch (NumberFormatException e) {
1426                         // Nothing to do
1427                     }
1428                     break;
1429                 }
1430             }
1431             if (sid == -1) break;
1432         }
1433
1434         scanResults = scanResultsBuf.toString();
1435         if (TextUtils.isEmpty(scanResults)) {
1436            return;
1437         }
1438
1439         synchronized(mScanResultCache) {
1440             mScanResults = new ArrayList<ScanResult>();
1441             String[] lines = scanResults.split("\n");
1442
1443             for (String line : lines) {
1444                 if (line.startsWith(BSSID_STR)) {
1445                     bssid = line.substring(BSSID_STR.length());
1446                 } else if (line.startsWith(FREQ_STR)) {
1447                     try {
1448                         freq = Integer.parseInt(line.substring(FREQ_STR.length()));
1449                     } catch (NumberFormatException e) {
1450                         freq = 0;
1451                     }
1452                 } else if (line.startsWith(LEVEL_STR)) {
1453                     try {
1454                         level = Integer.parseInt(line.substring(LEVEL_STR.length()));
1455                         /* some implementations avoid negative values by adding 256
1456                          * so we need to adjust for that here.
1457                          */
1458                         if (level > 0) level -= 256;
1459                     } catch(NumberFormatException e) {
1460                         level = 0;
1461                     }
1462                 } else if (line.startsWith(TSF_STR)) {
1463                     try {
1464                         tsf = Long.parseLong(line.substring(TSF_STR.length()));
1465                     } catch (NumberFormatException e) {
1466                         tsf = 0;
1467                     }
1468                 } else if (line.startsWith(FLAGS_STR)) {
1469                     flags = line.substring(FLAGS_STR.length());
1470                 } else if (line.startsWith(SSID_STR)) {
1471                     wifiSsid = WifiSsid.createFromAsciiEncoded(
1472                             line.substring(SSID_STR.length()));
1473                 } else if (line.startsWith(DELIMITER_STR) || line.startsWith(END_STR)) {
1474                     if (bssid != null) {
1475                         String ssid = (wifiSsid != null) ? wifiSsid.toString() : WifiSsid.NONE;
1476                         String key = bssid + ssid;
1477                         ScanResult scanResult = mScanResultCache.get(key);
1478                         if (scanResult != null) {
1479                             scanResult.level = level;
1480                             scanResult.wifiSsid = wifiSsid;
1481                             // Keep existing API
1482                             scanResult.SSID = (wifiSsid != null) ? wifiSsid.toString() :
1483                                     WifiSsid.NONE;
1484                             scanResult.capabilities = flags;
1485                             scanResult.frequency = freq;
1486                             scanResult.timestamp = tsf;
1487                         } else {
1488                             scanResult =
1489                                 new ScanResult(
1490                                         wifiSsid, bssid, flags, level, freq, tsf);
1491                             mScanResultCache.put(key, scanResult);
1492                         }
1493                         mScanResults.add(scanResult);
1494                     }
1495                     bssid = null;
1496                     level = 0;
1497                     freq = 0;
1498                     tsf = 0;
1499                     flags = "";
1500                     wifiSsid = null;
1501                 }
1502             }
1503         }
1504     }
1505
1506     /*
1507      * Fetch RSSI and linkspeed on current connection
1508      */
1509     private void fetchRssiAndLinkSpeedNative() {
1510         int newRssi = -1;
1511         int newLinkSpeed = -1;
1512
1513         String signalPoll = mWifiNative.signalPoll();
1514
1515         if (signalPoll != null) {
1516             String[] lines = signalPoll.split("\n");
1517             for (String line : lines) {
1518                 String[] prop = line.split("=");
1519                 if (prop.length < 2) continue;
1520                 try {
1521                     if (prop[0].equals("RSSI")) {
1522                         newRssi = Integer.parseInt(prop[1]);
1523                     } else if (prop[0].equals("LINKSPEED")) {
1524                         newLinkSpeed = Integer.parseInt(prop[1]);
1525                     }
1526                 } catch (NumberFormatException e) {
1527                     //Ignore, defaults on rssi and linkspeed are assigned
1528                 }
1529             }
1530         }
1531
1532         if (newRssi != -1 && MIN_RSSI < newRssi && newRssi < MAX_RSSI) { // screen out invalid values
1533             /* some implementations avoid negative values by adding 256
1534              * so we need to adjust for that here.
1535              */
1536             if (newRssi > 0) newRssi -= 256;
1537             mWifiInfo.setRssi(newRssi);
1538             /*
1539              * Rather then sending the raw RSSI out every time it
1540              * changes, we precalculate the signal level that would
1541              * be displayed in the status bar, and only send the
1542              * broadcast if that much more coarse-grained number
1543              * changes. This cuts down greatly on the number of
1544              * broadcasts, at the cost of not informing others
1545              * interested in RSSI of all the changes in signal
1546              * level.
1547              */
1548             int newSignalLevel = WifiManager.calculateSignalLevel(newRssi, WifiManager.RSSI_LEVELS);
1549             if (newSignalLevel != mLastSignalLevel) {
1550                 sendRssiChangeBroadcast(newRssi);
1551             }
1552             mLastSignalLevel = newSignalLevel;
1553         } else {
1554             mWifiInfo.setRssi(MIN_RSSI);
1555         }
1556
1557         if (newLinkSpeed != -1) {
1558             mWifiInfo.setLinkSpeed(newLinkSpeed);
1559         }
1560     }
1561
1562     /*
1563      * Fetch TX packet counters on current connection
1564      */
1565     private void fetchPktcntNative(RssiPacketCountInfo info) {
1566         String pktcntPoll = mWifiNative.pktcntPoll();
1567
1568         if (pktcntPoll != null) {
1569             String[] lines = pktcntPoll.split("\n");
1570             for (String line : lines) {
1571                 String[] prop = line.split("=");
1572                 if (prop.length < 2) continue;
1573                 try {
1574                     if (prop[0].equals("TXGOOD")) {
1575                         info.txgood = Integer.parseInt(prop[1]);
1576                     } else if (prop[0].equals("TXBAD")) {
1577                         info.txbad = Integer.parseInt(prop[1]);
1578                     }
1579                 } catch (NumberFormatException e) {
1580                     //Ignore
1581                 }
1582             }
1583         }
1584     }
1585
1586     private void configureLinkProperties() {
1587         if (mWifiConfigStore.isUsingStaticIp(mLastNetworkId)) {
1588             mLinkProperties = mWifiConfigStore.getLinkProperties(mLastNetworkId);
1589         } else {
1590             synchronized (mDhcpResultsLock) {
1591                 if ((mDhcpResults != null) && (mDhcpResults.linkProperties != null)) {
1592                     mLinkProperties = mDhcpResults.linkProperties;
1593                 }
1594             }
1595             mLinkProperties.setHttpProxy(mWifiConfigStore.getProxyProperties(mLastNetworkId));
1596         }
1597         mLinkProperties.setInterfaceName(mInterfaceName);
1598         if (DBG) log("netId=" + mLastNetworkId  + " Link configured: " + mLinkProperties);
1599     }
1600
1601     private int getMaxDhcpRetries() {
1602         return Settings.Global.getInt(mContext.getContentResolver(),
1603                                       Settings.Global.WIFI_MAX_DHCP_RETRY_COUNT,
1604                                       DEFAULT_MAX_DHCP_RETRIES);
1605     }
1606
1607     private void sendScanResultsAvailableBroadcast() {
1608         noteScanEnd();
1609         Intent intent = new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
1610         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1611         mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
1612     }
1613
1614     private void sendRssiChangeBroadcast(final int newRssi) {
1615         Intent intent = new Intent(WifiManager.RSSI_CHANGED_ACTION);
1616         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1617         intent.putExtra(WifiManager.EXTRA_NEW_RSSI, newRssi);
1618         mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
1619     }
1620
1621     private void sendNetworkStateChangeBroadcast(String bssid) {
1622         Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
1623         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1624         intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, new NetworkInfo(mNetworkInfo));
1625         intent.putExtra(WifiManager.EXTRA_LINK_PROPERTIES, new LinkProperties (mLinkProperties));
1626         if (bssid != null)
1627             intent.putExtra(WifiManager.EXTRA_BSSID, bssid);
1628         if (mNetworkInfo.getDetailedState() == DetailedState.VERIFYING_POOR_LINK ||
1629                 mNetworkInfo.getDetailedState() == DetailedState.CONNECTED) {
1630             intent.putExtra(WifiManager.EXTRA_WIFI_INFO, new WifiInfo(mWifiInfo));
1631         }
1632         mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
1633     }
1634
1635     private void sendLinkConfigurationChangedBroadcast() {
1636         Intent intent = new Intent(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION);
1637         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1638         intent.putExtra(WifiManager.EXTRA_LINK_PROPERTIES, new LinkProperties(mLinkProperties));
1639         mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
1640     }
1641
1642     private void sendSupplicantConnectionChangedBroadcast(boolean connected) {
1643         Intent intent = new Intent(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
1644         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1645         intent.putExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, connected);
1646         mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
1647     }
1648
1649     /**
1650      * Record the detailed state of a network.
1651      * @param state the new {@code DetailedState}
1652      */
1653     private void setNetworkDetailedState(NetworkInfo.DetailedState state) {
1654         if (DBG) {
1655             log("setDetailed state, old ="
1656                     + mNetworkInfo.getDetailedState() + " and new state=" + state);
1657         }
1658
1659         if (state != mNetworkInfo.getDetailedState()) {
1660             mNetworkInfo.setDetailedState(state, null, mWifiInfo.getSSID());
1661         }
1662     }
1663
1664     private DetailedState getNetworkDetailedState() {
1665         return mNetworkInfo.getDetailedState();
1666     }
1667
1668
1669     private SupplicantState handleSupplicantStateChange(Message message) {
1670         StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
1671         SupplicantState state = stateChangeResult.state;
1672         // Supplicant state change
1673         // [31-13] Reserved for future use
1674         // [8 - 0] Supplicant state (as defined in SupplicantState.java)
1675         // 50023 supplicant_state_changed (custom|1|5)
1676         mWifiInfo.setSupplicantState(state);
1677         // Network id is only valid when we start connecting
1678         if (SupplicantState.isConnecting(state)) {
1679             mWifiInfo.setNetworkId(stateChangeResult.networkId);
1680         } else {
1681             mWifiInfo.setNetworkId(WifiConfiguration.INVALID_NETWORK_ID);
1682         }
1683
1684         mWifiInfo.setBSSID(stateChangeResult.BSSID);
1685         mWifiInfo.setSSID(stateChangeResult.wifiSsid);
1686
1687         mSupplicantStateTracker.sendMessage(Message.obtain(message));
1688
1689         return state;
1690     }
1691
1692     /**
1693      * Resets the Wi-Fi Connections by clearing any state, resetting any sockets
1694      * using the interface, stopping DHCP & disabling interface
1695      */
1696     private void handleNetworkDisconnect() {
1697         if (DBG) log("Stopping DHCP and clearing IP");
1698
1699         stopDhcp();
1700
1701         try {
1702             mNwService.clearInterfaceAddresses(mInterfaceName);
1703             mNwService.disableIpv6(mInterfaceName);
1704         } catch (Exception e) {
1705             loge("Failed to clear addresses or disable ipv6" + e);
1706         }
1707
1708         /* Reset data structures */
1709         mWifiInfo.setInetAddress(null);
1710         mWifiInfo.setBSSID(null);
1711         mWifiInfo.setSSID(null);
1712         mWifiInfo.setNetworkId(WifiConfiguration.INVALID_NETWORK_ID);
1713         mWifiInfo.setRssi(MIN_RSSI);
1714         mWifiInfo.setLinkSpeed(-1);
1715         mWifiInfo.setMeteredHint(false);
1716
1717         setNetworkDetailedState(DetailedState.DISCONNECTED);
1718         mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.DISCONNECTED);
1719
1720         /* Clear network properties */
1721         mLinkProperties.clear();
1722
1723         /* send event to CM & network change broadcast */
1724         sendNetworkStateChangeBroadcast(mLastBssid);
1725
1726         /* Clear IP settings if the network used DHCP */
1727         if (!mWifiConfigStore.isUsingStaticIp(mLastNetworkId)) {
1728             mWifiConfigStore.clearLinkProperties(mLastNetworkId);
1729         }
1730
1731         mLastBssid= null;
1732         mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
1733     }
1734
1735     private void handleSupplicantConnectionLoss() {
1736         /* Socket connection can be lost when we do a graceful shutdown
1737         * or when the driver is hung. Ensure supplicant is stopped here.
1738         */
1739         mWifiNative.killSupplicant(mP2pSupported);
1740         mWifiNative.closeSupplicantConnection();
1741         sendSupplicantConnectionChangedBroadcast(false);
1742         setWifiState(WIFI_STATE_DISABLED);
1743     }
1744
1745     void handlePreDhcpSetup() {
1746         if (!mBluetoothConnectionActive) {
1747             /*
1748              * There are problems setting the Wi-Fi driver's power
1749              * mode to active when bluetooth coexistence mode is
1750              * enabled or sense.
1751              * <p>
1752              * We set Wi-Fi to active mode when
1753              * obtaining an IP address because we've found
1754              * compatibility issues with some routers with low power
1755              * mode.
1756              * <p>
1757              * In order for this active power mode to properly be set,
1758              * we disable coexistence mode until we're done with
1759              * obtaining an IP address.  One exception is if we
1760              * are currently connected to a headset, since disabling
1761              * coexistence would interrupt that connection.
1762              */
1763             // Disable the coexistence mode
1764             mWifiNative.setBluetoothCoexistenceMode(
1765                     mWifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED);
1766         }
1767
1768         /* Disable power save and suspend optimizations during DHCP */
1769         // Note: The order here is important for now. Brcm driver changes
1770         // power settings when we control suspend mode optimizations.
1771         // TODO: Remove this comment when the driver is fixed.
1772         setSuspendOptimizationsNative(SUSPEND_DUE_TO_DHCP, false);
1773         mWifiNative.setPowerSave(false);
1774     }
1775
1776
1777     void startDhcp() {
1778         if (mDhcpStateMachine == null) {
1779             mDhcpStateMachine = DhcpStateMachine.makeDhcpStateMachine(
1780                     mContext, WifiStateMachine.this, mInterfaceName);
1781
1782         }
1783         mDhcpStateMachine.registerForPreDhcpNotification();
1784         mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_START_DHCP);
1785     }
1786
1787     void stopDhcp() {
1788         if (mDhcpStateMachine != null) {
1789             /* In case we were in middle of DHCP operation restore back powermode */
1790             handlePostDhcpSetup();
1791             mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_STOP_DHCP);
1792         }
1793     }
1794
1795     void handlePostDhcpSetup() {
1796         /* Restore power save and suspend optimizations */
1797         setSuspendOptimizationsNative(SUSPEND_DUE_TO_DHCP, true);
1798         mWifiNative.setPowerSave(true);
1799
1800         // Set the coexistence mode back to its default value
1801         mWifiNative.setBluetoothCoexistenceMode(
1802                 mWifiNative.BLUETOOTH_COEXISTENCE_MODE_SENSE);
1803     }
1804
1805     private void handleSuccessfulIpConfiguration(DhcpResults dhcpResults) {
1806         mLastSignalLevel = -1; // force update of signal strength
1807         mReconnectCount = 0; //Reset IP failure tracking
1808         synchronized (mDhcpResultsLock) {
1809             mDhcpResults = dhcpResults;
1810         }
1811         LinkProperties linkProperties = dhcpResults.linkProperties;
1812         mWifiConfigStore.setLinkProperties(mLastNetworkId, new LinkProperties(linkProperties));
1813         InetAddress addr = null;
1814         Iterator<InetAddress> addrs = linkProperties.getAddresses().iterator();
1815         if (addrs.hasNext()) {
1816             addr = addrs.next();
1817         }
1818         mWifiInfo.setInetAddress(addr);
1819         mWifiInfo.setMeteredHint(dhcpResults.hasMeteredHint());
1820         if (getNetworkDetailedState() == DetailedState.CONNECTED) {
1821             //DHCP renewal in connected state
1822             linkProperties.setHttpProxy(mWifiConfigStore.getProxyProperties(mLastNetworkId));
1823             if (!linkProperties.equals(mLinkProperties)) {
1824                 if (DBG) {
1825                     log("Link configuration changed for netId: " + mLastNetworkId
1826                             + " old: " + mLinkProperties + "new: " + linkProperties);
1827                 }
1828                 mLinkProperties = linkProperties;
1829                 sendLinkConfigurationChangedBroadcast();
1830             }
1831         } else {
1832             configureLinkProperties();
1833         }
1834     }
1835
1836     private void handleFailedIpConfiguration() {
1837         loge("IP configuration failed");
1838
1839         mWifiInfo.setInetAddress(null);
1840         mWifiInfo.setMeteredHint(false);
1841         /**
1842          * If we've exceeded the maximum number of retries for DHCP
1843          * to a given network, disable the network
1844          */
1845         int maxRetries = getMaxDhcpRetries();
1846         // maxRetries == 0 means keep trying forever
1847         if (maxRetries > 0 && ++mReconnectCount > maxRetries) {
1848             loge("Failed " +
1849                     mReconnectCount + " times, Disabling " + mLastNetworkId);
1850             mWifiConfigStore.disableNetwork(mLastNetworkId,
1851                     WifiConfiguration.DISABLED_DHCP_FAILURE);
1852             mReconnectCount = 0;
1853         }
1854
1855         /* DHCP times out after about 30 seconds, we do a
1856          * disconnect and an immediate reconnect to try again
1857          */
1858         mWifiNative.disconnect();
1859         mWifiNative.reconnect();
1860     }
1861
1862     /* Current design is to not set the config on a running hostapd but instead
1863      * stop and start tethering when user changes config on a running access point
1864      *
1865      * TODO: Add control channel setup through hostapd that allows changing config
1866      * on a running daemon
1867      */
1868     private void startSoftApWithConfig(final WifiConfiguration config) {
1869         // start hostapd on a seperate thread
1870         new Thread(new Runnable() {
1871             public void run() {
1872                 try {
1873                     mNwService.startAccessPoint(config, mInterfaceName);
1874                 } catch (Exception e) {
1875                     loge("Exception in softap start " + e);
1876                     try {
1877                         mNwService.stopAccessPoint(mInterfaceName);
1878                         mNwService.startAccessPoint(config, mInterfaceName);
1879                     } catch (Exception e1) {
1880                         loge("Exception in softap re-start " + e1);
1881                         sendMessage(CMD_START_AP_FAILURE);
1882                         return;
1883                     }
1884                 }
1885                 if (DBG) log("Soft AP start successful");
1886                 sendMessage(CMD_START_AP_SUCCESS);
1887             }
1888         }).start();
1889     }
1890
1891     /********************************************************
1892      * HSM states
1893      *******************************************************/
1894
1895     class DefaultState extends State {
1896         @Override
1897         public boolean processMessage(Message message) {
1898             switch (message.what) {
1899                 case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
1900                     if (message.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
1901                         mWifiP2pChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
1902                     } else {
1903                         loge("WifiP2pService connection failure, error=" + message.arg1);
1904                     }
1905                     break;
1906                 case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
1907                     loge("WifiP2pService channel lost, message.arg1 =" + message.arg1);
1908                     //TODO: Re-establish connection to state machine after a delay
1909                     //mWifiP2pChannel.connect(mContext, getHandler(), mWifiP2pManager.getMessenger());
1910                     break;
1911                 case CMD_BLUETOOTH_ADAPTER_STATE_CHANGE:
1912                     mBluetoothConnectionActive = (message.arg1 !=
1913                             BluetoothAdapter.STATE_DISCONNECTED);
1914                     break;
1915                     /* Synchronous call returns */
1916                 case CMD_PING_SUPPLICANT:
1917                 case CMD_ENABLE_NETWORK:
1918                 case CMD_ADD_OR_UPDATE_NETWORK:
1919                 case CMD_REMOVE_NETWORK:
1920                 case CMD_SAVE_CONFIG:
1921                     replyToMessage(message, message.what, FAILURE);
1922                     break;
1923                 case CMD_GET_CONFIGURED_NETWORKS:
1924                     replyToMessage(message, message.what, (List<WifiConfiguration>) null);
1925                     break;
1926                 case CMD_ENABLE_RSSI_POLL:
1927                     mEnableRssiPolling = (message.arg1 == 1);
1928                     break;
1929                 case CMD_ENABLE_BACKGROUND_SCAN:
1930                     mEnableBackgroundScan = (message.arg1 == 1);
1931                     break;
1932                 case CMD_SET_HIGH_PERF_MODE:
1933                     if (message.arg1 == 1) {
1934                         setSuspendOptimizations(SUSPEND_DUE_TO_HIGH_PERF, false);
1935                     } else {
1936                         setSuspendOptimizations(SUSPEND_DUE_TO_HIGH_PERF, true);
1937                     }
1938                     break;
1939                 case CMD_BOOT_COMPLETED:
1940                     String countryCode = mPersistedCountryCode;
1941                     if (TextUtils.isEmpty(countryCode) == false) {
1942                         Settings.Global.putString(mContext.getContentResolver(),
1943                                 Settings.Global.WIFI_COUNTRY_CODE,
1944                                 countryCode);
1945                         // it may be that the state transition that should send this info
1946                         // to the driver happened between mPersistedCountryCode getting set
1947                         // and now, so simply persisting it here would mean we have sent
1948                         // nothing to the driver.  Send the cmd so it might be set now.
1949                         sendMessageAtFrontOfQueue(CMD_SET_COUNTRY_CODE, countryCode);
1950                     }
1951                     break;
1952                     /* Discard */
1953                 case CMD_START_SCAN:
1954                 case CMD_START_SUPPLICANT:
1955                 case CMD_STOP_SUPPLICANT:
1956                 case CMD_STOP_SUPPLICANT_FAILED:
1957                 case CMD_START_DRIVER:
1958                 case CMD_STOP_DRIVER:
1959                 case CMD_DELAYED_STOP_DRIVER:
1960                 case CMD_DRIVER_START_TIMED_OUT:
1961                 case CMD_CAPTIVE_CHECK_COMPLETE:
1962                 case CMD_START_AP:
1963                 case CMD_START_AP_SUCCESS:
1964                 case CMD_START_AP_FAILURE:
1965                 case CMD_STOP_AP:
1966                 case CMD_TETHER_STATE_CHANGE:
1967                 case CMD_TETHER_NOTIFICATION_TIMED_OUT:
1968                 case CMD_DISCONNECT:
1969                 case CMD_RECONNECT:
1970                 case CMD_REASSOCIATE:
1971                 case WifiMonitor.SUP_CONNECTION_EVENT:
1972                 case WifiMonitor.SUP_DISCONNECTION_EVENT:
1973                 case WifiMonitor.NETWORK_CONNECTION_EVENT:
1974                 case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
1975                 case WifiMonitor.SCAN_RESULTS_EVENT:
1976                 case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
1977                 case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
1978                 case WifiMonitor.WPS_OVERLAP_EVENT:
1979                 case CMD_BLACKLIST_NETWORK:
1980                 case CMD_CLEAR_BLACKLIST:
1981                 case CMD_SET_OPERATIONAL_MODE:
1982                 case CMD_SET_COUNTRY_CODE:
1983                 case CMD_SET_FREQUENCY_BAND:
1984                 case CMD_RSSI_POLL:
1985                 case CMD_ENABLE_ALL_NETWORKS:
1986                 case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
1987                 case DhcpStateMachine.CMD_POST_DHCP_ACTION:
1988                 /* Handled by WifiApConfigStore */
1989                 case CMD_SET_AP_CONFIG:
1990                 case CMD_SET_AP_CONFIG_COMPLETED:
1991                 case CMD_REQUEST_AP_CONFIG:
1992                 case CMD_RESPONSE_AP_CONFIG:
1993                 case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
1994                 case WifiWatchdogStateMachine.GOOD_LINK_DETECTED:
1995                 case CMD_NO_NETWORKS_PERIODIC_SCAN:
1996                 case CMD_DISABLE_P2P_RSP:
1997                     break;
1998                 case DhcpStateMachine.CMD_ON_QUIT:
1999                     mDhcpStateMachine = null;
2000                     break;
2001                 case CMD_SET_SUSPEND_OPT_ENABLED:
2002                     if (message.arg1 == 1) {
2003                         mSuspendWakeLock.release();
2004                         setSuspendOptimizations(SUSPEND_DUE_TO_SCREEN, true);
2005                     } else {
2006                         setSuspendOptimizations(SUSPEND_DUE_TO_SCREEN, false);
2007                     }
2008                     break;
2009                 case WifiMonitor.DRIVER_HUNG_EVENT:
2010                     setSupplicantRunning(false);
2011                     setSupplicantRunning(true);
2012                     break;
2013                 case WifiManager.CONNECT_NETWORK:
2014                     replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
2015                             WifiManager.BUSY);
2016                     break;
2017                 case WifiManager.FORGET_NETWORK:
2018                     replyToMessage(message, WifiManager.FORGET_NETWORK_FAILED,
2019                             WifiManager.BUSY);
2020                     break;
2021                 case WifiManager.SAVE_NETWORK:
2022                     replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED,
2023                             WifiManager.BUSY);
2024                     break;
2025                 case WifiManager.START_WPS:
2026                     replyToMessage(message, WifiManager.WPS_FAILED,
2027                             WifiManager.BUSY);
2028                     break;
2029                 case WifiManager.CANCEL_WPS:
2030                     replyToMessage(message, WifiManager.CANCEL_WPS_FAILED,
2031                             WifiManager.BUSY);
2032                     break;
2033                 case WifiManager.DISABLE_NETWORK:
2034                     replyToMessage(message, WifiManager.DISABLE_NETWORK_FAILED,
2035                             WifiManager.BUSY);
2036                     break;
2037                 case WifiManager.RSSI_PKTCNT_FETCH:
2038                     replyToMessage(message, WifiManager.RSSI_PKTCNT_FETCH_FAILED,
2039                             WifiManager.BUSY);
2040                     break;
2041                 case WifiP2pService.P2P_CONNECTION_CHANGED:
2042                     NetworkInfo info = (NetworkInfo) message.obj;
2043                     mP2pConnected.set(info.isConnected());
2044                     break;
2045                 case WifiP2pService.DISCONNECT_WIFI_REQUEST:
2046                     mTemporarilyDisconnectWifi = (message.arg1 == 1);
2047                     replyToMessage(message, WifiP2pService.DISCONNECT_WIFI_RESPONSE);
2048                     break;
2049                 default:
2050                     loge("Error! unhandled message" + message);
2051                     break;
2052             }
2053             return HANDLED;
2054         }
2055     }
2056
2057     class InitialState extends State {
2058         @Override
2059         public void enter() {
2060             mWifiNative.unloadDriver();
2061
2062             if (mWifiP2pChannel == null) {
2063                 mWifiP2pChannel = new AsyncChannel();
2064                 mWifiP2pChannel.connect(mContext, getHandler(), mWifiP2pManager.getMessenger());
2065             }
2066
2067             if (mWifiApConfigChannel == null) {
2068                 mWifiApConfigChannel = new AsyncChannel();
2069                 WifiApConfigStore wifiApConfigStore = WifiApConfigStore.makeWifiApConfigStore(
2070                         mContext, getHandler());
2071                 wifiApConfigStore.loadApConfiguration();
2072                 mWifiApConfigChannel.connectSync(mContext, getHandler(),
2073                         wifiApConfigStore.getMessenger());
2074             }
2075         }
2076         @Override
2077         public boolean processMessage(Message message) {
2078             switch (message.what) {
2079                 case CMD_START_SUPPLICANT:
2080                     if (mWifiNative.loadDriver()) {
2081                         try {
2082                             mNwService.wifiFirmwareReload(mInterfaceName, "STA");
2083                         } catch (Exception e) {
2084                             loge("Failed to reload STA firmware " + e);
2085                             // continue
2086                         }
2087
2088                         try {
2089                             // A runtime crash can leave the interface up and
2090                             // this affects connectivity when supplicant starts up.
2091                             // Ensure interface is down before a supplicant start.
2092                             mNwService.setInterfaceDown(mInterfaceName);
2093                             // Set privacy extensions
2094                             mNwService.setInterfaceIpv6PrivacyExtensions(mInterfaceName, true);
2095
2096                            // IPv6 is enabled only as long as access point is connected since:
2097                            // - IPv6 addresses and routes stick around after disconnection
2098                            // - kernel is unaware when connected and fails to start IPv6 negotiation
2099                            // - kernel can start autoconfiguration when 802.1x is not complete
2100                             mNwService.disableIpv6(mInterfaceName);
2101                         } catch (RemoteException re) {
2102                             loge("Unable to change interface settings: " + re);
2103                         } catch (IllegalStateException ie) {
2104                             loge("Unable to change interface settings: " + ie);
2105                         }
2106
2107                        /* Stop a running supplicant after a runtime restart
2108                         * Avoids issues with drivers that do not handle interface down
2109                         * on a running supplicant properly.
2110                         */
2111                         mWifiNative.killSupplicant(mP2pSupported);
2112                         if(mWifiNative.startSupplicant(mP2pSupported)) {
2113                             setWifiState(WIFI_STATE_ENABLING);
2114                             if (DBG) log("Supplicant start successful");
2115                             mWifiMonitor.startMonitoring();
2116                             transitionTo(mSupplicantStartingState);
2117                         } else {
2118                             loge("Failed to start supplicant!");
2119                         }
2120                     } else {
2121                         loge("Failed to load driver");
2122                     }
2123                     break;
2124                 case CMD_START_AP:
2125                     if (mWifiNative.loadDriver()) {
2126                         setWifiApState(WIFI_AP_STATE_ENABLING);
2127                         transitionTo(mSoftApStartingState);
2128                     } else {
2129                         loge("Failed to load driver for softap");
2130                     }
2131                 default:
2132                     return NOT_HANDLED;
2133             }
2134             return HANDLED;
2135         }
2136     }
2137
2138     class SupplicantStartingState extends State {
2139         private void initializeWpsDetails() {
2140             String detail;
2141             detail = SystemProperties.get("ro.product.name", "");
2142             if (!mWifiNative.setDeviceName(detail)) {
2143                 loge("Failed to set device name " +  detail);
2144             }
2145             detail = SystemProperties.get("ro.product.manufacturer", "");
2146             if (!mWifiNative.setManufacturer(detail)) {
2147                 loge("Failed to set manufacturer " + detail);
2148             }
2149             detail = SystemProperties.get("ro.product.model", "");
2150             if (!mWifiNative.setModelName(detail)) {
2151                 loge("Failed to set model name " + detail);
2152             }
2153             detail = SystemProperties.get("ro.product.model", "");
2154             if (!mWifiNative.setModelNumber(detail)) {
2155                 loge("Failed to set model number " + detail);
2156             }
2157             detail = SystemProperties.get("ro.serialno", "");
2158             if (!mWifiNative.setSerialNumber(detail)) {
2159                 loge("Failed to set serial number " + detail);
2160             }
2161             if (!mWifiNative.setConfigMethods("physical_display virtual_push_button")) {
2162                 loge("Failed to set WPS config methods");
2163             }
2164             if (!mWifiNative.setDeviceType(mPrimaryDeviceType)) {
2165                 loge("Failed to set primary device type " + mPrimaryDeviceType);
2166             }
2167         }
2168
2169         @Override
2170         public boolean processMessage(Message message) {
2171             switch(message.what) {
2172                 case WifiMonitor.SUP_CONNECTION_EVENT:
2173                     if (DBG) log("Supplicant connection established");
2174                     setWifiState(WIFI_STATE_ENABLED);
2175                     mSupplicantRestartCount = 0;
2176                     /* Reset the supplicant state to indicate the supplicant
2177                      * state is not known at this time */
2178                     mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
2179                     /* Initialize data structures */
2180                     mLastBssid = null;
2181                     mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
2182                     mLastSignalLevel = -1;
2183
2184                     mWifiInfo.setMacAddress(mWifiNative.getMacAddress());
2185                     mWifiConfigStore.loadAndEnableAllNetworks();
2186                     initializeWpsDetails();
2187
2188                     sendSupplicantConnectionChangedBroadcast(true);
2189                     transitionTo(mDriverStartedState);
2190                     break;
2191                 case WifiMonitor.SUP_DISCONNECTION_EVENT:
2192                     if (++mSupplicantRestartCount <= SUPPLICANT_RESTART_TRIES) {
2193                         loge("Failed to setup control channel, restart supplicant");
2194                         mWifiNative.killSupplicant(mP2pSupported);
2195                         transitionTo(mInitialState);
2196                         sendMessageDelayed(CMD_START_SUPPLICANT, SUPPLICANT_RESTART_INTERVAL_MSECS);
2197                     } else {
2198                         loge("Failed " + mSupplicantRestartCount +
2199                                 " times to start supplicant, unload driver");
2200                         mSupplicantRestartCount = 0;
2201                         setWifiState(WIFI_STATE_UNKNOWN);
2202                         transitionTo(mInitialState);
2203                     }
2204                     break;
2205                 case CMD_START_SUPPLICANT:
2206                 case CMD_STOP_SUPPLICANT:
2207                 case CMD_START_AP:
2208                 case CMD_STOP_AP:
2209                 case CMD_START_DRIVER:
2210                 case CMD_STOP_DRIVER:
2211                 case CMD_SET_OPERATIONAL_MODE:
2212                 case CMD_SET_COUNTRY_CODE:
2213                 case CMD_SET_FREQUENCY_BAND:
2214                 case CMD_START_PACKET_FILTERING:
2215                 case CMD_STOP_PACKET_FILTERING:
2216                     deferMessage(message);
2217                     break;
2218                 default:
2219                     return NOT_HANDLED;
2220             }
2221             return HANDLED;
2222         }
2223     }
2224
2225     class SupplicantStartedState extends State {
2226         @Override
2227         public void enter() {
2228             /* Wifi is available as long as we have a connection to supplicant */
2229             mNetworkInfo.setIsAvailable(true);
2230
2231             int defaultInterval = mContext.getResources().getInteger(
2232                     R.integer.config_wifi_supplicant_scan_interval);
2233
2234             mSupplicantScanIntervalMs = Settings.Global.getLong(mContext.getContentResolver(),
2235                     Settings.Global.WIFI_SUPPLICANT_SCAN_INTERVAL_MS,
2236                     defaultInterval);
2237
2238             mWifiNative.setScanInterval((int)mSupplicantScanIntervalMs / 1000);
2239         }
2240         @Override
2241         public boolean processMessage(Message message) {
2242             switch(message.what) {
2243                 case CMD_STOP_SUPPLICANT:   /* Supplicant stopped by user */
2244                     if (mP2pSupported) {
2245                         transitionTo(mWaitForP2pDisableState);
2246                     } else {
2247                         transitionTo(mSupplicantStoppingState);
2248                     }
2249                     break;
2250                 case WifiMonitor.SUP_DISCONNECTION_EVENT:  /* Supplicant connection lost */
2251                     loge("Connection lost, restart supplicant");
2252                     handleSupplicantConnectionLoss();
2253                     handleNetworkDisconnect();
2254                     mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
2255                     if (mP2pSupported) {
2256                         transitionTo(mWaitForP2pDisableState);
2257                     } else {
2258                         transitionTo(mInitialState);
2259                     }
2260                     sendMessageDelayed(CMD_START_SUPPLICANT, SUPPLICANT_RESTART_INTERVAL_MSECS);
2261                     break;
2262                 case WifiMonitor.SCAN_RESULTS_EVENT:
2263                     setScanResults();
2264                     sendScanResultsAvailableBroadcast();
2265                     mScanResultIsPending = false;
2266                     break;
2267                 case CMD_PING_SUPPLICANT:
2268                     boolean ok = mWifiNative.ping();
2269                     replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
2270                     break;
2271                     /* Cannot start soft AP while in client mode */
2272                 case CMD_START_AP:
2273                     loge("Failed to start soft AP with a running supplicant");
2274                     setWifiApState(WIFI_AP_STATE_FAILED);
2275                     break;
2276                 case CMD_SET_OPERATIONAL_MODE:
2277                     mOperationalMode = message.arg1;
2278                     break;
2279                 default:
2280                     return NOT_HANDLED;
2281             }
2282             return HANDLED;
2283         }
2284
2285         @Override
2286         public void exit() {
2287             mNetworkInfo.setIsAvailable(false);
2288         }
2289     }
2290
2291     class SupplicantStoppingState extends State {
2292         @Override
2293         public void enter() {
2294             /* Send any reset commands to supplicant before shutting it down */
2295             handleNetworkDisconnect();
2296             if (mDhcpStateMachine != null) {
2297                 mDhcpStateMachine.doQuit();
2298             }
2299
2300             if (DBG) log("stopping supplicant");
2301             if (!mWifiNative.stopSupplicant()) {
2302                 loge("Failed to stop supplicant");
2303             }
2304
2305             /* Send ourselves a delayed message to indicate failure after a wait time */
2306             sendMessageDelayed(obtainMessage(CMD_STOP_SUPPLICANT_FAILED,
2307                     ++mSupplicantStopFailureToken, 0), SUPPLICANT_RESTART_INTERVAL_MSECS);
2308             setWifiState(WIFI_STATE_DISABLING);
2309             mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
2310         }
2311         @Override
2312         public boolean processMessage(Message message) {
2313             switch(message.what) {
2314                 case WifiMonitor.SUP_CONNECTION_EVENT:
2315                     loge("Supplicant connection received while stopping");
2316                     break;
2317                 case WifiMonitor.SUP_DISCONNECTION_EVENT:
2318                     if (DBG) log("Supplicant connection lost");
2319                     handleSupplicantConnectionLoss();
2320                     transitionTo(mInitialState);
2321                     break;
2322                 case CMD_STOP_SUPPLICANT_FAILED:
2323                     if (message.arg1 == mSupplicantStopFailureToken) {
2324                         loge("Timed out on a supplicant stop, kill and proceed");
2325                         handleSupplicantConnectionLoss();
2326                         transitionTo(mInitialState);
2327                     }
2328                     break;
2329                 case CMD_START_SUPPLICANT:
2330                 case CMD_STOP_SUPPLICANT:
2331                 case CMD_START_AP:
2332                 case CMD_STOP_AP:
2333                 case CMD_START_DRIVER:
2334                 case CMD_STOP_DRIVER:
2335                 case CMD_SET_OPERATIONAL_MODE:
2336                 case CMD_SET_COUNTRY_CODE:
2337                 case CMD_SET_FREQUENCY_BAND:
2338                 case CMD_START_PACKET_FILTERING:
2339                 case CMD_STOP_PACKET_FILTERING:
2340                     deferMessage(message);
2341                     break;
2342                 default:
2343                     return NOT_HANDLED;
2344             }
2345             return HANDLED;
2346         }
2347     }
2348
2349     class DriverStartingState extends State {
2350         private int mTries;
2351         @Override
2352         public void enter() {
2353             mTries = 1;
2354             /* Send ourselves a delayed message to start driver a second time */
2355             sendMessageDelayed(obtainMessage(CMD_DRIVER_START_TIMED_OUT,
2356                         ++mDriverStartToken, 0), DRIVER_START_TIME_OUT_MSECS);
2357         }
2358         @Override
2359         public boolean processMessage(Message message) {
2360             switch(message.what) {
2361                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
2362                     SupplicantState state = handleSupplicantStateChange(message);
2363                     /* If suplicant is exiting out of INTERFACE_DISABLED state into
2364                      * a state that indicates driver has started, it is ready to
2365                      * receive driver commands
2366                      */
2367                     if (SupplicantState.isDriverActive(state)) {
2368                         transitionTo(mDriverStartedState);
2369                     }
2370                     break;
2371                 case CMD_DRIVER_START_TIMED_OUT:
2372                     if (message.arg1 == mDriverStartToken) {
2373                         if (mTries >= 2) {
2374                             loge("Failed to start driver after " + mTries);
2375                             transitionTo(mDriverStoppedState);
2376                         } else {
2377                             loge("Driver start failed, retrying");
2378                             mWakeLock.acquire();
2379                             mWifiNative.startDriver();
2380                             mWakeLock.release();
2381
2382                             ++mTries;
2383                             /* Send ourselves a delayed message to start driver again */
2384                             sendMessageDelayed(obtainMessage(CMD_DRIVER_START_TIMED_OUT,
2385                                         ++mDriverStartToken, 0), DRIVER_START_TIME_OUT_MSECS);
2386                         }
2387                     }
2388                     break;
2389                     /* Queue driver commands & connection events */
2390                 case CMD_START_DRIVER:
2391                 case CMD_STOP_DRIVER:
2392                 case WifiMonitor.NETWORK_CONNECTION_EVENT:
2393                 case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
2394                 case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
2395                 case WifiMonitor.WPS_OVERLAP_EVENT:
2396                 case CMD_SET_COUNTRY_CODE:
2397                 case CMD_SET_FREQUENCY_BAND:
2398                 case CMD_START_PACKET_FILTERING:
2399                 case CMD_STOP_PACKET_FILTERING:
2400                 case CMD_START_SCAN:
2401                 case CMD_DISCONNECT:
2402                 case CMD_REASSOCIATE:
2403                 case CMD_RECONNECT:
2404                     deferMessage(message);
2405                     break;
2406                 default:
2407                     return NOT_HANDLED;
2408             }
2409             return HANDLED;
2410         }
2411     }
2412
2413     class DriverStartedState extends State {
2414         @Override
2415         public void enter() {
2416             mIsRunning = true;
2417             mInDelayedStop = false;
2418             mDelayedStopCounter++;
2419             updateBatteryWorkSource(null);
2420             /**
2421              * Enable bluetooth coexistence scan mode when bluetooth connection is active.
2422              * When this mode is on, some of the low-level scan parameters used by the
2423              * driver are changed to reduce interference with bluetooth
2424              */
2425             mWifiNative.setBluetoothCoexistenceScanMode(mBluetoothConnectionActive);
2426             /* set country code */
2427             setCountryCode();
2428             /* set frequency band of operation */
2429             setFrequencyBand();
2430             /* initialize network state */
2431             setNetworkDetailedState(DetailedState.DISCONNECTED);
2432
2433             /* Remove any filtering on Multicast v6 at start */
2434             mWifiNative.stopFilteringMulticastV6Packets();
2435
2436             /* Reset Multicast v4 filtering state */
2437             if (mFilteringMulticastV4Packets.get()) {
2438                 mWifiNative.startFilteringMulticastV4Packets();
2439             } else {
2440                 mWifiNative.stopFilteringMulticastV4Packets();
2441             }
2442
2443             if (mOperationalMode != CONNECT_MODE) {
2444                 mWifiNative.disconnect();
2445                 transitionTo(mScanModeState);
2446             } else {
2447                 /* Driver stop may have disabled networks, enable right after start */
2448                 mWifiConfigStore.enableAllNetworks();
2449                 mWifiNative.reconnect();
2450                 // Status pulls in the current supplicant state and network connection state
2451                 // events over the monitor connection. This helps framework sync up with
2452                 // current supplicant state
2453                 mWifiNative.status();
2454                 transitionTo(mDisconnectedState);
2455             }
2456
2457             // We may have missed screen update at boot
2458             if (mScreenBroadcastReceived.get() == false) {
2459                 PowerManager powerManager = (PowerManager)mContext.getSystemService(
2460                         Context.POWER_SERVICE);
2461                 handleScreenStateChanged(powerManager.isScreenOn());
2462             } else {
2463                 // Set the right suspend mode settings
2464                 mWifiNative.setSuspendOptimizations(mSuspendOptNeedsDisabled == 0
2465                         && mUserWantsSuspendOpt.get());
2466             }
2467             mWifiNative.setPowerSave(true);
2468
2469             if (mP2pSupported) mWifiP2pChannel.sendMessage(WifiStateMachine.CMD_ENABLE_P2P);
2470
2471             final Intent intent = new Intent(WifiManager.WIFI_SCAN_AVAILABLE);
2472             intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
2473             intent.putExtra(WifiManager.EXTRA_SCAN_AVAILABLE, WIFI_STATE_ENABLED);
2474             mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
2475         }
2476
2477         @Override
2478         public boolean processMessage(Message message) {
2479             switch(message.what) {
2480                 case CMD_START_SCAN:
2481                     noteScanStart(message.arg1);
2482                     startScanNative(WifiNative.SCAN_WITH_CONNECTION_SETUP);
2483                     break;
2484                 case CMD_SET_COUNTRY_CODE:
2485                     String country = (String) message.obj;
2486                     if (DBG) log("set country code " + country);
2487                     if (!mWifiNative.setCountryCode(country.toUpperCase())) {
2488                         loge("Failed to set country code " + country);
2489                     }
2490                     break;
2491                 case CMD_SET_FREQUENCY_BAND:
2492                     int band =  message.arg1;
2493                     if (DBG) log("set frequency band " + band);
2494                     if (mWifiNative.setBand(band)) {
2495                         mFrequencyBand.set(band);
2496                         //Fetch the latest scan results when frequency band is set
2497                         startScanNative(WifiNative.SCAN_WITH_CONNECTION_SETUP);
2498                     } else {
2499                         loge("Failed to set frequency band " + band);
2500                     }
2501                     break;
2502                 case CMD_BLUETOOTH_ADAPTER_STATE_CHANGE:
2503                     mBluetoothConnectionActive = (message.arg1 !=
2504                             BluetoothAdapter.STATE_DISCONNECTED);
2505                     mWifiNative.setBluetoothCoexistenceScanMode(mBluetoothConnectionActive);
2506                     break;
2507                 case CMD_STOP_DRIVER:
2508                     int mode = message.arg1;
2509
2510                     /* Already doing a delayed stop */
2511                     if (mInDelayedStop) {
2512                         if (DBG) log("Already in delayed stop");
2513                         break;
2514                     }
2515                     /* disconnect right now, but leave the driver running for a bit */
2516                     mWifiConfigStore.disableAllNetworks();
2517
2518                     mInDelayedStop = true;
2519                     mDelayedStopCounter++;
2520                     if (DBG) log("Delayed stop message " + mDelayedStopCounter);
2521
2522                     /* send regular delayed shut down */
2523                     Intent driverStopIntent = new Intent(ACTION_DELAYED_DRIVER_STOP, null);
2524                     driverStopIntent.putExtra(DELAYED_STOP_COUNTER, mDelayedStopCounter);
2525                     mDriverStopIntent = PendingIntent.getBroadcast(mContext,
2526                             DRIVER_STOP_REQUEST, driverStopIntent,
2527                             PendingIntent.FLAG_UPDATE_CURRENT);
2528
2529                     mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()
2530                             + mDriverStopDelayMs, mDriverStopIntent);
2531                     break;
2532                 case CMD_START_DRIVER:
2533                     if (mInDelayedStop) {
2534                         mInDelayedStop = false;
2535                         mDelayedStopCounter++;
2536                         mAlarmManager.cancel(mDriverStopIntent);
2537                         if (DBG) log("Delayed stop ignored due to start");
2538                         if (mOperationalMode == CONNECT_MODE) {
2539                             mWifiConfigStore.enableAllNetworks();
2540                         }
2541                     }
2542                     break;
2543                 case CMD_DELAYED_STOP_DRIVER:
2544                     if (DBG) log("delayed stop " + message.arg1 + " " + mDelayedStopCounter);
2545                     if (message.arg1 != mDelayedStopCounter) break;
2546                     if (getCurrentState() != mDisconnectedState) {
2547                         mWifiNative.disconnect();
2548                         handleNetworkDisconnect();
2549                     }
2550                     mWakeLock.acquire();
2551                     mWifiNative.stopDriver();
2552                     mWakeLock.release();
2553                     if (mP2pSupported) {
2554                         transitionTo(mWaitForP2pDisableState);
2555                     } else {
2556                         transitionTo(mDriverStoppingState);
2557                     }
2558                     break;
2559                 case CMD_START_PACKET_FILTERING:
2560                     if (message.arg1 == MULTICAST_V6) {
2561                         mWifiNative.startFilteringMulticastV6Packets();
2562                     } else if (message.arg1 == MULTICAST_V4) {
2563                         mWifiNative.startFilteringMulticastV4Packets();
2564                     } else {
2565                         loge("Illegal arugments to CMD_START_PACKET_FILTERING");
2566                     }
2567                     break;
2568                 case CMD_STOP_PACKET_FILTERING:
2569                     if (message.arg1 == MULTICAST_V6) {
2570                         mWifiNative.stopFilteringMulticastV6Packets();
2571                     } else if (message.arg1 == MULTICAST_V4) {
2572                         mWifiNative.stopFilteringMulticastV4Packets();
2573                     } else {
2574                         loge("Illegal arugments to CMD_STOP_PACKET_FILTERING");
2575                     }
2576                     break;
2577                 case CMD_SET_SUSPEND_OPT_ENABLED:
2578                     if (message.arg1 == 1) {
2579                         setSuspendOptimizationsNative(SUSPEND_DUE_TO_SCREEN, true);
2580                         mSuspendWakeLock.release();
2581                     } else {
2582                         setSuspendOptimizationsNative(SUSPEND_DUE_TO_SCREEN, false);
2583                     }
2584                     break;
2585                 case CMD_SET_HIGH_PERF_MODE:
2586                     if (message.arg1 == 1) {
2587                         setSuspendOptimizationsNative(SUSPEND_DUE_TO_HIGH_PERF, false);
2588                     } else {
2589                         setSuspendOptimizationsNative(SUSPEND_DUE_TO_HIGH_PERF, true);
2590                     }
2591                     break;
2592                 default:
2593                     return NOT_HANDLED;
2594             }
2595             return HANDLED;
2596         }
2597         @Override
2598         public void exit() {
2599             mIsRunning = false;
2600             updateBatteryWorkSource(null);
2601             mScanResults = new ArrayList<ScanResult>();
2602
2603             final Intent intent = new Intent(WifiManager.WIFI_SCAN_AVAILABLE);
2604             intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
2605             intent.putExtra(WifiManager.EXTRA_SCAN_AVAILABLE, WIFI_STATE_DISABLED);
2606             mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
2607             noteScanEnd(); // wrap up any pending request.
2608         }
2609     }
2610
2611     class WaitForP2pDisableState extends State {
2612         private State mTransitionToState;
2613         @Override
2614         public void enter() {
2615             switch (getCurrentMessage().what) {
2616                 case WifiMonitor.SUP_DISCONNECTION_EVENT:
2617                     mTransitionToState = mInitialState;
2618                     break;
2619                 case CMD_DELAYED_STOP_DRIVER:
2620                     mTransitionToState = mDriverStoppingState;
2621                     break;
2622                 case CMD_STOP_SUPPLICANT:
2623                     mTransitionToState = mSupplicantStoppingState;
2624                     break;
2625                 default:
2626                     mTransitionToState = mDriverStoppingState;
2627                     break;
2628             }
2629             mWifiP2pChannel.sendMessage(WifiStateMachine.CMD_DISABLE_P2P_REQ);
2630         }
2631         @Override
2632         public boolean processMessage(Message message) {
2633             switch(message.what) {
2634                 case WifiStateMachine.CMD_DISABLE_P2P_RSP:
2635                     transitionTo(mTransitionToState);
2636                     break;
2637                 /* Defer wifi start/shut and driver commands */
2638                 case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
2639                 case CMD_START_SUPPLICANT:
2640                 case CMD_STOP_SUPPLICANT:
2641                 case CMD_START_AP:
2642                 case CMD_STOP_AP:
2643                 case CMD_START_DRIVER:
2644                 case CMD_STOP_DRIVER:
2645                 case CMD_SET_OPERATIONAL_MODE:
2646                 case CMD_SET_COUNTRY_CODE:
2647                 case CMD_SET_FREQUENCY_BAND:
2648                 case CMD_START_PACKET_FILTERING:
2649                 case CMD_STOP_PACKET_FILTERING:
2650                 case CMD_START_SCAN:
2651                 case CMD_DISCONNECT:
2652                 case CMD_REASSOCIATE:
2653                 case CMD_RECONNECT:
2654                     deferMessage(message);
2655                     break;
2656                 default:
2657                     return NOT_HANDLED;
2658             }
2659             return HANDLED;
2660         }
2661     }
2662
2663     class DriverStoppingState extends State {
2664         @Override
2665         public boolean processMessage(Message message) {
2666             switch(message.what) {
2667                 case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
2668                     SupplicantState state = handleSupplicantStateChange(message);
2669                     if (state == SupplicantState.INTERFACE_DISABLED) {
2670                         transitionTo(mDriverStoppedState);
2671                     }
2672                     break;
2673                     /* Queue driver commands */
2674                 case CMD_START_DRIVER:
2675                 case CMD_STOP_DRIVER:
2676                 case CMD_SET_COUNTRY_CODE:
2677                 case CMD_SET_FREQUENCY_BAND:
2678                 case CMD_START_PACKET_FILTERING:
2679                 case CMD_STOP_PACKET_FILTERING:
2680                 case CMD_START_SCAN:
2681                 case CMD_DISCONNECT:
2682                 case CMD_REASSOCIATE:
2683                 case CMD_RECONNECT:
2684                     deferMessage(message);
2685                     break;
2686                 default:
2687                     return NOT_HANDLED;
2688             }
2689             return HANDLED;
2690         }
2691     }
2692
2693     class DriverStoppedState extends State {
2694         @Override
2695         public boolean processMessage(Message message) {
2696             switch (message.what) {
2697                 case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
2698                     StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
2699                     SupplicantState state = stateChangeResult.state;
2700                     // A WEXT bug means that we can be back to driver started state
2701                     // unexpectedly
2702                     if (SupplicantState.isDriverActive(state)) {
2703                         transitionTo(mDriverStartedState);
2704                     }
2705                     break;
2706                 case CMD_START_DRIVER:
2707                     mWakeLock.acquire();
2708                     mWifiNative.startDriver();
2709                     mWakeLock.release();
2710                     transitionTo(mDriverStartingState);
2711                     break;
2712                 default:
2713                     return NOT_HANDLED;
2714             }
2715             return HANDLED;
2716         }
2717     }
2718
2719     class ScanModeState extends State {
2720         private int mLastOperationMode;
2721         @Override
2722         public void enter() {
2723             mWifiConfigStore.disableAllNetworks();
2724             mLastOperationMode = mOperationalMode;
2725             if (mLastOperationMode == SCAN_ONLY_WITH_WIFI_OFF_MODE) {
2726                 mWifiP2pChannel.sendMessage(CMD_DISABLE_P2P_REQ);
2727                 setWifiState(WIFI_STATE_DISABLED);
2728             }
2729         }
2730         @Override
2731         public void exit() {
2732             if (mLastOperationMode == SCAN_ONLY_WITH_WIFI_OFF_MODE) {
2733                 setWifiState(WIFI_STATE_ENABLED);
2734                 // Load and re-enable networks when going back to enabled state
2735                 // This is essential for networks to show up after restore
2736                 mWifiConfigStore.loadAndEnableAllNetworks();
2737                 mWifiP2pChannel.sendMessage(CMD_ENABLE_P2P);
2738             } else {
2739                 mWifiConfigStore.enableAllNetworks();
2740             }
2741             mWifiNative.reconnect();
2742         }
2743         @Override
2744         public boolean processMessage(Message message) {
2745             switch(message.what) {
2746                 case CMD_SET_OPERATIONAL_MODE:
2747                     if (message.arg1 == CONNECT_MODE) {
2748                         mOperationalMode = CONNECT_MODE;
2749                         transitionTo(mDisconnectedState);
2750                     } else {
2751                         // Nothing to do
2752                         return HANDLED;
2753                     }
2754                     break;
2755                 // Handle scan. All the connection related commands are
2756                 // handled only in ConnectModeState
2757                 case CMD_START_SCAN:
2758                     noteScanStart(message.arg1);
2759                     startScanNative(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP);
2760                     break;
2761                 default:
2762                     return NOT_HANDLED;
2763             }
2764             return HANDLED;
2765         }
2766     }
2767
2768     class ConnectModeState extends State {
2769         @Override
2770         public boolean processMessage(Message message) {
2771             WifiConfiguration config;
2772             boolean ok;
2773             switch(message.what) {
2774                 case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
2775                     mSupplicantStateTracker.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT);
2776                     break;
2777                 case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
2778                     SupplicantState state = handleSupplicantStateChange(message);
2779                     // A driver/firmware hang can now put the interface in a down state.
2780                     // We detect the interface going down and recover from it
2781                     if (!SupplicantState.isDriverActive(state)) {
2782                         if (mNetworkInfo.getState() != NetworkInfo.State.DISCONNECTED) {
2783                             handleNetworkDisconnect();
2784                         }
2785                         log("Detected an interface down, restart driver");
2786                         transitionTo(mDriverStoppedState);
2787                         sendMessage(CMD_START_DRIVER);
2788                         break;
2789                     }
2790
2791                     // Supplicant can fail to report a NETWORK_DISCONNECTION_EVENT
2792                     // when authentication times out after a successful connection,
2793                     // we can figure this from the supplicant state. If supplicant
2794                     // state is DISCONNECTED, but the mNetworkInfo says we are not
2795                     // disconnected, we need to handle a disconnection
2796                     if (state == SupplicantState.DISCONNECTED &&
2797                             mNetworkInfo.getState() != NetworkInfo.State.DISCONNECTED) {
2798                         if (DBG) log("Missed CTRL-EVENT-DISCONNECTED, disconnect");
2799                         handleNetworkDisconnect();
2800                         transitionTo(mDisconnectedState);
2801                     }
2802                     break;
2803                 case WifiP2pService.DISCONNECT_WIFI_REQUEST:
2804                     if (message.arg1 == 1) {
2805                         mWifiNative.disconnect();
2806                         mTemporarilyDisconnectWifi = true;
2807                     } else {
2808                         mWifiNative.reconnect();
2809                         mTemporarilyDisconnectWifi = false;
2810                     }
2811                     break;
2812                 case CMD_ADD_OR_UPDATE_NETWORK:
2813                     config = (WifiConfiguration) message.obj;
2814                     replyToMessage(message, CMD_ADD_OR_UPDATE_NETWORK,
2815                             mWifiConfigStore.addOrUpdateNetwork(config));
2816                     break;
2817                 case CMD_REMOVE_NETWORK:
2818                     ok = mWifiConfigStore.removeNetwork(message.arg1);
2819                     replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
2820                     break;
2821                 case CMD_ENABLE_NETWORK:
2822                     ok = mWifiConfigStore.enableNetwork(message.arg1, message.arg2 == 1);
2823                     replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
2824                     break;
2825                 case CMD_ENABLE_ALL_NETWORKS:
2826                     long time =  android.os.SystemClock.elapsedRealtime();
2827                     if (time - mLastEnableAllNetworksTime > MIN_INTERVAL_ENABLE_ALL_NETWORKS_MS) {
2828                         mWifiConfigStore.enableAllNetworks();
2829                         mLastEnableAllNetworksTime = time;
2830                     }
2831                     break;
2832                 case WifiManager.DISABLE_NETWORK:
2833                     if (mWifiConfigStore.disableNetwork(message.arg1,
2834                             WifiConfiguration.DISABLED_UNKNOWN_REASON) == true) {
2835                         replyToMessage(message, WifiManager.DISABLE_NETWORK_SUCCEEDED);
2836                     } else {
2837                         replyToMessage(message, WifiManager.DISABLE_NETWORK_FAILED,
2838                                 WifiManager.ERROR);
2839                     }
2840                     break;
2841                 case CMD_BLACKLIST_NETWORK:
2842                     mWifiNative.addToBlacklist((String)message.obj);
2843                     break;
2844                 case CMD_CLEAR_BLACKLIST:
2845                     mWifiNative.clearBlacklist();
2846                     break;
2847                 case CMD_SAVE_CONFIG:
2848                     ok = mWifiConfigStore.saveConfig();
2849                     replyToMessage(message, CMD_SAVE_CONFIG, ok ? SUCCESS : FAILURE);
2850
2851                     // Inform the backup manager about a data change
2852                     IBackupManager ibm = IBackupManager.Stub.asInterface(
2853                             ServiceManager.getService(Context.BACKUP_SERVICE));
2854                     if (ibm != null) {
2855                         try {
2856                             ibm.dataChanged("com.android.providers.settings");
2857                         } catch (Exception e) {
2858                             // Try again later
2859                         }
2860                     }
2861                     break;
2862                 case CMD_GET_CONFIGURED_NETWORKS:
2863                     replyToMessage(message, message.what,
2864                             mWifiConfigStore.getConfiguredNetworks());
2865                     break;
2866                     /* Do a redundant disconnect without transition */
2867                 case CMD_DISCONNECT:
2868                     mWifiNative.disconnect();
2869                     break;
2870                 case CMD_RECONNECT:
2871                     mWifiNative.reconnect();
2872                     break;
2873                 case CMD_REASSOCIATE:
2874                     mWifiNative.reassociate();
2875                     break;
2876                 case WifiManager.CONNECT_NETWORK:
2877                     /* The connect message can contain a network id passed as arg1 on message or
2878                      * or a config passed as obj on message.
2879                      * For a new network, a config is passed to create and connect.
2880                      * For an existing network, a network id is passed
2881                      */
2882                     int netId = message.arg1;
2883                     config = (WifiConfiguration) message.obj;
2884
2885                     /* Save the network config */
2886                     if (config != null) {
2887                         NetworkUpdateResult result = mWifiConfigStore.saveNetwork(config);
2888                         netId = result.getNetworkId();
2889                     }
2890
2891                     if (mWifiConfigStore.selectNetwork(netId) &&
2892                             mWifiNative.reconnect()) {
2893                         /* The state tracker handles enabling networks upon completion/failure */
2894                         mSupplicantStateTracker.sendMessage(WifiManager.CONNECT_NETWORK);
2895                         replyToMessage(message, WifiManager.CONNECT_NETWORK_SUCCEEDED);
2896                         /* Expect a disconnection from the old connection */
2897                         transitionTo(mDisconnectingState);
2898                     } else {
2899                         loge("Failed to connect config: " + config + " netId: " + netId);
2900                         replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
2901                                 WifiManager.ERROR);
2902                         break;
2903                     }
2904                     break;
2905                 case WifiManager.SAVE_NETWORK:
2906                     config = (WifiConfiguration) message.obj;
2907                     NetworkUpdateResult result = mWifiConfigStore.saveNetwork(config);
2908                     if (result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID) {
2909                         replyToMessage(message, WifiManager.SAVE_NETWORK_SUCCEEDED);
2910                     } else {
2911                         loge("Failed to save network");
2912                         replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED,
2913                                 WifiManager.ERROR);
2914                     }
2915                     break;
2916                 case WifiManager.FORGET_NETWORK:
2917                     if (mWifiConfigStore.forgetNetwork(message.arg1)) {
2918                         replyToMessage(message, WifiManager.FORGET_NETWORK_SUCCEEDED);
2919                     } else {
2920                         loge("Failed to forget network");
2921                         replyToMessage(message, WifiManager.FORGET_NETWORK_FAILED,
2922                                 WifiManager.ERROR);
2923                     }
2924                     break;
2925                 case WifiManager.START_WPS:
2926                     WpsInfo wpsInfo = (WpsInfo) message.obj;
2927                     WpsResult wpsResult;
2928                     switch (wpsInfo.setup) {
2929                         case WpsInfo.PBC:
2930                             wpsResult = mWifiConfigStore.startWpsPbc(wpsInfo);
2931                             break;
2932                         case WpsInfo.KEYPAD:
2933                             wpsResult = mWifiConfigStore.startWpsWithPinFromAccessPoint(wpsInfo);
2934                             break;
2935                         case WpsInfo.DISPLAY:
2936                             wpsResult = mWifiConfigStore.startWpsWithPinFromDevice(wpsInfo);
2937                             break;
2938                         default:
2939                             wpsResult = new WpsResult(Status.FAILURE);
2940                             loge("Invalid setup for WPS");
2941                             break;
2942                     }
2943                     if (wpsResult.status == Status.SUCCESS) {
2944                         replyToMessage(message, WifiManager.START_WPS_SUCCEEDED, wpsResult);
2945                         transitionTo(mWpsRunningState);
2946                     } else {
2947                         loge("Failed to start WPS with config " + wpsInfo.toString());
2948                         replyToMessage(message, WifiManager.WPS_FAILED, WifiManager.ERROR);
2949                     }
2950                     break;
2951                 case WifiMonitor.NETWORK_CONNECTION_EVENT:
2952                     if (DBG) log("Network connection established");
2953                     mLastNetworkId = message.arg1;
2954                     mLastBssid = (String) message.obj;
2955
2956                     mWifiInfo.setBSSID(mLastBssid);
2957                     mWifiInfo.setNetworkId(mLastNetworkId);
2958                     /* send event to CM & network change broadcast */
2959                     setNetworkDetailedState(DetailedState.OBTAINING_IPADDR);
2960                     sendNetworkStateChangeBroadcast(mLastBssid);
2961                     transitionTo(mObtainingIpState);
2962                     break;
2963                 case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
2964                     if (DBG) log("Network connection lost");
2965                     handleNetworkDisconnect();
2966                     transitionTo(mDisconnectedState);
2967                     break;
2968                 default:
2969                     return NOT_HANDLED;
2970             }
2971             return HANDLED;
2972         }
2973     }
2974
2975     class L2ConnectedState extends State {
2976         @Override
2977         public void enter() {
2978             mRssiPollToken++;
2979             if (mEnableRssiPolling) {
2980                 sendMessage(CMD_RSSI_POLL, mRssiPollToken, 0);
2981             }
2982         }
2983
2984         @Override
2985         public boolean processMessage(Message message) {
2986             switch (message.what) {
2987               case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
2988                   handlePreDhcpSetup();
2989                   mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_PRE_DHCP_ACTION_COMPLETE);
2990                   break;
2991               case DhcpStateMachine.CMD_POST_DHCP_ACTION:
2992                   handlePostDhcpSetup();
2993                   if (message.arg1 == DhcpStateMachine.DHCP_SUCCESS) {
2994                       if (DBG) log("DHCP successful");
2995                       handleSuccessfulIpConfiguration((DhcpResults) message.obj);
2996                       transitionTo(mVerifyingLinkState);
2997                   } else if (message.arg1 == DhcpStateMachine.DHCP_FAILURE) {
2998                       if (DBG) log("DHCP failed");
2999                       handleFailedIpConfiguration();
3000                       transitionTo(mDisconnectingState);
3001                   }
3002                   break;
3003                 case CMD_DISCONNECT:
3004                     mWifiNative.disconnect();
3005                     transitionTo(mDisconnectingState);
3006                     break;
3007                 case WifiP2pService.DISCONNECT_WIFI_REQUEST:
3008                     if (message.arg1 == 1) {
3009                         mWifiNative.disconnect();
3010                         mTemporarilyDisconnectWifi = true;
3011                         transitionTo(mDisconnectingState);
3012                     }
3013                     break;
3014                 case CMD_SET_OPERATIONAL_MODE:
3015                     if (message.arg1 != CONNECT_MODE) {
3016                         sendMessage(CMD_DISCONNECT);
3017                         deferMessage(message);
3018                     }
3019                     break;
3020                 case CMD_START_SCAN:
3021                     /* Do not attempt to connect when we are already connected */
3022                     noteScanStart(message.arg1);
3023                     startScanNative(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP);
3024                     break;
3025                     /* Ignore connection to same network */
3026                 case WifiManager.CONNECT_NETWORK:
3027                     int netId = message.arg1;
3028                     if (mWifiInfo.getNetworkId() == netId) {
3029                         break;
3030                     }
3031                     return NOT_HANDLED;
3032                 case WifiManager.SAVE_NETWORK:
3033                     WifiConfiguration config = (WifiConfiguration) message.obj;
3034                     NetworkUpdateResult result = mWifiConfigStore.saveNetwork(config);
3035                     if (mWifiInfo.getNetworkId() == result.getNetworkId()) {
3036                         if (result.hasIpChanged()) {
3037                             log("Reconfiguring IP on connection");
3038                             transitionTo(mObtainingIpState);
3039                         }
3040                         if (result.hasProxyChanged()) {
3041                             log("Reconfiguring proxy on connection");
3042                             configureLinkProperties();
3043                             sendLinkConfigurationChangedBroadcast();
3044                         }
3045                     }
3046
3047                     if (result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID) {
3048                         replyToMessage(message, WifiManager.SAVE_NETWORK_SUCCEEDED);
3049                     } else {
3050                         loge("Failed to save network");
3051                         replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED,
3052                                 WifiManager.ERROR);
3053                     }
3054                     break;
3055                     /* Ignore */
3056                 case WifiMonitor.NETWORK_CONNECTION_EVENT:
3057                     break;
3058                 case CMD_RSSI_POLL:
3059                     if (message.arg1 == mRssiPollToken) {
3060                         // Get Info and continue polling
3061                         fetchRssiAndLinkSpeedNative();
3062                         sendMessageDelayed(obtainMessage(CMD_RSSI_POLL,
3063                                 mRssiPollToken, 0), POLL_RSSI_INTERVAL_MSECS);
3064                     } else {
3065                         // Polling has completed
3066                     }
3067                     break;
3068                 case CMD_ENABLE_RSSI_POLL:
3069                     mEnableRssiPolling = (message.arg1 == 1);
3070                     mRssiPollToken++;
3071                     if (mEnableRssiPolling) {
3072                         // first poll
3073                         fetchRssiAndLinkSpeedNative();
3074                         sendMessageDelayed(obtainMessage(CMD_RSSI_POLL,
3075                                 mRssiPollToken, 0), POLL_RSSI_INTERVAL_MSECS);
3076                     }
3077                     break;
3078                 case WifiManager.RSSI_PKTCNT_FETCH:
3079                     RssiPacketCountInfo info = new RssiPacketCountInfo();
3080                     fetchRssiAndLinkSpeedNative();
3081                     info.rssi = mWifiInfo.getRssi();
3082                     fetchPktcntNative(info);
3083                     replyToMessage(message, WifiManager.RSSI_PKTCNT_FETCH_SUCCEEDED, info);
3084                     break;
3085                 default:
3086                     return NOT_HANDLED;
3087             }
3088
3089             return HANDLED;
3090         }
3091     }
3092
3093     class ObtainingIpState extends State {
3094         @Override
3095         public void enter() {
3096             if (!mWifiConfigStore.isUsingStaticIp(mLastNetworkId)) {
3097                 startDhcp();
3098             } else {
3099                 // stop any running dhcp before assigning static IP
3100                 stopDhcp();
3101                 DhcpResults dhcpResults = new DhcpResults(
3102                         mWifiConfigStore.getLinkProperties(mLastNetworkId));
3103                 dhcpResults.linkProperties.setInterfaceName(mInterfaceName);
3104                 InterfaceConfiguration ifcg = new InterfaceConfiguration();
3105                 Iterator<LinkAddress> addrs =
3106                         dhcpResults.linkProperties.getLinkAddresses().iterator();
3107                 if (!addrs.hasNext()) {
3108                     loge("Static IP lacks address");
3109                     sendMessage(CMD_STATIC_IP_FAILURE);
3110                 } else {
3111                     ifcg.setLinkAddress(addrs.next());
3112                     ifcg.setInterfaceUp();
3113                     try {
3114                         mNwService.setInterfaceConfig(mInterfaceName, ifcg);
3115                         if (DBG) log("Static IP configuration succeeded");
3116                         sendMessage(CMD_STATIC_IP_SUCCESS, dhcpResults);
3117                     } catch (RemoteException re) {
3118                         loge("Static IP configuration failed: " + re);
3119                         sendMessage(CMD_STATIC_IP_FAILURE);
3120                     } catch (IllegalStateException e) {
3121                         loge("Static IP configuration failed: " + e);
3122                         sendMessage(CMD_STATIC_IP_FAILURE);
3123                     }
3124                 }
3125             }
3126         }
3127       @Override
3128       public boolean processMessage(Message message) {
3129           if (DBG) log(getName() + message.toString() + "\n");
3130           switch(message.what) {
3131             case CMD_STATIC_IP_SUCCESS:
3132                   handleSuccessfulIpConfiguration((DhcpResults) message.obj);
3133                   transitionTo(mVerifyingLinkState);
3134                   break;
3135               case CMD_STATIC_IP_FAILURE:
3136                   handleFailedIpConfiguration();
3137                   transitionTo(mDisconnectingState);
3138                   break;
3139              case WifiManager.SAVE_NETWORK:
3140                   deferMessage(message);
3141                   break;
3142                   /* Defer any power mode changes since we must keep active power mode at DHCP */
3143               case CMD_SET_HIGH_PERF_MODE:
3144                   deferMessage(message);
3145                   break;
3146                   /* Defer scan request since we should not switch to other channels at DHCP */
3147               case CMD_START_SCAN:
3148                   deferMessage(message);
3149                   break;
3150               default:
3151                   return NOT_HANDLED;
3152           }
3153           return HANDLED;
3154       }
3155     }
3156
3157     class VerifyingLinkState extends State {
3158         @Override
3159         public void enter() {
3160             setNetworkDetailedState(DetailedState.VERIFYING_POOR_LINK);
3161             mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.VERIFYING_POOR_LINK);
3162             sendNetworkStateChangeBroadcast(mLastBssid);
3163         }
3164         @Override
3165         public boolean processMessage(Message message) {
3166             switch (message.what) {
3167                 case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
3168                     //stay here
3169                     break;
3170                 case WifiWatchdogStateMachine.GOOD_LINK_DETECTED:
3171                     transitionTo(mCaptivePortalCheckState);
3172                     break;
3173                 default:
3174                     return NOT_HANDLED;
3175             }
3176             return HANDLED;
3177         }
3178     }
3179
3180     class CaptivePortalCheckState extends State {
3181         @Override
3182         public void enter() {
3183             setNetworkDetailedState(DetailedState.CAPTIVE_PORTAL_CHECK);
3184             mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.CAPTIVE_PORTAL_CHECK);
3185             sendNetworkStateChangeBroadcast(mLastBssid);
3186         }
3187         @Override
3188         public boolean processMessage(Message message) {
3189             switch (message.what) {
3190                 case CMD_CAPTIVE_CHECK_COMPLETE:
3191                     try {
3192                         mNwService.enableIpv6(mInterfaceName);
3193                     } catch (RemoteException re) {
3194                         loge("Failed to enable IPv6: " + re);
3195                     } catch (IllegalStateException e) {
3196                         loge("Failed to enable IPv6: " + e);
3197                     }
3198                     setNetworkDetailedState(DetailedState.CONNECTED);
3199                     mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.CONNECTED);
3200                     sendNetworkStateChangeBroadcast(mLastBssid);
3201                     transitionTo(mConnectedState);
3202                     break;
3203                 default:
3204                     return NOT_HANDLED;
3205             }
3206             return HANDLED;
3207         }
3208     }
3209
3210     class ConnectedState extends State {
3211         @Override
3212         public boolean processMessage(Message message) {
3213             switch (message.what) {
3214                case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
3215                     if (DBG) log("Watchdog reports poor link");
3216                     try {
3217                         mNwService.disableIpv6(mInterfaceName);
3218                     } catch (RemoteException re) {
3219                         loge("Failed to disable IPv6: " + re);
3220                     } catch (IllegalStateException e) {
3221                         loge("Failed to disable IPv6: " + e);
3222                     }
3223                     /* Report a disconnect */
3224                     setNetworkDetailedState(DetailedState.DISCONNECTED);
3225                     mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.DISCONNECTED);
3226                     sendNetworkStateChangeBroadcast(mLastBssid);
3227
3228                     transitionTo(mVerifyingLinkState);
3229                     break;
3230                 default:
3231                     return NOT_HANDLED;
3232             }
3233             return HANDLED;
3234         }
3235         @Override
3236         public void exit() {
3237             /* Request a CS wakelock during transition to mobile */
3238             checkAndSetConnectivityInstance();
3239             mCm.requestNetworkTransitionWakelock(getName());
3240         }
3241     }
3242
3243     class DisconnectingState extends State {
3244         @Override
3245         public boolean processMessage(Message message) {
3246             switch (message.what) {
3247                 case CMD_SET_OPERATIONAL_MODE:
3248                     if (message.arg1 != CONNECT_MODE) {
3249                         deferMessage(message);
3250                     }
3251                     break;
3252                 case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
3253                     /* If we get a SUPPLICANT_STATE_CHANGE_EVENT before NETWORK_DISCONNECTION_EVENT
3254                      * we have missed the network disconnection, transition to mDisconnectedState
3255                      * and handle the rest of the events there
3256                      */
3257                     deferMessage(message);
3258                     handleNetworkDisconnect();
3259                     transitionTo(mDisconnectedState);
3260                     break;
3261                 default:
3262                     return NOT_HANDLED;
3263             }
3264             return HANDLED;
3265         }
3266     }
3267
3268     class DisconnectedState extends State {
3269         private boolean mAlarmEnabled = false;
3270         /* This is set from the overlay config file or from a secure setting.
3271          * A value of 0 disables scanning in the framework.
3272          */
3273         private long mFrameworkScanIntervalMs;
3274
3275         private void setScanAlarm(boolean enabled) {
3276             if (enabled == mAlarmEnabled) return;
3277             if (enabled) {
3278                 if (mFrameworkScanIntervalMs > 0) {
3279                     mAlarmManager.setRepeating(AlarmManager.RTC_WAKEUP,
3280                             System.currentTimeMillis() + mFrameworkScanIntervalMs,
3281                             mFrameworkScanIntervalMs,
3282                             mScanIntent);
3283                     mAlarmEnabled = true;
3284                 }
3285             } else {
3286                 mAlarmManager.cancel(mScanIntent);
3287                 mAlarmEnabled = false;
3288             }
3289         }
3290
3291         @Override
3292         public void enter() {
3293             // We dont scan frequently if this is a temporary disconnect
3294             // due to p2p
3295             if (mTemporarilyDisconnectWifi) {
3296                 mWifiP2pChannel.sendMessage(WifiP2pService.DISCONNECT_WIFI_RESPONSE);
3297                 return;
3298             }
3299
3300             mFrameworkScanIntervalMs = Settings.Global.getLong(mContext.getContentResolver(),
3301                     Settings.Global.WIFI_FRAMEWORK_SCAN_INTERVAL_MS,
3302                     mDefaultFrameworkScanIntervalMs);
3303             /*
3304              * We initiate background scanning if it is enabled, otherwise we
3305              * initiate an infrequent scan that wakes up the device to ensure
3306              * a user connects to an access point on the move
3307              */
3308             if (mEnableBackgroundScan) {
3309                 /* If a regular scan result is pending, do not initiate background
3310                  * scan until the scan results are returned. This is needed because
3311                  * initiating a background scan will cancel the regular scan and
3312                  * scan results will not be returned until background scanning is
3313                  * cleared
3314                  */
3315                 if (!mScanResultIsPending) {
3316                     mWifiNative.enableBackgroundScan(true);
3317                 }
3318             } else {
3319                 setScanAlarm(true);
3320             }
3321
3322             /**
3323              * If we have no networks saved, the supplicant stops doing the periodic scan.
3324              * The scans are useful to notify the user of the presence of an open network.
3325              * Note that these are not wake up scans.
3326              */
3327             if (!mP2pConnected.get() && mWifiConfigStore.getConfiguredNetworks().size() == 0) {
3328                 sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,
3329                             ++mPeriodicScanToken, 0), mSupplicantScanIntervalMs);
3330             }
3331         }
3332         @Override
3333         public boolean processMessage(Message message) {
3334             boolean ret = HANDLED;
3335             switch (message.what) {
3336                 case CMD_NO_NETWORKS_PERIODIC_SCAN:
3337                     if (mP2pConnected.get()) break;
3338                     if (message.arg1 == mPeriodicScanToken &&
3339                             mWifiConfigStore.getConfiguredNetworks().size() == 0) {
3340                         sendMessage(CMD_START_SCAN, UNKNOWN_SCAN_SOURCE);
3341                         sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,
3342                                     ++mPeriodicScanToken, 0), mSupplicantScanIntervalMs);
3343                     }
3344                     break;
3345                 case WifiManager.FORGET_NETWORK:
3346                 case CMD_REMOVE_NETWORK:
3347                     // Set up a delayed message here. After the forget/remove is handled
3348                     // the handled delayed message will determine if there is a need to
3349                     // scan and continue
3350                     sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,
3351                                 ++mPeriodicScanToken, 0), mSupplicantScanIntervalMs);
3352                     ret = NOT_HANDLED;
3353                     break;
3354                 case CMD_SET_OPERATIONAL_MODE:
3355                     if (message.arg1 != CONNECT_MODE) {
3356                         mOperationalMode = message.arg1;
3357                         transitionTo(mScanModeState);
3358                     }
3359                     break;
3360                 case CMD_ENABLE_BACKGROUND_SCAN:
3361                     mEnableBackgroundScan = (message.arg1 == 1);
3362                     if (mEnableBackgroundScan) {
3363                         mWifiNative.enableBackgroundScan(true);
3364                         setScanAlarm(false);
3365                     } else {
3366                         mWifiNative.enableBackgroundScan(false);
3367                         setScanAlarm(true);
3368                     }
3369                     break;
3370                     /* Ignore network disconnect */
3371                 case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
3372                     break;
3373                 case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
3374                     StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
3375                     setNetworkDetailedState(WifiInfo.getDetailedStateOf(stateChangeResult.state));
3376                     /* ConnectModeState does the rest of the handling */
3377                     ret = NOT_HANDLED;
3378                     break;
3379                 case CMD_START_SCAN:
3380                     /* Disable background scan temporarily during a regular scan */
3381                     if (mEnableBackgroundScan) {
3382                         mWifiNative.enableBackgroundScan(false);
3383                     }
3384                     /* Handled in parent state */
3385                     ret = NOT_HANDLED;
3386                     break;
3387                 case WifiMonitor.SCAN_RESULTS_EVENT:
3388                     /* Re-enable background scan when a pending scan result is received */
3389                     if (mEnableBackgroundScan && mScanResultIsPending) {
3390                         mWifiNative.enableBackgroundScan(true);
3391                     }
3392                     /* Handled in parent state */
3393                     ret = NOT_HANDLED;
3394                     break;
3395                 case WifiP2pService.P2P_CONNECTION_CHANGED:
3396                     NetworkInfo info = (NetworkInfo) message.obj;
3397                     mP2pConnected.set(info.isConnected());
3398                     if (mP2pConnected.get()) {
3399                         int defaultInterval = mContext.getResources().getInteger(
3400                                 R.integer.config_wifi_scan_interval_p2p_connected);
3401                         long scanIntervalMs = Settings.Global.getLong(mContext.getContentResolver(),
3402                                 Settings.Global.WIFI_SCAN_INTERVAL_WHEN_P2P_CONNECTED_MS,
3403                                 defaultInterval);
3404                         mWifiNative.setScanInterval((int) scanIntervalMs/1000);
3405                     } else if (mWifiConfigStore.getConfiguredNetworks().size() == 0) {
3406                         if (DBG) log("Turn on scanning after p2p disconnected");
3407                         sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,
3408                                     ++mPeriodicScanToken, 0), mSupplicantScanIntervalMs);
3409                     }
3410                 case CMD_RECONNECT:
3411                 case CMD_REASSOCIATE:
3412                     if (mTemporarilyDisconnectWifi) {
3413                         // Drop a third party reconnect/reassociate if STA is
3414                         // temporarily disconnected for p2p
3415                         break;
3416                     } else {
3417                         // ConnectModeState handles it
3418                         ret = NOT_HANDLED;
3419                     }
3420                     break;
3421                 default:
3422                     ret = NOT_HANDLED;
3423             }
3424             return ret;
3425         }
3426
3427         @Override
3428         public void exit() {
3429             /* No need for a background scan upon exit from a disconnected state */
3430             if (mEnableBackgroundScan) {
3431                 mWifiNative.enableBackgroundScan(false);
3432             }
3433             setScanAlarm(false);
3434         }
3435     }
3436
3437     class WpsRunningState extends State {
3438         //Tracks the source to provide a reply
3439         private Message mSourceMessage;
3440         @Override
3441         public void enter() {
3442             mSourceMessage = Message.obtain(getCurrentMessage());
3443         }
3444         @Override
3445         public boolean processMessage(Message message) {
3446             switch (message.what) {
3447                 case WifiMonitor.WPS_SUCCESS_EVENT:
3448                     // Ignore intermediate success, wait for full connection
3449                     break;
3450                 case WifiMonitor.NETWORK_CONNECTION_EVENT:
3451                     replyToMessage(mSourceMessage, WifiManager.WPS_COMPLETED);
3452                     mSourceMessage.recycle();
3453                     mSourceMessage = null;
3454                     deferMessage(message);
3455                     transitionTo(mDisconnectedState);
3456                     break;
3457                 case WifiMonitor.WPS_OVERLAP_EVENT:
3458                     replyToMessage(mSourceMessage, WifiManager.WPS_FAILED,
3459                             WifiManager.WPS_OVERLAP_ERROR);
3460                     mSourceMessage.recycle();
3461                     mSourceMessage = null;
3462                     transitionTo(mDisconnectedState);
3463                     break;
3464                 case WifiMonitor.WPS_FAIL_EVENT:
3465                     //arg1 has the reason for the failure
3466                     replyToMessage(mSourceMessage, WifiManager.WPS_FAILED, message.arg1);
3467                     mSourceMessage.recycle();
3468                     mSourceMessage = null;
3469                     transitionTo(mDisconnectedState);
3470                     break;
3471                 case WifiMonitor.WPS_TIMEOUT_EVENT:
3472                     replyToMessage(mSourceMessage, WifiManager.WPS_FAILED,
3473                             WifiManager.WPS_TIMED_OUT);
3474                     mSourceMessage.recycle();
3475                     mSourceMessage = null;
3476                     transitionTo(mDisconnectedState);
3477                     break;
3478                 case WifiManager.START_WPS:
3479                     replyToMessage(message, WifiManager.WPS_FAILED, WifiManager.IN_PROGRESS);
3480                     break;
3481                 case WifiManager.CANCEL_WPS:
3482                     if (mWifiNative.cancelWps()) {
3483                         replyToMessage(message, WifiManager.CANCEL_WPS_SUCCEDED);
3484                     } else {
3485                         replyToMessage(message, WifiManager.CANCEL_WPS_FAILED, WifiManager.ERROR);
3486                     }
3487                     transitionTo(mDisconnectedState);
3488                     break;
3489                 /* Defer all commands that can cause connections to a different network
3490                  * or put the state machine out of connect mode
3491                  */
3492                 case CMD_STOP_DRIVER:
3493                 case CMD_SET_OPERATIONAL_MODE:
3494                 case WifiManager.CONNECT_NETWORK:
3495                 case CMD_ENABLE_NETWORK:
3496                 case CMD_RECONNECT:
3497                 case CMD_REASSOCIATE:
3498                     deferMessage(message);
3499                     break;
3500                 case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
3501                     if (DBG) log("Network connection lost");
3502                     handleNetworkDisconnect();
3503                     break;
3504                 case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
3505                     // Disregard auth failure events during WPS connection. The
3506                     // EAP sequence is retried several times, and there might be
3507                     // failures (especially for wps pin). We will get a WPS_XXX
3508                     // event at the end of the sequence anyway.
3509                     if (DBG) log("Ignore auth failure during WPS connection");
3510                     break;
3511                 case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
3512                     //Throw away supplicant state changes when WPS is running.
3513                     //We will start getting supplicant state changes once we get
3514                     //a WPS success or failure
3515                     break;
3516                 default:
3517                     return NOT_HANDLED;
3518             }
3519             return HANDLED;
3520         }
3521
3522         @Override
3523         public void exit() {
3524             mWifiConfigStore.enableAllNetworks();
3525             mWifiConfigStore.loadConfiguredNetworks();
3526         }
3527     }
3528
3529     class SoftApStartingState extends State {
3530         @Override
3531         public void enter() {
3532             final Message message = getCurrentMessage();
3533             if (message.what == CMD_START_AP) {
3534                 final WifiConfiguration config = (WifiConfiguration) message.obj;
3535
3536                 if (config == null) {
3537                     mWifiApConfigChannel.sendMessage(CMD_REQUEST_AP_CONFIG);
3538                 } else {
3539                     mWifiApConfigChannel.sendMessage(CMD_SET_AP_CONFIG, config);
3540                     startSoftApWithConfig(config);
3541                 }
3542             } else {
3543                 throw new RuntimeException("Illegal transition to SoftApStartingState: " + message);
3544             }
3545         }
3546         @Override
3547         public boolean processMessage(Message message) {
3548             switch(message.what) {
3549                 case CMD_START_SUPPLICANT:
3550                 case CMD_STOP_SUPPLICANT:
3551                 case CMD_START_AP:
3552                 case CMD_STOP_AP:
3553                 case CMD_START_DRIVER:
3554                 case CMD_STOP_DRIVER:
3555                 case CMD_SET_OPERATIONAL_MODE:
3556                 case CMD_SET_COUNTRY_CODE:
3557                 case CMD_SET_FREQUENCY_BAND:
3558                 case CMD_START_PACKET_FILTERING:
3559                 case CMD_STOP_PACKET_FILTERING:
3560                 case CMD_TETHER_STATE_CHANGE:
3561                     deferMessage(message);
3562                     break;
3563                 case WifiStateMachine.CMD_RESPONSE_AP_CONFIG:
3564                     WifiConfiguration config = (WifiConfiguration) message.obj;
3565                     if (config != null) {
3566                         startSoftApWithConfig(config);
3567                     } else {
3568                         loge("Softap config is null!");
3569                         sendMessage(CMD_START_AP_FAILURE);
3570                     }
3571                     break;
3572                 case CMD_START_AP_SUCCESS:
3573                     setWifiApState(WIFI_AP_STATE_ENABLED);
3574                     transitionTo(mSoftApStartedState);
3575                     break;
3576                 case CMD_START_AP_FAILURE:
3577                     setWifiApState(WIFI_AP_STATE_FAILED);
3578                     transitionTo(mInitialState);
3579                     break;
3580                 default:
3581                     return NOT_HANDLED;
3582             }
3583             return HANDLED;
3584         }
3585     }
3586
3587     class SoftApStartedState extends State {
3588         @Override
3589         public boolean processMessage(Message message) {
3590             switch(message.what) {
3591                 case CMD_STOP_AP:
3592                     if (DBG) log("Stopping Soft AP");
3593                     /* We have not tethered at this point, so we just shutdown soft Ap */
3594                     try {
3595                         mNwService.stopAccessPoint(mInterfaceName);
3596                     } catch(Exception e) {
3597                         loge("Exception in stopAccessPoint()");
3598                     }
3599                     setWifiApState(WIFI_AP_STATE_DISABLED);
3600                     transitionTo(mInitialState);
3601                     break;
3602                 case CMD_START_AP:
3603                     // Ignore a start on a running access point
3604                     break;
3605                     /* Fail client mode operation when soft AP is enabled */
3606                 case CMD_START_SUPPLICANT:
3607                     loge("Cannot start supplicant with a running soft AP");
3608                     setWifiState(WIFI_STATE_UNKNOWN);
3609                     break;
3610                 case CMD_TETHER_STATE_CHANGE:
3611                     TetherStateChange stateChange = (TetherStateChange) message.obj;
3612                     if (startTethering(stateChange.available)) {
3613                         transitionTo(mTetheringState);
3614                     }
3615                     break;
3616                 default:
3617                     return NOT_HANDLED;
3618             }
3619             return HANDLED;
3620         }
3621     }
3622
3623     class TetheringState extends State {
3624         @Override
3625         public void enter() {
3626             /* Send ourselves a delayed message to shut down if tethering fails to notify */
3627             sendMessageDelayed(obtainMessage(CMD_TETHER_NOTIFICATION_TIMED_OUT,
3628                     ++mTetherToken, 0), TETHER_NOTIFICATION_TIME_OUT_MSECS);
3629         }
3630         @Override
3631         public boolean processMessage(Message message) {
3632             switch(message.what) {
3633                 case CMD_TETHER_STATE_CHANGE:
3634                     TetherStateChange stateChange = (TetherStateChange) message.obj;
3635                     if (isWifiTethered(stateChange.active)) {
3636                         transitionTo(mTetheredState);
3637                     }
3638                     return HANDLED;
3639                 case CMD_TETHER_NOTIFICATION_TIMED_OUT:
3640                     if (message.arg1 == mTetherToken) {
3641                         loge("Failed to get tether update, shutdown soft access point");
3642                         transitionTo(mSoftApStartedState);
3643                         // Needs to be first thing handled
3644                         sendMessageAtFrontOfQueue(CMD_STOP_AP);
3645                     }
3646                     break;
3647                 case CMD_START_SUPPLICANT:
3648                 case CMD_STOP_SUPPLICANT:
3649                 case CMD_START_AP:
3650                 case CMD_STOP_AP:
3651                 case CMD_START_DRIVER:
3652                 case CMD_STOP_DRIVER:
3653                 case CMD_SET_OPERATIONAL_MODE:
3654                 case CMD_SET_COUNTRY_CODE:
3655                 case CMD_SET_FREQUENCY_BAND:
3656                 case CMD_START_PACKET_FILTERING:
3657                 case CMD_STOP_PACKET_FILTERING:
3658                     deferMessage(message);
3659                     break;
3660                 default:
3661                     return NOT_HANDLED;
3662             }
3663             return HANDLED;
3664         }
3665     }
3666
3667     class TetheredState extends State {
3668         @Override
3669         public boolean processMessage(Message message) {
3670             switch(message.what) {
3671                 case CMD_TETHER_STATE_CHANGE:
3672                     TetherStateChange stateChange = (TetherStateChange) message.obj;
3673                     if (!isWifiTethered(stateChange.active)) {
3674                         loge("Tethering reports wifi as untethered!, shut down soft Ap");
3675                         setHostApRunning(null, false);
3676                     }
3677                     return HANDLED;
3678                 case CMD_STOP_AP:
3679                     if (DBG) log("Untethering before stopping AP");
3680                     setWifiApState(WIFI_AP_STATE_DISABLING);
3681                     stopTethering();
3682                     transitionTo(mUntetheringState);
3683                     // More work to do after untethering
3684                     deferMessage(message);
3685                     break;
3686                 default:
3687                     return NOT_HANDLED;
3688             }
3689             return HANDLED;
3690         }
3691     }
3692
3693     class UntetheringState extends State {
3694         @Override
3695         public void enter() {
3696             /* Send ourselves a delayed message to shut down if tethering fails to notify */
3697             sendMessageDelayed(obtainMessage(CMD_TETHER_NOTIFICATION_TIMED_OUT,
3698                     ++mTetherToken, 0), TETHER_NOTIFICATION_TIME_OUT_MSECS);
3699
3700         }
3701         @Override
3702         public boolean processMessage(Message message) {
3703             switch(message.what) {
3704                 case CMD_TETHER_STATE_CHANGE:
3705                     TetherStateChange stateChange = (TetherStateChange) message.obj;
3706
3707                     /* Wait till wifi is untethered */
3708                     if (isWifiTethered(stateChange.active)) break;
3709
3710                     transitionTo(mSoftApStartedState);
3711                     break;
3712                 case CMD_TETHER_NOTIFICATION_TIMED_OUT:
3713                     if (message.arg1 == mTetherToken) {
3714                         loge("Failed to get tether update, force stop access point");
3715                         transitionTo(mSoftApStartedState);
3716                     }
3717                     break;
3718                 case CMD_START_SUPPLICANT:
3719                 case CMD_STOP_SUPPLICANT:
3720                 case CMD_START_AP:
3721                 case CMD_STOP_AP:
3722                 case CMD_START_DRIVER:
3723                 case CMD_STOP_DRIVER:
3724                 case CMD_SET_OPERATIONAL_MODE:
3725                 case CMD_SET_COUNTRY_CODE:
3726                 case CMD_SET_FREQUENCY_BAND:
3727                 case CMD_START_PACKET_FILTERING:
3728                 case CMD_STOP_PACKET_FILTERING:
3729                     deferMessage(message);
3730                     break;
3731                 default:
3732                     return NOT_HANDLED;
3733             }
3734             return HANDLED;
3735         }
3736     }
3737
3738     //State machine initiated requests can have replyTo set to null indicating
3739     //there are no recepients, we ignore those reply actions
3740     private void replyToMessage(Message msg, int what) {
3741         if (msg.replyTo == null) return;
3742         Message dstMsg = obtainMessageWithArg2(msg);
3743         dstMsg.what = what;
3744         mReplyChannel.replyToMessage(msg, dstMsg);
3745     }
3746
3747     private void replyToMessage(Message msg, int what, int arg1) {
3748         if (msg.replyTo == null) return;
3749         Message dstMsg = obtainMessageWithArg2(msg);
3750         dstMsg.what = what;
3751         dstMsg.arg1 = arg1;
3752         mReplyChannel.replyToMessage(msg, dstMsg);
3753     }
3754
3755     private void replyToMessage(Message msg, int what, Object obj) {
3756         if (msg.replyTo == null) return;
3757         Message dstMsg = obtainMessageWithArg2(msg);
3758         dstMsg.what = what;
3759         dstMsg.obj = obj;
3760         mReplyChannel.replyToMessage(msg, dstMsg);
3761     }
3762
3763     /**
3764      * arg2 on the source message has a unique id that needs to be retained in replies
3765      * to match the request
3766      *
3767      * see WifiManager for details
3768      */
3769     private Message obtainMessageWithArg2(Message srcMsg) {
3770         Message msg = Message.obtain();
3771         msg.arg2 = srcMsg.arg2;
3772         return msg;
3773     }
3774 }