WifiStateMachine.java revision e498475b187277309c81b38240c7e71ec049e369
1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.net.wifi;
18
19import static android.net.wifi.WifiManager.WIFI_STATE_DISABLED;
20import static android.net.wifi.WifiManager.WIFI_STATE_DISABLING;
21import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED;
22import static android.net.wifi.WifiManager.WIFI_STATE_ENABLING;
23import static android.net.wifi.WifiManager.WIFI_STATE_UNKNOWN;
24
25/**
26 * TODO: Add soft AP states as part of WIFI_STATE_XXX
27 * Retain WIFI_STATE_ENABLING that indicates driver is loading
28 * Add WIFI_STATE_AP_ENABLED to indicate soft AP has started
29 * and WIFI_STATE_FAILED for failure
30 * Deprecate WIFI_STATE_UNKNOWN
31 *
32 * Doing this will simplify the logic for sending broadcasts
33 */
34import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED;
35import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLING;
36import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
37import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLING;
38import static android.net.wifi.WifiManager.WIFI_AP_STATE_FAILED;
39
40import android.app.ActivityManagerNative;
41import android.net.NetworkInfo;
42import android.net.DhcpInfo;
43import android.net.NetworkUtils;
44import android.net.ConnectivityManager;
45import android.net.NetworkInfo.DetailedState;
46import android.net.NetworkProperties;
47import android.net.wifi.WifiConfiguration.Status;
48import android.os.Binder;
49import android.os.Message;
50import android.os.Parcelable;
51import android.os.Handler;
52import android.os.IBinder;
53import android.os.INetworkManagementService;
54import android.os.PowerManager;
55import android.os.SystemProperties;
56import android.os.RemoteException;
57import android.os.ServiceManager;
58import android.os.Process;
59import android.provider.Settings;
60import android.text.TextUtils;
61import android.util.EventLog;
62import android.util.Log;
63import android.util.Slog;
64import android.app.backup.IBackupManager;
65import android.bluetooth.BluetoothDevice;
66import android.bluetooth.BluetoothHeadset;
67import android.bluetooth.BluetoothA2dp;
68import android.content.ContentResolver;
69import android.content.Intent;
70import android.content.Context;
71import android.database.ContentObserver;
72import com.android.internal.app.IBatteryStats;
73import com.android.internal.util.HierarchicalState;
74import com.android.internal.util.HierarchicalStateMachine;
75
76import java.net.InetAddress;
77import java.net.NetworkInterface;
78import java.net.SocketException;
79import java.net.UnknownHostException;
80import java.util.ArrayList;
81import java.util.BitSet;
82import java.util.LinkedHashMap;
83import java.util.List;
84import java.util.Map;
85import java.util.Set;
86import java.util.concurrent.atomic.AtomicBoolean;
87import java.util.concurrent.atomic.AtomicInteger;
88import 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 * @hide
95 */
96//TODO: we still need frequent scanning for the case when
97// we issue disconnect but need scan results for open network notification
98public class WifiStateMachine extends HierarchicalStateMachine {
99
100    private static final String TAG = "WifiStateMachine";
101    private static final String NETWORKTYPE = "WIFI";
102    private static final boolean DBG = false;
103
104    /* TODO: fetch a configurable interface */
105    private static final String SOFTAP_IFACE = "wl0.1";
106
107    private WifiMonitor mWifiMonitor;
108    private INetworkManagementService nwService;
109    private ConnectivityManager mCm;
110
111    /* Scan results handling */
112    private List<ScanResult> mScanResults;
113    private static final Pattern scanResultPattern = Pattern.compile("\t+");
114    private static final int SCAN_RESULT_CACHE_SIZE = 80;
115    private final LinkedHashMap<String, ScanResult> mScanResultCache;
116
117    private String mInterfaceName;
118
119    private int mNumAllowedChannels = 0;
120    private int mLastSignalLevel = -1;
121    private String mLastBssid;
122    private int mLastNetworkId;
123    private boolean mEnableRssiPolling = false;
124    private boolean mPasswordKeyMayBeIncorrect = false;
125    private boolean mUseStaticIp = false;
126    private int mReconnectCount = 0;
127    private boolean mIsScanMode = false;
128    private boolean mConfigChanged = false;
129
130    /**
131     * Instance of the bluetooth headset helper. This needs to be created
132     * early because there is a delay before it actually 'connects', as
133     * noted by its javadoc. If we check before it is connected, it will be
134     * in an error state and we will not disable coexistence.
135     */
136    private BluetoothHeadset mBluetoothHeadset;
137
138    private BluetoothA2dp mBluetoothA2dp;
139
140    /**
141     * Observes the static IP address settings.
142     */
143    private SettingsObserver mSettingsObserver;
144    private NetworkProperties mNetworkProperties;
145
146    // Held during driver load and unload
147    private static PowerManager.WakeLock sWakeLock;
148
149    private Context mContext;
150
151    private DhcpInfo mDhcpInfo;
152    private WifiInfo mWifiInfo;
153    private NetworkInfo mNetworkInfo;
154    private SupplicantStateTracker mSupplicantStateTracker;
155    /* Tracks the highest priority of configured networks */
156    private int mLastPriority = -1;
157    /* Tracks if all networks need to be enabled */
158    private boolean mEnableAllNetworks = false;
159
160    // Event log tags (must be in sync with event-log-tags)
161    private static final int EVENTLOG_WIFI_STATE_CHANGED        = 50021;
162    private static final int EVENTLOG_WIFI_EVENT_HANDLED        = 50022;
163    private static final int EVENTLOG_SUPPLICANT_STATE_CHANGED  = 50023;
164
165    /* Load the driver */
166    private static final int CMD_LOAD_DRIVER                      = 1;
167    /* Unload the driver */
168    private static final int CMD_UNLOAD_DRIVER                    = 2;
169    /* Indicates driver load succeeded */
170    private static final int CMD_LOAD_DRIVER_SUCCESS              = 3;
171    /* Indicates driver load failed */
172    private static final int CMD_LOAD_DRIVER_FAILURE              = 4;
173    /* Indicates driver unload succeeded */
174    private static final int CMD_UNLOAD_DRIVER_SUCCESS            = 5;
175    /* Indicates driver unload failed */
176    private static final int CMD_UNLOAD_DRIVER_FAILURE            = 6;
177
178    /* Start the supplicant */
179    private static final int CMD_START_SUPPLICANT                 = 11;
180    /* Stop the supplicant */
181    private static final int CMD_STOP_SUPPLICANT                  = 12;
182    /* Start the driver */
183    private static final int CMD_START_DRIVER                     = 13;
184    /* Start the driver */
185    private static final int CMD_STOP_DRIVER                      = 14;
186    /* Indicates DHCP succeded */
187    private static final int CMD_IP_CONFIG_SUCCESS                = 15;
188    /* Indicates DHCP failed */
189    private static final int CMD_IP_CONFIG_FAILURE                = 16;
190    /* Re-configure interface */
191    private static final int CMD_RECONFIGURE_IP                   = 17;
192
193
194    /* Start the soft access point */
195    private static final int CMD_START_AP                         = 21;
196    /* Stop the soft access point */
197    private static final int CMD_STOP_AP                          = 22;
198
199
200    /* Supplicant events */
201    /* Connection to supplicant established */
202    private static final int SUP_CONNECTION_EVENT                 = 31;
203    /* Connection to supplicant lost */
204    private static final int SUP_DISCONNECTION_EVENT              = 32;
205    /* Driver start completed */
206    private static final int DRIVER_START_EVENT                   = 33;
207    /* Driver stop completed */
208    private static final int DRIVER_STOP_EVENT                    = 34;
209    /* Network connection completed */
210    private static final int NETWORK_CONNECTION_EVENT             = 36;
211    /* Network disconnection completed */
212    private static final int NETWORK_DISCONNECTION_EVENT          = 37;
213    /* Scan results are available */
214    private static final int SCAN_RESULTS_EVENT                   = 38;
215    /* Supplicate state changed */
216    private static final int SUPPLICANT_STATE_CHANGE_EVENT        = 39;
217    /* Password may be incorrect */
218    private static final int PASSWORD_MAY_BE_INCORRECT_EVENT      = 40;
219
220    /* Supplicant commands */
221    /* Is supplicant alive ? */
222    private static final int CMD_PING_SUPPLICANT                  = 51;
223    /* Add/update a network configuration */
224    private static final int CMD_ADD_OR_UPDATE_NETWORK            = 52;
225    /* Delete a network */
226    private static final int CMD_REMOVE_NETWORK                   = 53;
227    /* Enable a network. The device will attempt a connection to the given network. */
228    private static final int CMD_ENABLE_NETWORK                   = 54;
229    /* Disable a network. The device does not attempt a connection to the given network. */
230    private static final int CMD_DISABLE_NETWORK                  = 55;
231    /* Blacklist network. De-prioritizes the given BSSID for connection. */
232    private static final int CMD_BLACKLIST_NETWORK                = 56;
233    /* Clear the blacklist network list */
234    private static final int CMD_CLEAR_BLACKLIST                  = 57;
235    /* Get the configured networks */
236    private static final int CMD_GET_NETWORK_CONFIG               = 58;
237    /* Save configuration */
238    private static final int CMD_SAVE_CONFIG                      = 59;
239    /* Connection status */
240    private static final int CMD_CONNECTION_STATUS                = 60;
241
242    /* Supplicant commands after driver start*/
243    /* Initiate a scan */
244    private static final int CMD_START_SCAN                       = 71;
245    /* Set scan mode. CONNECT_MODE or SCAN_ONLY_MODE */
246    private static final int CMD_SET_SCAN_MODE                    = 72;
247    /* Set scan type. SCAN_ACTIVE or SCAN_PASSIVE */
248    private static final int CMD_SET_SCAN_TYPE                    = 73;
249    /* Disconnect from a network */
250    private static final int CMD_DISCONNECT                       = 74;
251    /* Reconnect to a network */
252    private static final int CMD_RECONNECT                        = 75;
253    /* Reassociate to a network */
254    private static final int CMD_REASSOCIATE                      = 76;
255    /* Set power mode
256     * POWER_MODE_ACTIVE
257     * POWER_MODE_AUTO
258     */
259    private static final int CMD_SET_POWER_MODE                   = 77;
260    /* Set bluetooth co-existence
261     * BLUETOOTH_COEXISTENCE_MODE_ENABLED
262     * BLUETOOTH_COEXISTENCE_MODE_DISABLED
263     * BLUETOOTH_COEXISTENCE_MODE_SENSE
264     */
265    private static final int CMD_SET_BLUETOOTH_COEXISTENCE        = 78;
266    /* Enable/disable bluetooth scan mode
267     * true(1)
268     * false(0)
269     */
270    private static final int CMD_SET_BLUETOOTH_SCAN_MODE          = 79;
271    /* Set number of allowed channels */
272    private static final int CMD_SET_NUM_ALLOWED_CHANNELS         = 80;
273    /* Request connectivity manager wake lock before driver stop */
274    private static final int CMD_REQUEST_CM_WAKELOCK              = 81;
275    /* Enables RSSI poll */
276    private static final int CMD_ENABLE_RSSI_POLL                 = 82;
277    /* RSSI poll */
278    private static final int CMD_RSSI_POLL                        = 83;
279    /* Get current RSSI */
280    private static final int CMD_GET_RSSI                         = 84;
281    /* Get approx current RSSI */
282    private static final int CMD_GET_RSSI_APPROX                  = 85;
283    /* Get link speed on connection */
284    private static final int CMD_GET_LINK_SPEED                   = 86;
285    /* Radio mac address */
286    private static final int CMD_GET_MAC_ADDR                     = 87;
287    /* Set up packet filtering */
288    private static final int CMD_START_PACKET_FILTERING           = 88;
289    /* Clear packet filter */
290    private static final int CMD_STOP_PACKET_FILTERING            = 89;
291    /* Connect to a specified network (network id
292     * or WifiConfiguration) This involves increasing
293     * the priority of the network, enabling the network
294     * (while disabling others) and issuing a reconnect.
295     * Note that CMD_RECONNECT just does a reconnect to
296     * an existing network. All the networks get enabled
297     * upon a successful connection or a failure.
298     */
299    private static final int CMD_CONNECT_NETWORK                  = 90;
300    /* Save the specified network. This involves adding
301     * an enabled network (if new) and updating the
302     * config and issuing a save on supplicant config.
303     */
304    private static final int CMD_SAVE_NETWORK                     = 91;
305    /* Delete the specified network. This involves
306     * removing the network and issuing a save on
307     * supplicant config.
308     */
309    private static final int CMD_FORGET_NETWORK                   = 92;
310
311
312
313    /**
314     * Interval in milliseconds between polling for connection
315     * status items that are not sent via asynchronous events.
316     * An example is RSSI (signal strength).
317     */
318    private static final int POLL_RSSI_INTERVAL_MSECS = 3000;
319
320    private static final int CONNECT_MODE   = 1;
321    private static final int SCAN_ONLY_MODE = 2;
322
323    private static final int SCAN_ACTIVE = 1;
324    private static final int SCAN_PASSIVE = 2;
325
326    /**
327     * The maximum number of times we will retry a connection to an access point
328     * for which we have failed in acquiring an IP address from DHCP. A value of
329     * N means that we will make N+1 connection attempts in all.
330     * <p>
331     * See {@link Settings.Secure#WIFI_MAX_DHCP_RETRY_COUNT}. This is the default
332     * value if a Settings value is not present.
333     */
334    private static final int DEFAULT_MAX_DHCP_RETRIES = 9;
335
336    private static final int DRIVER_POWER_MODE_ACTIVE = 1;
337    private static final int DRIVER_POWER_MODE_AUTO = 0;
338
339    /* Default parent state */
340    private HierarchicalState mDefaultState = new DefaultState();
341    /* Temporary initial state */
342    private HierarchicalState mInitialState = new InitialState();
343    /* Unloading the driver */
344    private HierarchicalState mDriverUnloadingState = new DriverUnloadingState();
345    /* Loading the driver */
346    private HierarchicalState mDriverUnloadedState = new DriverUnloadedState();
347    /* Driver load/unload failed */
348    private HierarchicalState mDriverFailedState = new DriverFailedState();
349    /* Driver loading */
350    private HierarchicalState mDriverLoadingState = new DriverLoadingState();
351    /* Driver loaded */
352    private HierarchicalState mDriverLoadedState = new DriverLoadedState();
353    /* Driver loaded, waiting for supplicant to start */
354    private HierarchicalState mWaitForSupState = new WaitForSupState();
355
356    /* Driver loaded and supplicant ready */
357    private HierarchicalState mDriverSupReadyState = new DriverSupReadyState();
358    /* Driver start issued, waiting for completed event */
359    private HierarchicalState mDriverStartingState = new DriverStartingState();
360    /* Driver started */
361    private HierarchicalState mDriverStartedState = new DriverStartedState();
362    /* Driver stopping */
363    private HierarchicalState mDriverStoppingState = new DriverStoppingState();
364    /* Driver stopped */
365    private HierarchicalState mDriverStoppedState = new DriverStoppedState();
366    /* Scan for networks, no connection will be established */
367    private HierarchicalState mScanModeState = new ScanModeState();
368    /* Connecting to an access point */
369    private HierarchicalState mConnectModeState = new ConnectModeState();
370    /* Fetching IP after network connection (assoc+auth complete) */
371    private HierarchicalState mConnectingState = new ConnectingState();
372    /* Connected with IP addr */
373    private HierarchicalState mConnectedState = new ConnectedState();
374    /* disconnect issued, waiting for network disconnect confirmation */
375    private HierarchicalState mDisconnectingState = new DisconnectingState();
376    /* Network is not connected, supplicant assoc+auth is not complete */
377    private HierarchicalState mDisconnectedState = new DisconnectedState();
378
379    /* Soft Ap is running */
380    private HierarchicalState mSoftApStartedState = new SoftApStartedState();
381
382    /* Argument for Message object to indicate a synchronous call */
383    private static final int SYNCHRONOUS_CALL = 1;
384    private static final int ASYNCHRONOUS_CALL = 0;
385
386
387    /**
388     * One of  {@link WifiManager#WIFI_STATE_DISABLED},
389     *         {@link WifiManager#WIFI_STATE_DISABLING},
390     *         {@link WifiManager#WIFI_STATE_ENABLED},
391     *         {@link WifiManager#WIFI_STATE_ENABLING},
392     *         {@link WifiManager#WIFI_STATE_UNKNOWN}
393     *
394     */
395    private final AtomicInteger mWifiState = new AtomicInteger(WIFI_STATE_DISABLED);
396
397    /**
398     * One of  {@link WifiManager#WIFI_AP_STATE_DISABLED},
399     *         {@link WifiManager#WIFI_AP_STATE_DISABLING},
400     *         {@link WifiManager#WIFI_AP_STATE_ENABLED},
401     *         {@link WifiManager#WIFI_AP_STATE_ENABLING},
402     *         {@link WifiManager#WIFI_AP_STATE_FAILED}
403     *
404     */
405    private final AtomicInteger mWifiApState = new AtomicInteger(WIFI_AP_STATE_DISABLED);
406
407    private final AtomicInteger mLastEnableUid = new AtomicInteger(Process.myUid());
408    private final AtomicInteger mLastApEnableUid = new AtomicInteger(Process.myUid());
409
410    private final IBatteryStats mBatteryStats;
411
412    public WifiStateMachine(Context context) {
413        super(TAG);
414
415        mContext = context;
416
417        mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, NETWORKTYPE, "");
418        mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo"));
419
420        IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
421        nwService = INetworkManagementService.Stub.asInterface(b);
422
423        mWifiMonitor = new WifiMonitor(this);
424        mDhcpInfo = new DhcpInfo();
425        mWifiInfo = new WifiInfo();
426        mInterfaceName = SystemProperties.get("wifi.interface", "tiwlan0");
427        mSupplicantStateTracker = new SupplicantStateTracker(context, getHandler());
428
429        mBluetoothHeadset = new BluetoothHeadset(mContext, null);
430        mNetworkProperties = new NetworkProperties();
431
432        mNetworkInfo.setIsAvailable(false);
433        mNetworkProperties.clear();
434        mLastBssid = null;
435        mLastNetworkId = -1;
436        mLastSignalLevel = -1;
437
438        mScanResultCache = new LinkedHashMap<String, ScanResult>(
439            SCAN_RESULT_CACHE_SIZE, 0.75f, true) {
440                /*
441                 * Limit the cache size by SCAN_RESULT_CACHE_SIZE
442                 * elements
443                 */
444                @Override
445                public boolean removeEldestEntry(Map.Entry eldest) {
446                    return SCAN_RESULT_CACHE_SIZE < this.size();
447                }
448        };
449
450        mSettingsObserver = new SettingsObserver(new Handler());
451
452        PowerManager powerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
453        sWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
454
455        addState(mDefaultState);
456            addState(mInitialState, mDefaultState);
457            addState(mDriverUnloadingState, mDefaultState);
458            addState(mDriverUnloadedState, mDefaultState);
459                addState(mDriverFailedState, mDriverUnloadedState);
460            addState(mDriverLoadingState, mDefaultState);
461            addState(mDriverLoadedState, mDefaultState);
462                addState(mWaitForSupState, mDriverLoadedState);
463            addState(mDriverSupReadyState, mDefaultState);
464                addState(mDriverStartingState, mDriverSupReadyState);
465                addState(mDriverStartedState, mDriverSupReadyState);
466                    addState(mScanModeState, mDriverStartedState);
467                    addState(mConnectModeState, mDriverStartedState);
468                        addState(mConnectingState, mConnectModeState);
469                        addState(mConnectedState, mConnectModeState);
470                        addState(mDisconnectingState, mConnectModeState);
471                        addState(mDisconnectedState, mConnectModeState);
472                addState(mDriverStoppingState, mDriverSupReadyState);
473                addState(mDriverStoppedState, mDriverSupReadyState);
474            addState(mSoftApStartedState, mDefaultState);
475
476        setInitialState(mInitialState);
477
478        if (DBG) setDbg(true);
479
480        //start the state machine
481        start();
482    }
483
484    /*********************************************************
485     * Methods exposed for public use
486     ********************************************************/
487
488    /**
489     * TODO: doc
490     */
491    public boolean pingSupplicant() {
492        return sendSyncMessage(CMD_PING_SUPPLICANT).boolValue;
493    }
494
495    /**
496     * TODO: doc
497     */
498    public void startScan(boolean forceActive) {
499        sendMessage(obtainMessage(CMD_START_SCAN, forceActive ?
500                SCAN_ACTIVE : SCAN_PASSIVE, 0));
501    }
502
503    /**
504     * TODO: doc
505     */
506    public void setWifiEnabled(boolean enable) {
507        mLastEnableUid.set(Binder.getCallingUid());
508        if (enable) {
509            /* Argument is the state that is entered prior to load */
510            sendMessage(obtainMessage(CMD_LOAD_DRIVER, WIFI_STATE_ENABLING, 0));
511            sendMessage(CMD_START_SUPPLICANT);
512        } else {
513            sendMessage(CMD_STOP_SUPPLICANT);
514            /* Argument is the state that is entered upon success */
515            sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_STATE_DISABLED, 0));
516        }
517    }
518
519    /**
520     * TODO: doc
521     */
522    public void setWifiApEnabled(WifiConfiguration wifiConfig, boolean enable) {
523        mLastApEnableUid.set(Binder.getCallingUid());
524        if (enable) {
525            /* Argument is the state that is entered prior to load */
526            sendMessage(obtainMessage(CMD_LOAD_DRIVER, WIFI_AP_STATE_ENABLING, 0));
527            sendMessage(obtainMessage(CMD_START_AP, wifiConfig));
528        } else {
529            sendMessage(CMD_STOP_AP);
530            /* Argument is the state that is entered upon success */
531            sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_AP_STATE_DISABLED, 0));
532        }
533    }
534
535    /**
536     * TODO: doc
537     */
538    public int getWifiState() {
539        return mWifiState.get();
540    }
541
542    /**
543     * TODO: doc
544     */
545    public String getWifiStateByName() {
546        switch (mWifiState.get()) {
547            case WIFI_STATE_DISABLING:
548                return "disabling";
549            case WIFI_STATE_DISABLED:
550                return "disabled";
551            case WIFI_STATE_ENABLING:
552                return "enabling";
553            case WIFI_STATE_ENABLED:
554                return "enabled";
555            case WIFI_STATE_UNKNOWN:
556                return "unknown state";
557            default:
558                return "[invalid state]";
559        }
560    }
561
562    /**
563     * TODO: doc
564     */
565    public int getWifiApState() {
566        return mWifiApState.get();
567    }
568
569    /**
570     * TODO: doc
571     */
572    public String getWifiApStateByName() {
573        switch (mWifiApState.get()) {
574            case WIFI_AP_STATE_DISABLING:
575                return "disabling";
576            case WIFI_AP_STATE_DISABLED:
577                return "disabled";
578            case WIFI_AP_STATE_ENABLING:
579                return "enabling";
580            case WIFI_AP_STATE_ENABLED:
581                return "enabled";
582            case WIFI_AP_STATE_FAILED:
583                return "failed";
584            default:
585                return "[invalid state]";
586        }
587    }
588
589    /**
590     * Get status information for the current connection, if any.
591     * @return a {@link WifiInfo} object containing information about the current connection
592     *
593     */
594    public WifiInfo requestConnectionInfo() {
595        return mWifiInfo;
596    }
597
598    public DhcpInfo getDhcpInfo() {
599        return mDhcpInfo;
600    }
601
602    /**
603     * TODO: doc
604     */
605    public void setDriverStart(boolean enable) {
606      if (enable) {
607          sendMessage(CMD_START_DRIVER);
608      } else {
609          sendMessage(CMD_STOP_DRIVER);
610      }
611    }
612
613    /**
614     * TODO: doc
615     */
616    public void setScanOnlyMode(boolean enable) {
617      if (enable) {
618          sendMessage(obtainMessage(CMD_SET_SCAN_MODE, SCAN_ONLY_MODE, 0));
619      } else {
620          sendMessage(obtainMessage(CMD_SET_SCAN_MODE, CONNECT_MODE, 0));
621      }
622    }
623
624    /**
625     * TODO: doc
626     */
627    public void setScanType(boolean active) {
628      if (active) {
629          sendMessage(obtainMessage(CMD_SET_SCAN_TYPE, SCAN_ACTIVE, 0));
630      } else {
631          sendMessage(obtainMessage(CMD_SET_SCAN_TYPE, SCAN_PASSIVE, 0));
632      }
633    }
634
635    /**
636     * TODO: doc
637     */
638    public List<ScanResult> getScanResultsList() {
639        return mScanResults;
640    }
641
642    /**
643     * Disconnect from Access Point
644     */
645    public void disconnectCommand() {
646        sendMessage(CMD_DISCONNECT);
647    }
648
649    /**
650     * Initiate a reconnection to AP
651     */
652    public void reconnectCommand() {
653        sendMessage(CMD_RECONNECT);
654    }
655
656    /**
657     * Initiate a re-association to AP
658     */
659    public void reassociateCommand() {
660        sendMessage(CMD_REASSOCIATE);
661    }
662
663    /**
664     * Add a network synchronously
665     *
666     * @return network id of the new network
667     */
668    public int addOrUpdateNetwork(WifiConfiguration config) {
669        return sendSyncMessage(CMD_ADD_OR_UPDATE_NETWORK, config).intValue;
670    }
671
672    public List<WifiConfiguration> getConfiguredNetworks() {
673        return sendSyncMessage(CMD_GET_NETWORK_CONFIG).configList;
674    }
675
676    /**
677     * Delete a network
678     *
679     * @param networkId id of the network to be removed
680     */
681    public boolean removeNetwork(int networkId) {
682        return sendSyncMessage(obtainMessage(CMD_REMOVE_NETWORK, networkId, 0)).boolValue;
683    }
684
685    private class EnableNetParams {
686        private int netId;
687        private boolean disableOthers;
688        EnableNetParams(int n, boolean b) {
689            netId = n;
690            disableOthers = b;
691        }
692    }
693    /**
694     * Enable a network
695     *
696     * @param netId network id of the network
697     * @param disableOthers true, if all other networks have to be disabled
698     * @return {@code true} if the operation succeeds, {@code false} otherwise
699     */
700    public boolean enableNetwork(int netId, boolean disableOthers) {
701        return sendSyncMessage(CMD_ENABLE_NETWORK,
702                new EnableNetParams(netId, disableOthers)).boolValue;
703    }
704
705    /**
706     * Disable a network
707     *
708     * @param netId network id of the network
709     * @return {@code true} if the operation succeeds, {@code false} otherwise
710     */
711    public boolean disableNetwork(int netId) {
712        return sendSyncMessage(obtainMessage(CMD_DISABLE_NETWORK, netId, 0)).boolValue;
713    }
714
715    /**
716     * Blacklist a BSSID. This will avoid the AP if there are
717     * alternate APs to connect
718     *
719     * @param bssid BSSID of the network
720     */
721    public void addToBlacklist(String bssid) {
722        sendMessage(obtainMessage(CMD_BLACKLIST_NETWORK, bssid));
723    }
724
725    /**
726     * Clear the blacklist list
727     *
728     */
729    public void clearBlacklist() {
730        sendMessage(obtainMessage(CMD_CLEAR_BLACKLIST));
731    }
732
733    public void connectNetwork(int netId) {
734        sendMessage(obtainMessage(CMD_CONNECT_NETWORK, netId, 0));
735    }
736
737    public void connectNetwork(WifiConfiguration wifiConfig) {
738        sendMessage(obtainMessage(CMD_CONNECT_NETWORK, wifiConfig));
739    }
740
741    public void saveNetwork(WifiConfiguration wifiConfig) {
742        sendMessage(obtainMessage(CMD_SAVE_NETWORK, wifiConfig));
743    }
744
745    public void forgetNetwork(int netId) {
746        sendMessage(obtainMessage(CMD_FORGET_NETWORK, netId, 0));
747    }
748
749    /**
750     * Get detailed status of the connection
751     *
752     * @return Example status result
753     *  bssid=aa:bb:cc:dd:ee:ff
754     *  ssid=TestNet
755     *  id=3
756     *  pairwise_cipher=NONE
757     *  group_cipher=NONE
758     *  key_mgmt=NONE
759     *  wpa_state=COMPLETED
760     *  ip_address=X.X.X.X
761     */
762    public String status() {
763        return sendSyncMessage(CMD_CONNECTION_STATUS).stringValue;
764    }
765
766    public void enableRssiPolling(boolean enabled) {
767       sendMessage(obtainMessage(CMD_ENABLE_RSSI_POLL, enabled ? 1 : 0, 0));
768    }
769    /**
770     * Get RSSI to currently connected network
771     *
772     * @return RSSI value, -1 on failure
773     */
774    public int getRssi() {
775        return sendSyncMessage(CMD_GET_RSSI).intValue;
776    }
777
778    /**
779     * Get approx RSSI to currently connected network
780     *
781     * @return RSSI value, -1 on failure
782     */
783    public int getRssiApprox() {
784        return sendSyncMessage(CMD_GET_RSSI_APPROX).intValue;
785    }
786
787    /**
788     * Get link speed to currently connected network
789     *
790     * @return link speed, -1 on failure
791     */
792    public int getLinkSpeed() {
793        return sendSyncMessage(CMD_GET_LINK_SPEED).intValue;
794    }
795
796    /**
797     * Get MAC address of radio
798     *
799     * @return MAC address, null on failure
800     */
801    public String getMacAddress() {
802        return sendSyncMessage(CMD_GET_MAC_ADDR).stringValue;
803    }
804
805    /**
806     * Start packet filtering
807     */
808    public void startPacketFiltering() {
809        sendMessage(CMD_START_PACKET_FILTERING);
810    }
811
812    /**
813     * Stop packet filtering
814     */
815    public void stopPacketFiltering() {
816        sendMessage(CMD_STOP_PACKET_FILTERING);
817    }
818
819    /**
820     * Set power mode
821     * @param mode
822     *     DRIVER_POWER_MODE_AUTO
823     *     DRIVER_POWER_MODE_ACTIVE
824     */
825    public void setPowerMode(int mode) {
826        sendMessage(obtainMessage(CMD_SET_POWER_MODE, mode, 0));
827    }
828
829    /**
830     * Set the number of allowed radio frequency channels from the system
831     * setting value, if any.
832     */
833    public void setNumAllowedChannels() {
834        try {
835            setNumAllowedChannels(
836                    Settings.Secure.getInt(mContext.getContentResolver(),
837                    Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS));
838        } catch (Settings.SettingNotFoundException e) {
839            if (mNumAllowedChannels != 0) {
840                setNumAllowedChannels(mNumAllowedChannels);
841            }
842            // otherwise, use the driver default
843        }
844    }
845
846    /**
847     * Set the number of radio frequency channels that are allowed to be used
848     * in the current regulatory domain.
849     * @param numChannels the number of allowed channels. Must be greater than 0
850     * and less than or equal to 16.
851     */
852    public void setNumAllowedChannels(int numChannels) {
853        sendMessage(obtainMessage(CMD_SET_NUM_ALLOWED_CHANNELS, numChannels, 0));
854    }
855
856    /**
857     * Get number of allowed channels
858     *
859     * @return channel count, -1 on failure
860     *
861     * TODO: this is not a public API and needs to be removed in favor
862     * of asynchronous reporting. unused for now.
863     */
864    public int getNumAllowedChannels() {
865        return -1;
866    }
867
868    /**
869     * Set bluetooth coex mode:
870     *
871     * @param mode
872     *  BLUETOOTH_COEXISTENCE_MODE_ENABLED
873     *  BLUETOOTH_COEXISTENCE_MODE_DISABLED
874     *  BLUETOOTH_COEXISTENCE_MODE_SENSE
875     */
876    public void setBluetoothCoexistenceMode(int mode) {
877        sendMessage(obtainMessage(CMD_SET_BLUETOOTH_COEXISTENCE, mode, 0));
878    }
879
880    /**
881     * Enable or disable Bluetooth coexistence scan mode. When this mode is on,
882     * some of the low-level scan parameters used by the driver are changed to
883     * reduce interference with A2DP streaming.
884     *
885     * @param isBluetoothPlaying whether to enable or disable this mode
886     */
887    public void setBluetoothScanMode(boolean isBluetoothPlaying) {
888        sendMessage(obtainMessage(CMD_SET_BLUETOOTH_SCAN_MODE, isBluetoothPlaying ? 1 : 0, 0));
889    }
890
891    /**
892     * Save configuration on supplicant
893     *
894     * @return {@code true} if the operation succeeds, {@code false} otherwise
895     *
896     * TODO: deprecate this
897     */
898    public boolean saveConfig() {
899        return sendSyncMessage(CMD_SAVE_CONFIG).boolValue;
900    }
901
902    /**
903     * TODO: doc
904     */
905    public void requestCmWakeLock() {
906        sendMessage(CMD_REQUEST_CM_WAKELOCK);
907    }
908
909    /*********************************************************
910     * Internal private functions
911     ********************************************************/
912
913    class SyncReturn {
914        boolean boolValue;
915        int intValue;
916        String stringValue;
917        Object objValue;
918        List<WifiConfiguration> configList;
919    }
920
921    class SyncParams {
922        Object mParameter;
923        SyncReturn mSyncReturn;
924        SyncParams() {
925            mSyncReturn = new SyncReturn();
926        }
927        SyncParams(Object p) {
928            mParameter = p;
929            mSyncReturn = new SyncReturn();
930        }
931    }
932
933    /**
934     * message.arg2 is reserved to indicate synchronized
935     * message.obj is used to store SyncParams
936     */
937    private SyncReturn syncedSend(Message msg) {
938        SyncParams syncParams = (SyncParams) msg.obj;
939        msg.arg2 = SYNCHRONOUS_CALL;
940        synchronized(syncParams) {
941            if (DBG) Log.d(TAG, "syncedSend " + msg);
942            sendMessage(msg);
943            try {
944                syncParams.wait();
945            } catch (InterruptedException e) {
946                Log.e(TAG, "sendSyncMessage: unexpected interruption of wait()");
947                return null;
948            }
949        }
950        return syncParams.mSyncReturn;
951    }
952
953    private SyncReturn sendSyncMessage(Message msg) {
954        SyncParams syncParams = new SyncParams();
955        msg.obj = syncParams;
956        return syncedSend(msg);
957    }
958
959    private SyncReturn sendSyncMessage(int what, Object param) {
960        SyncParams syncParams = new SyncParams(param);
961        Message msg = obtainMessage(what, syncParams);
962        return syncedSend(msg);
963    }
964
965
966    private SyncReturn sendSyncMessage(int what) {
967        return sendSyncMessage(obtainMessage(what));
968    }
969
970    private void notifyOnMsgObject(Message msg) {
971        SyncParams syncParams = (SyncParams) msg.obj;
972        if (syncParams != null) {
973            synchronized(syncParams) {
974                if (DBG) Log.d(TAG, "notifyOnMsgObject " + msg);
975                syncParams.notify();
976            }
977        }
978        else {
979            Log.e(TAG, "Error! syncParams in notifyOnMsgObject is null");
980        }
981    }
982
983    private void setWifiState(int wifiState) {
984        final int previousWifiState = mWifiState.get();
985
986        try {
987            if (wifiState == WIFI_STATE_ENABLED) {
988                mBatteryStats.noteWifiOn(mLastEnableUid.get());
989            } else if (wifiState == WIFI_STATE_DISABLED) {
990                mBatteryStats.noteWifiOff(mLastEnableUid.get());
991            }
992        } catch (RemoteException e) {
993            Log.e(TAG, "Failed to note battery stats in wifi");
994        }
995
996        mWifiState.set(wifiState);
997
998        if (DBG) Log.d(TAG, "setWifiState: " + getWifiStateByName());
999
1000        final Intent intent = new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION);
1001        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1002        intent.putExtra(WifiManager.EXTRA_WIFI_STATE, wifiState);
1003        intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_STATE, previousWifiState);
1004        mContext.sendStickyBroadcast(intent);
1005    }
1006
1007    private void setWifiApState(int wifiApState) {
1008        final int previousWifiApState = mWifiApState.get();
1009
1010        try {
1011            if (wifiApState == WIFI_AP_STATE_ENABLED) {
1012                mBatteryStats.noteWifiOn(mLastApEnableUid.get());
1013            } else if (wifiApState == WIFI_AP_STATE_DISABLED) {
1014                mBatteryStats.noteWifiOff(mLastApEnableUid.get());
1015            }
1016        } catch (RemoteException e) {
1017            Log.d(TAG, "Failed to note battery stats in wifi");
1018        }
1019
1020        // Update state
1021        mWifiApState.set(wifiApState);
1022
1023        if (DBG) Log.d(TAG, "setWifiApState: " + getWifiApStateByName());
1024
1025        final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
1026        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1027        intent.putExtra(WifiManager.EXTRA_WIFI_AP_STATE, wifiApState);
1028        intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_AP_STATE, previousWifiApState);
1029        mContext.sendStickyBroadcast(intent);
1030    }
1031
1032    /**
1033     * Parse the scan result line passed to us by wpa_supplicant (helper).
1034     * @param line the line to parse
1035     * @return the {@link ScanResult} object
1036     */
1037    private ScanResult parseScanResult(String line) {
1038        ScanResult scanResult = null;
1039        if (line != null) {
1040            /*
1041             * Cache implementation (LinkedHashMap) is not synchronized, thus,
1042             * must synchronized here!
1043             */
1044            synchronized (mScanResultCache) {
1045                String[] result = scanResultPattern.split(line);
1046                if (3 <= result.length && result.length <= 5) {
1047                    String bssid = result[0];
1048                    // bssid | frequency | level | flags | ssid
1049                    int frequency;
1050                    int level;
1051                    try {
1052                        frequency = Integer.parseInt(result[1]);
1053                        level = Integer.parseInt(result[2]);
1054                        /* some implementations avoid negative values by adding 256
1055                         * so we need to adjust for that here.
1056                         */
1057                        if (level > 0) level -= 256;
1058                    } catch (NumberFormatException e) {
1059                        frequency = 0;
1060                        level = 0;
1061                    }
1062
1063                    /*
1064                     * The formatting of the results returned by
1065                     * wpa_supplicant is intended to make the fields
1066                     * line up nicely when printed,
1067                     * not to make them easy to parse. So we have to
1068                     * apply some heuristics to figure out which field
1069                     * is the SSID and which field is the flags.
1070                     */
1071                    String ssid;
1072                    String flags;
1073                    if (result.length == 4) {
1074                        if (result[3].charAt(0) == '[') {
1075                            flags = result[3];
1076                            ssid = "";
1077                        } else {
1078                            flags = "";
1079                            ssid = result[3];
1080                        }
1081                    } else if (result.length == 5) {
1082                        flags = result[3];
1083                        ssid = result[4];
1084                    } else {
1085                        // Here, we must have 3 fields: no flags and ssid
1086                        // set
1087                        flags = "";
1088                        ssid = "";
1089                    }
1090
1091                    // bssid + ssid is the hash key
1092                    String key = bssid + ssid;
1093                    scanResult = mScanResultCache.get(key);
1094                    if (scanResult != null) {
1095                        scanResult.level = level;
1096                        scanResult.SSID = ssid;
1097                        scanResult.capabilities = flags;
1098                        scanResult.frequency = frequency;
1099                    } else {
1100                        // Do not add scan results that have no SSID set
1101                        if (0 < ssid.trim().length()) {
1102                            scanResult =
1103                                new ScanResult(
1104                                    ssid, bssid, flags, level, frequency);
1105                            mScanResultCache.put(key, scanResult);
1106                        }
1107                    }
1108                } else {
1109                    Log.w(TAG, "Misformatted scan result text with " +
1110                          result.length + " fields: " + line);
1111                }
1112            }
1113        }
1114
1115        return scanResult;
1116    }
1117
1118    /**
1119     * scanResults input format
1120     * 00:bb:cc:dd:cc:ee       2427    166     [WPA-EAP-TKIP][WPA2-EAP-CCMP]   Net1
1121     * 00:bb:cc:dd:cc:ff       2412    165     [WPA-EAP-TKIP][WPA2-EAP-CCMP]   Net2
1122     */
1123    private void setScanResults(String scanResults) {
1124        if (scanResults == null) {
1125            return;
1126        }
1127
1128        List<ScanResult> scanList = new ArrayList<ScanResult>();
1129
1130        int lineCount = 0;
1131
1132        int scanResultsLen = scanResults.length();
1133        // Parse the result string, keeping in mind that the last line does
1134        // not end with a newline.
1135        for (int lineBeg = 0, lineEnd = 0; lineEnd <= scanResultsLen; ++lineEnd) {
1136            if (lineEnd == scanResultsLen || scanResults.charAt(lineEnd) == '\n') {
1137                ++lineCount;
1138
1139                if (lineCount == 1) {
1140                    lineBeg = lineEnd + 1;
1141                    continue;
1142                }
1143                if (lineEnd > lineBeg) {
1144                    String line = scanResults.substring(lineBeg, lineEnd);
1145                    ScanResult scanResult = parseScanResult(line);
1146                    if (scanResult != null) {
1147                        scanList.add(scanResult);
1148                    } else {
1149                        Log.w(TAG, "misformatted scan result for: " + line);
1150                    }
1151                }
1152                lineBeg = lineEnd + 1;
1153            }
1154        }
1155
1156        mScanResults = scanList;
1157    }
1158
1159    private void configureNetworkProperties() {
1160        try {
1161            mNetworkProperties.setInterface(NetworkInterface.getByName(mInterfaceName));
1162        } catch (SocketException e) {
1163            Log.e(TAG, "SocketException creating NetworkInterface from " + mInterfaceName +
1164                    ". e=" + e);
1165            return;
1166        } catch (NullPointerException e) {
1167            Log.e(TAG, "NPE creating NetworkInterface. e=" + e);
1168            return;
1169        }
1170        // TODO - fix this for v6
1171        try {
1172            mNetworkProperties.addAddress(InetAddress.getByAddress(
1173                    NetworkUtils.v4IntToArray(mDhcpInfo.ipAddress)));
1174        } catch (UnknownHostException e) {
1175            Log.e(TAG, "Exception setting IpAddress using " + mDhcpInfo + ", e=" + e);
1176        }
1177
1178        try {
1179            mNetworkProperties.setGateway(InetAddress.getByAddress(NetworkUtils.v4IntToArray(
1180                    mDhcpInfo.gateway)));
1181        } catch (UnknownHostException e) {
1182            Log.e(TAG, "Exception setting Gateway using " + mDhcpInfo + ", e=" + e);
1183        }
1184
1185        try {
1186            mNetworkProperties.addDns(InetAddress.getByAddress(
1187                    NetworkUtils.v4IntToArray(mDhcpInfo.dns1)));
1188        } catch (UnknownHostException e) {
1189            Log.e(TAG, "Exception setting Dns1 using " + mDhcpInfo + ", e=" + e);
1190        }
1191        try {
1192            mNetworkProperties.addDns(InetAddress.getByAddress(
1193                    NetworkUtils.v4IntToArray(mDhcpInfo.dns2)));
1194
1195        } catch (UnknownHostException e) {
1196            Log.e(TAG, "Exception setting Dns2 using " + mDhcpInfo + ", e=" + e);
1197        }
1198        // TODO - add proxy info
1199    }
1200
1201
1202    private void checkUseStaticIp() {
1203        mUseStaticIp = false;
1204        final ContentResolver cr = mContext.getContentResolver();
1205        try {
1206            if (Settings.System.getInt(cr, Settings.System.WIFI_USE_STATIC_IP) == 0) {
1207                return;
1208            }
1209        } catch (Settings.SettingNotFoundException e) {
1210            return;
1211        }
1212
1213        try {
1214            String addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_IP);
1215            if (addr != null) {
1216                mDhcpInfo.ipAddress = stringToIpAddr(addr);
1217            } else {
1218                return;
1219            }
1220            addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_GATEWAY);
1221            if (addr != null) {
1222                mDhcpInfo.gateway = stringToIpAddr(addr);
1223            } else {
1224                return;
1225            }
1226            addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_NETMASK);
1227            if (addr != null) {
1228                mDhcpInfo.netmask = stringToIpAddr(addr);
1229            } else {
1230                return;
1231            }
1232            addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_DNS1);
1233            if (addr != null) {
1234                mDhcpInfo.dns1 = stringToIpAddr(addr);
1235            } else {
1236                return;
1237            }
1238            addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_DNS2);
1239            if (addr != null) {
1240                mDhcpInfo.dns2 = stringToIpAddr(addr);
1241            } else {
1242                mDhcpInfo.dns2 = 0;
1243            }
1244        } catch (UnknownHostException e) {
1245            return;
1246        }
1247        mUseStaticIp = true;
1248    }
1249
1250    private static int stringToIpAddr(String addrString) throws UnknownHostException {
1251        try {
1252            String[] parts = addrString.split("\\.");
1253            if (parts.length != 4) {
1254                throw new UnknownHostException(addrString);
1255            }
1256
1257            int a = Integer.parseInt(parts[0])      ;
1258            int b = Integer.parseInt(parts[1]) <<  8;
1259            int c = Integer.parseInt(parts[2]) << 16;
1260            int d = Integer.parseInt(parts[3]) << 24;
1261
1262            return a | b | c | d;
1263        } catch (NumberFormatException ex) {
1264            throw new UnknownHostException(addrString);
1265        }
1266    }
1267
1268    private int getMaxDhcpRetries() {
1269        return Settings.Secure.getInt(mContext.getContentResolver(),
1270                                      Settings.Secure.WIFI_MAX_DHCP_RETRY_COUNT,
1271                                      DEFAULT_MAX_DHCP_RETRIES);
1272    }
1273
1274    private class SettingsObserver extends ContentObserver {
1275        public SettingsObserver(Handler handler) {
1276            super(handler);
1277            ContentResolver cr = mContext.getContentResolver();
1278            cr.registerContentObserver(Settings.System.getUriFor(
1279                Settings.System.WIFI_USE_STATIC_IP), false, this);
1280            cr.registerContentObserver(Settings.System.getUriFor(
1281                Settings.System.WIFI_STATIC_IP), false, this);
1282            cr.registerContentObserver(Settings.System.getUriFor(
1283                Settings.System.WIFI_STATIC_GATEWAY), false, this);
1284            cr.registerContentObserver(Settings.System.getUriFor(
1285                Settings.System.WIFI_STATIC_NETMASK), false, this);
1286            cr.registerContentObserver(Settings.System.getUriFor(
1287                Settings.System.WIFI_STATIC_DNS1), false, this);
1288            cr.registerContentObserver(Settings.System.getUriFor(
1289                Settings.System.WIFI_STATIC_DNS2), false, this);
1290        }
1291
1292        @Override
1293        public void onChange(boolean selfChange) {
1294            super.onChange(selfChange);
1295
1296            boolean wasStaticIp = mUseStaticIp;
1297            int oIp, oGw, oMsk, oDns1, oDns2;
1298            oIp = oGw = oMsk = oDns1 = oDns2 = 0;
1299            if (wasStaticIp) {
1300                oIp = mDhcpInfo.ipAddress;
1301                oGw = mDhcpInfo.gateway;
1302                oMsk = mDhcpInfo.netmask;
1303                oDns1 = mDhcpInfo.dns1;
1304                oDns2 = mDhcpInfo.dns2;
1305            }
1306            checkUseStaticIp();
1307
1308            if (mWifiInfo.getSupplicantState() == SupplicantState.UNINITIALIZED) {
1309                return;
1310            }
1311
1312            boolean changed =
1313                (wasStaticIp != mUseStaticIp) ||
1314                    (wasStaticIp && (
1315                        oIp   != mDhcpInfo.ipAddress ||
1316                        oGw   != mDhcpInfo.gateway ||
1317                        oMsk  != mDhcpInfo.netmask ||
1318                        oDns1 != mDhcpInfo.dns1 ||
1319                        oDns2 != mDhcpInfo.dns2));
1320
1321            if (changed) {
1322                sendMessage(CMD_RECONFIGURE_IP);
1323                mConfigChanged = true;
1324            }
1325        }
1326    }
1327
1328    /**
1329     * Whether to disable coexistence mode while obtaining IP address. This
1330     * logic will return true only if the current bluetooth
1331     * headset/handsfree state is disconnected. This means if it is in an
1332     * error state, we will NOT disable coexistence mode to err on the side
1333     * of safety.
1334     *
1335     * @return Whether to disable coexistence mode.
1336     */
1337    private boolean shouldDisableCoexistenceMode() {
1338        int state = mBluetoothHeadset.getState(mBluetoothHeadset.getCurrentHeadset());
1339        return state == BluetoothHeadset.STATE_DISCONNECTED;
1340    }
1341
1342    private void checkIsBluetoothPlaying() {
1343        boolean isBluetoothPlaying = false;
1344        Set<BluetoothDevice> connected = mBluetoothA2dp.getConnectedSinks();
1345
1346        for (BluetoothDevice device : connected) {
1347            if (mBluetoothA2dp.getSinkState(device) == BluetoothA2dp.STATE_PLAYING) {
1348                isBluetoothPlaying = true;
1349                break;
1350            }
1351        }
1352        setBluetoothScanMode(isBluetoothPlaying);
1353    }
1354
1355    private void sendScanResultsAvailableBroadcast() {
1356        if (!ActivityManagerNative.isSystemReady()) return;
1357
1358        mContext.sendBroadcast(new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
1359    }
1360
1361    private void sendRssiChangeBroadcast(final int newRssi) {
1362        if (!ActivityManagerNative.isSystemReady()) return;
1363
1364        Intent intent = new Intent(WifiManager.RSSI_CHANGED_ACTION);
1365        intent.putExtra(WifiManager.EXTRA_NEW_RSSI, newRssi);
1366        mContext.sendBroadcast(intent);
1367    }
1368
1369    private void sendNetworkStateChangeBroadcast(String bssid) {
1370        Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
1371        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
1372                | Intent.FLAG_RECEIVER_REPLACE_PENDING);
1373        intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, mNetworkInfo);
1374        intent.putExtra(WifiManager.EXTRA_NETWORK_PROPERTIES, mNetworkProperties);
1375        if (bssid != null)
1376            intent.putExtra(WifiManager.EXTRA_BSSID, bssid);
1377        mContext.sendStickyBroadcast(intent);
1378    }
1379
1380    private void sendConfigChangeBroadcast() {
1381        Intent intent = new Intent(WifiManager.CONFIG_CHANGED_ACTION);
1382        intent.putExtra(WifiManager.EXTRA_NETWORK_PROPERTIES, mNetworkProperties);
1383        mContext.sendBroadcast(intent);
1384    }
1385
1386    private void sendSupplicantStateChangedBroadcast(StateChangeResult sc, boolean failedAuth) {
1387        Intent intent = new Intent(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
1388        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
1389                | Intent.FLAG_RECEIVER_REPLACE_PENDING);
1390        intent.putExtra(WifiManager.EXTRA_NEW_STATE, (Parcelable)sc.state);
1391        if (failedAuth) {
1392            intent.putExtra(
1393                WifiManager.EXTRA_SUPPLICANT_ERROR,
1394                WifiManager.ERROR_AUTHENTICATING);
1395        }
1396        mContext.sendStickyBroadcast(intent);
1397    }
1398
1399    private void sendSupplicantConfigChangedBroadcast() {
1400        Intent intent = new Intent(WifiManager.SUPPLICANT_CONFIG_CHANGED_ACTION);
1401        mContext.sendBroadcast(intent);
1402    }
1403
1404    private void sendSupplicantConnectionChangedBroadcast(boolean connected) {
1405        if (!ActivityManagerNative.isSystemReady()) return;
1406
1407        Intent intent = new Intent(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
1408        intent.putExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, connected);
1409        mContext.sendBroadcast(intent);
1410    }
1411
1412    /**
1413     * Record the detailed state of a network.
1414     * @param state the new @{code DetailedState}
1415     */
1416    private void setDetailedState(NetworkInfo.DetailedState state) {
1417        Log.d(TAG, "setDetailed state, old ="
1418                + mNetworkInfo.getDetailedState() + " and new state=" + state);
1419        if (state != mNetworkInfo.getDetailedState()) {
1420            mNetworkInfo.setDetailedState(state, null, null);
1421        }
1422    }
1423
1424    private static String removeDoubleQuotes(String string) {
1425      if (string.length() <= 2) return "";
1426      return string.substring(1, string.length() - 1);
1427    }
1428
1429    private static String convertToQuotedString(String string) {
1430        return "\"" + string + "\"";
1431    }
1432
1433    private static String makeString(BitSet set, String[] strings) {
1434        StringBuffer buf = new StringBuffer();
1435        int nextSetBit = -1;
1436
1437        /* Make sure all set bits are in [0, strings.length) to avoid
1438         * going out of bounds on strings.  (Shouldn't happen, but...) */
1439        set = set.get(0, strings.length);
1440
1441        while ((nextSetBit = set.nextSetBit(nextSetBit + 1)) != -1) {
1442            buf.append(strings[nextSetBit].replace('_', '-')).append(' ');
1443        }
1444
1445        // remove trailing space
1446        if (set.cardinality() > 0) {
1447            buf.setLength(buf.length() - 1);
1448        }
1449
1450        return buf.toString();
1451    }
1452
1453    private static int lookupString(String string, String[] strings) {
1454        int size = strings.length;
1455
1456        string = string.replace('-', '_');
1457
1458        for (int i = 0; i < size; i++)
1459            if (string.equals(strings[i]))
1460                return i;
1461
1462        // if we ever get here, we should probably add the
1463        // value to WifiConfiguration to reflect that it's
1464        // supported by the WPA supplicant
1465        Log.w(TAG, "Failed to look-up a string: " + string);
1466
1467        return -1;
1468    }
1469
1470    private void enableAllNetworks() {
1471        if (mEnableAllNetworks) {
1472            mEnableAllNetworks = false;
1473            List<WifiConfiguration> configList = getConfiguredNetworksNative();
1474            for (WifiConfiguration config : configList) {
1475                if(config != null && config.status == Status.DISABLED) {
1476                    WifiNative.enableNetworkCommand(config.networkId, false);
1477                }
1478            }
1479            WifiNative.saveConfigCommand();
1480            sendSupplicantConfigChangedBroadcast();
1481        }
1482    }
1483
1484    private int addOrUpdateNetworkNative(WifiConfiguration config) {
1485        /*
1486         * If the supplied networkId is -1, we create a new empty
1487         * network configuration. Otherwise, the networkId should
1488         * refer to an existing configuration.
1489         */
1490        int netId = config.networkId;
1491        boolean newNetwork = netId == -1;
1492        // networkId of -1 means we want to create a new network
1493
1494        if (newNetwork) {
1495            netId = WifiNative.addNetworkCommand();
1496            if (netId < 0) {
1497                Log.e(TAG, "Failed to add a network!");
1498                return -1;
1499          }
1500        }
1501
1502        setVariables: {
1503
1504            if (config.SSID != null &&
1505                    !WifiNative.setNetworkVariableCommand(
1506                        netId,
1507                        WifiConfiguration.ssidVarName,
1508                        config.SSID)) {
1509                Log.d(TAG, "failed to set SSID: "+config.SSID);
1510                break setVariables;
1511            }
1512
1513            if (config.BSSID != null &&
1514                    !WifiNative.setNetworkVariableCommand(
1515                        netId,
1516                        WifiConfiguration.bssidVarName,
1517                        config.BSSID)) {
1518                Log.d(TAG, "failed to set BSSID: "+config.BSSID);
1519                break setVariables;
1520            }
1521
1522            String allowedKeyManagementString =
1523                makeString(config.allowedKeyManagement, WifiConfiguration.KeyMgmt.strings);
1524            if (config.allowedKeyManagement.cardinality() != 0 &&
1525                    !WifiNative.setNetworkVariableCommand(
1526                        netId,
1527                        WifiConfiguration.KeyMgmt.varName,
1528                        allowedKeyManagementString)) {
1529                Log.d(TAG, "failed to set key_mgmt: "+
1530                        allowedKeyManagementString);
1531                break setVariables;
1532            }
1533
1534            String allowedProtocolsString =
1535                makeString(config.allowedProtocols, WifiConfiguration.Protocol.strings);
1536            if (config.allowedProtocols.cardinality() != 0 &&
1537                    !WifiNative.setNetworkVariableCommand(
1538                        netId,
1539                        WifiConfiguration.Protocol.varName,
1540                        allowedProtocolsString)) {
1541                Log.d(TAG, "failed to set proto: "+
1542                        allowedProtocolsString);
1543                break setVariables;
1544            }
1545
1546            String allowedAuthAlgorithmsString =
1547                makeString(config.allowedAuthAlgorithms, WifiConfiguration.AuthAlgorithm.strings);
1548            if (config.allowedAuthAlgorithms.cardinality() != 0 &&
1549                    !WifiNative.setNetworkVariableCommand(
1550                        netId,
1551                        WifiConfiguration.AuthAlgorithm.varName,
1552                        allowedAuthAlgorithmsString)) {
1553                Log.d(TAG, "failed to set auth_alg: "+
1554                        allowedAuthAlgorithmsString);
1555                break setVariables;
1556            }
1557
1558            String allowedPairwiseCiphersString =
1559                    makeString(config.allowedPairwiseCiphers,
1560                    WifiConfiguration.PairwiseCipher.strings);
1561            if (config.allowedPairwiseCiphers.cardinality() != 0 &&
1562                    !WifiNative.setNetworkVariableCommand(
1563                        netId,
1564                        WifiConfiguration.PairwiseCipher.varName,
1565                        allowedPairwiseCiphersString)) {
1566                Log.d(TAG, "failed to set pairwise: "+
1567                        allowedPairwiseCiphersString);
1568                break setVariables;
1569            }
1570
1571            String allowedGroupCiphersString =
1572                makeString(config.allowedGroupCiphers, WifiConfiguration.GroupCipher.strings);
1573            if (config.allowedGroupCiphers.cardinality() != 0 &&
1574                    !WifiNative.setNetworkVariableCommand(
1575                        netId,
1576                        WifiConfiguration.GroupCipher.varName,
1577                        allowedGroupCiphersString)) {
1578                Log.d(TAG, "failed to set group: "+
1579                        allowedGroupCiphersString);
1580                break setVariables;
1581            }
1582
1583            // Prevent client screw-up by passing in a WifiConfiguration we gave it
1584            // by preventing "*" as a key.
1585            if (config.preSharedKey != null && !config.preSharedKey.equals("*") &&
1586                    !WifiNative.setNetworkVariableCommand(
1587                        netId,
1588                        WifiConfiguration.pskVarName,
1589                        config.preSharedKey)) {
1590                Log.d(TAG, "failed to set psk: "+config.preSharedKey);
1591                break setVariables;
1592            }
1593
1594            boolean hasSetKey = false;
1595            if (config.wepKeys != null) {
1596                for (int i = 0; i < config.wepKeys.length; i++) {
1597                    // Prevent client screw-up by passing in a WifiConfiguration we gave it
1598                    // by preventing "*" as a key.
1599                    if (config.wepKeys[i] != null && !config.wepKeys[i].equals("*")) {
1600                        if (!WifiNative.setNetworkVariableCommand(
1601                                    netId,
1602                                    WifiConfiguration.wepKeyVarNames[i],
1603                                    config.wepKeys[i])) {
1604                            Log.d(TAG,
1605                                    "failed to set wep_key"+i+": " +
1606                                    config.wepKeys[i]);
1607                            break setVariables;
1608                        }
1609                        hasSetKey = true;
1610                    }
1611                }
1612            }
1613
1614            if (hasSetKey) {
1615                if (!WifiNative.setNetworkVariableCommand(
1616                            netId,
1617                            WifiConfiguration.wepTxKeyIdxVarName,
1618                            Integer.toString(config.wepTxKeyIndex))) {
1619                    Log.d(TAG,
1620                            "failed to set wep_tx_keyidx: "+
1621                            config.wepTxKeyIndex);
1622                    break setVariables;
1623                }
1624            }
1625
1626            if (!WifiNative.setNetworkVariableCommand(
1627                        netId,
1628                        WifiConfiguration.priorityVarName,
1629                        Integer.toString(config.priority))) {
1630                Log.d(TAG, config.SSID + ": failed to set priority: "
1631                        +config.priority);
1632                break setVariables;
1633            }
1634
1635            if (config.hiddenSSID && !WifiNative.setNetworkVariableCommand(
1636                        netId,
1637                        WifiConfiguration.hiddenSSIDVarName,
1638                        Integer.toString(config.hiddenSSID ? 1 : 0))) {
1639                Log.d(TAG, config.SSID + ": failed to set hiddenSSID: "+
1640                        config.hiddenSSID);
1641                break setVariables;
1642            }
1643
1644            for (WifiConfiguration.EnterpriseField field
1645                    : config.enterpriseFields) {
1646                String varName = field.varName();
1647                String value = field.value();
1648                if (value != null) {
1649                    if (field != config.eap) {
1650                        value = (value.length() == 0) ? "NULL" : convertToQuotedString(value);
1651                    }
1652                    if (!WifiNative.setNetworkVariableCommand(
1653                                netId,
1654                                varName,
1655                                value)) {
1656                        Log.d(TAG, config.SSID + ": failed to set " + varName +
1657                                ": " + value);
1658                        break setVariables;
1659                    }
1660                }
1661            }
1662            return netId;
1663        }
1664
1665        if (newNetwork) {
1666            WifiNative.removeNetworkCommand(netId);
1667            Log.d(TAG,
1668                    "Failed to set a network variable, removed network: "
1669                    + netId);
1670        }
1671
1672        return -1;
1673    }
1674
1675    private List<WifiConfiguration> getConfiguredNetworksNative() {
1676        String listStr = WifiNative.listNetworksCommand();
1677        mLastPriority = 0;
1678        List<WifiConfiguration> networks =
1679            new ArrayList<WifiConfiguration>();
1680        if (listStr == null)
1681            return networks;
1682
1683        String[] lines = listStr.split("\n");
1684        // Skip the first line, which is a header
1685        for (int i = 1; i < lines.length; i++) {
1686            String[] result = lines[i].split("\t");
1687            // network-id | ssid | bssid | flags
1688            WifiConfiguration config = new WifiConfiguration();
1689            try {
1690                config.networkId = Integer.parseInt(result[0]);
1691            } catch(NumberFormatException e) {
1692                continue;
1693            }
1694            if (result.length > 3) {
1695                if (result[3].indexOf("[CURRENT]") != -1)
1696                    config.status = WifiConfiguration.Status.CURRENT;
1697                else if (result[3].indexOf("[DISABLED]") != -1)
1698                    config.status = WifiConfiguration.Status.DISABLED;
1699                else
1700                    config.status = WifiConfiguration.Status.ENABLED;
1701            } else {
1702                config.status = WifiConfiguration.Status.ENABLED;
1703            }
1704            readNetworkVariables(config);
1705            if (config.priority > mLastPriority) {
1706                mLastPriority = config.priority;
1707            }
1708            networks.add(config);
1709        }
1710        return networks;
1711    }
1712
1713    /**
1714     * Read the variables from the supplicant daemon that are needed to
1715     * fill in the WifiConfiguration object.
1716     *
1717     * @param config the {@link WifiConfiguration} object to be filled in.
1718     */
1719    private void readNetworkVariables(WifiConfiguration config) {
1720
1721        int netId = config.networkId;
1722        if (netId < 0)
1723            return;
1724
1725        /*
1726         * TODO: maybe should have a native method that takes an array of
1727         * variable names and returns an array of values. But we'd still
1728         * be doing a round trip to the supplicant daemon for each variable.
1729         */
1730        String value;
1731
1732        value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.ssidVarName);
1733        if (!TextUtils.isEmpty(value)) {
1734            config.SSID = removeDoubleQuotes(value);
1735        } else {
1736            config.SSID = null;
1737        }
1738
1739        value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.bssidVarName);
1740        if (!TextUtils.isEmpty(value)) {
1741            config.BSSID = value;
1742        } else {
1743            config.BSSID = null;
1744        }
1745
1746        value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.priorityVarName);
1747        config.priority = -1;
1748        if (!TextUtils.isEmpty(value)) {
1749            try {
1750                config.priority = Integer.parseInt(value);
1751            } catch (NumberFormatException ignore) {
1752            }
1753        }
1754
1755        value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.hiddenSSIDVarName);
1756        config.hiddenSSID = false;
1757        if (!TextUtils.isEmpty(value)) {
1758            try {
1759                config.hiddenSSID = Integer.parseInt(value) != 0;
1760            } catch (NumberFormatException ignore) {
1761            }
1762        }
1763
1764        value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.wepTxKeyIdxVarName);
1765        config.wepTxKeyIndex = -1;
1766        if (!TextUtils.isEmpty(value)) {
1767            try {
1768                config.wepTxKeyIndex = Integer.parseInt(value);
1769            } catch (NumberFormatException ignore) {
1770            }
1771        }
1772
1773        for (int i = 0; i < 4; i++) {
1774            value = WifiNative.getNetworkVariableCommand(netId,
1775                    WifiConfiguration.wepKeyVarNames[i]);
1776            if (!TextUtils.isEmpty(value)) {
1777                config.wepKeys[i] = value;
1778            } else {
1779                config.wepKeys[i] = null;
1780            }
1781        }
1782
1783        value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.pskVarName);
1784        if (!TextUtils.isEmpty(value)) {
1785            config.preSharedKey = value;
1786        } else {
1787            config.preSharedKey = null;
1788        }
1789
1790        value = WifiNative.getNetworkVariableCommand(config.networkId,
1791                WifiConfiguration.Protocol.varName);
1792        if (!TextUtils.isEmpty(value)) {
1793            String vals[] = value.split(" ");
1794            for (String val : vals) {
1795                int index =
1796                    lookupString(val, WifiConfiguration.Protocol.strings);
1797                if (0 <= index) {
1798                    config.allowedProtocols.set(index);
1799                }
1800            }
1801        }
1802
1803        value = WifiNative.getNetworkVariableCommand(config.networkId,
1804                WifiConfiguration.KeyMgmt.varName);
1805        if (!TextUtils.isEmpty(value)) {
1806            String vals[] = value.split(" ");
1807            for (String val : vals) {
1808                int index =
1809                    lookupString(val, WifiConfiguration.KeyMgmt.strings);
1810                if (0 <= index) {
1811                    config.allowedKeyManagement.set(index);
1812                }
1813            }
1814        }
1815
1816        value = WifiNative.getNetworkVariableCommand(config.networkId,
1817                WifiConfiguration.AuthAlgorithm.varName);
1818        if (!TextUtils.isEmpty(value)) {
1819            String vals[] = value.split(" ");
1820            for (String val : vals) {
1821                int index =
1822                    lookupString(val, WifiConfiguration.AuthAlgorithm.strings);
1823                if (0 <= index) {
1824                    config.allowedAuthAlgorithms.set(index);
1825                }
1826            }
1827        }
1828
1829        value = WifiNative.getNetworkVariableCommand(config.networkId,
1830                WifiConfiguration.PairwiseCipher.varName);
1831        if (!TextUtils.isEmpty(value)) {
1832            String vals[] = value.split(" ");
1833            for (String val : vals) {
1834                int index =
1835                    lookupString(val, WifiConfiguration.PairwiseCipher.strings);
1836                if (0 <= index) {
1837                    config.allowedPairwiseCiphers.set(index);
1838                }
1839            }
1840        }
1841
1842        value = WifiNative.getNetworkVariableCommand(config.networkId,
1843                WifiConfiguration.GroupCipher.varName);
1844        if (!TextUtils.isEmpty(value)) {
1845            String vals[] = value.split(" ");
1846            for (String val : vals) {
1847                int index =
1848                    lookupString(val, WifiConfiguration.GroupCipher.strings);
1849                if (0 <= index) {
1850                    config.allowedGroupCiphers.set(index);
1851                }
1852            }
1853        }
1854
1855        for (WifiConfiguration.EnterpriseField field :
1856                config.enterpriseFields) {
1857            value = WifiNative.getNetworkVariableCommand(netId,
1858                    field.varName());
1859            if (!TextUtils.isEmpty(value)) {
1860                if (field != config.eap) value = removeDoubleQuotes(value);
1861                field.setValue(value);
1862            }
1863        }
1864
1865    }
1866
1867    /**
1868     * Poll for info not reported via events
1869     * RSSI & Linkspeed
1870     */
1871    private void requestPolledInfo() {
1872        int newRssi = WifiNative.getRssiCommand();
1873        if (newRssi != -1 && -200 < newRssi && newRssi < 256) { // screen out invalid values
1874            /* some implementations avoid negative values by adding 256
1875             * so we need to adjust for that here.
1876             */
1877            if (newRssi > 0) newRssi -= 256;
1878            mWifiInfo.setRssi(newRssi);
1879            /*
1880             * Rather then sending the raw RSSI out every time it
1881             * changes, we precalculate the signal level that would
1882             * be displayed in the status bar, and only send the
1883             * broadcast if that much more coarse-grained number
1884             * changes. This cuts down greatly on the number of
1885             * broadcasts, at the cost of not mWifiInforming others
1886             * interested in RSSI of all the changes in signal
1887             * level.
1888             */
1889            // TODO: The second arg to the call below needs to be a symbol somewhere, but
1890            // it's actually the size of an array of icons that's private
1891            // to StatusBar Policy.
1892            int newSignalLevel = WifiManager.calculateSignalLevel(newRssi, 4);
1893            if (newSignalLevel != mLastSignalLevel) {
1894                sendRssiChangeBroadcast(newRssi);
1895            }
1896            mLastSignalLevel = newSignalLevel;
1897        } else {
1898            mWifiInfo.setRssi(-200);
1899        }
1900        int newLinkSpeed = WifiNative.getLinkSpeedCommand();
1901        if (newLinkSpeed != -1) {
1902            mWifiInfo.setLinkSpeed(newLinkSpeed);
1903        }
1904    }
1905
1906    /**
1907     * Resets the Wi-Fi Connections by clearing any state, resetting any sockets
1908     * using the interface, stopping DHCP & disabling interface
1909     */
1910    private void handleNetworkDisconnect() {
1911        Log.d(TAG, "Reset connections and stopping DHCP");
1912
1913        /*
1914         * Reset connections & stop DHCP
1915         */
1916        NetworkUtils.resetConnections(mInterfaceName);
1917
1918        if (!NetworkUtils.stopDhcp(mInterfaceName)) {
1919            Log.e(TAG, "Could not stop DHCP");
1920        }
1921
1922        /* Disable interface */
1923        NetworkUtils.disableInterface(mInterfaceName);
1924
1925        /* send event to CM & network change broadcast */
1926        setDetailedState(DetailedState.DISCONNECTED);
1927        sendNetworkStateChangeBroadcast(mLastBssid);
1928
1929        /* Reset data structures */
1930        mWifiInfo.setIpAddress(0);
1931        mWifiInfo.setBSSID(null);
1932        mWifiInfo.setSSID(null);
1933        mWifiInfo.setNetworkId(-1);
1934
1935        /* Clear network properties */
1936        mNetworkProperties.clear();
1937
1938        mLastBssid= null;
1939        mLastNetworkId = -1;
1940
1941    }
1942
1943
1944    /*********************************************************
1945     * Notifications from WifiMonitor
1946     ********************************************************/
1947
1948    /**
1949     * A structure for supplying information about a supplicant state
1950     * change in the STATE_CHANGE event message that comes from the
1951     * WifiMonitor
1952     * thread.
1953     */
1954    private static class StateChangeResult {
1955        StateChangeResult(int networkId, String BSSID, Object state) {
1956            this.state = state;
1957            this.BSSID = BSSID;
1958            this.networkId = networkId;
1959        }
1960        int networkId;
1961        String BSSID;
1962        Object state;
1963    }
1964
1965    /**
1966     * Send the tracker a notification that a user-entered password key
1967     * may be incorrect (i.e., caused authentication to fail).
1968     */
1969    void notifyPasswordKeyMayBeIncorrect() {
1970        sendMessage(PASSWORD_MAY_BE_INCORRECT_EVENT);
1971    }
1972
1973    /**
1974     * Send the tracker a notification that a connection to the supplicant
1975     * daemon has been established.
1976     */
1977    void notifySupplicantConnection() {
1978        sendMessage(SUP_CONNECTION_EVENT);
1979    }
1980
1981    /**
1982     * Send the tracker a notification that a connection to the supplicant
1983     * daemon has been established.
1984     */
1985    void notifySupplicantLost() {
1986        sendMessage(SUP_DISCONNECTION_EVENT);
1987    }
1988
1989    /**
1990     * Send the tracker a notification that the state of Wifi connectivity
1991     * has changed.
1992     * @param networkId the configured network on which the state change occurred
1993     * @param newState the new network state
1994     * @param BSSID when the new state is {@link DetailedState#CONNECTED
1995     * NetworkInfo.DetailedState.CONNECTED},
1996     * this is the MAC address of the access point. Otherwise, it
1997     * is {@code null}.
1998     */
1999    void notifyNetworkStateChange(DetailedState newState, String BSSID, int networkId) {
2000        if (newState == NetworkInfo.DetailedState.CONNECTED) {
2001            sendMessage(obtainMessage(NETWORK_CONNECTION_EVENT,
2002                    new StateChangeResult(networkId, BSSID, newState)));
2003        } else {
2004            sendMessage(obtainMessage(NETWORK_DISCONNECTION_EVENT,
2005                    new StateChangeResult(networkId, BSSID, newState)));
2006        }
2007    }
2008
2009    /**
2010     * Send the tracker a notification that the state of the supplicant
2011     * has changed.
2012     * @param networkId the configured network on which the state change occurred
2013     * @param newState the new {@code SupplicantState}
2014     */
2015    void notifySupplicantStateChange(int networkId, String BSSID, SupplicantState newState) {
2016        sendMessage(obtainMessage(SUPPLICANT_STATE_CHANGE_EVENT,
2017                new StateChangeResult(networkId, BSSID, newState)));
2018    }
2019
2020    /**
2021     * Send the tracker a notification that a scan has completed, and results
2022     * are available.
2023     */
2024    void notifyScanResultsAvailable() {
2025        /**
2026         * Switch scan mode over to passive.
2027         * Turning off scan-only mode happens only in "Connect" mode
2028         */
2029        setScanType(false);
2030        sendMessage(SCAN_RESULTS_EVENT);
2031    }
2032
2033    void notifyDriverStarted() {
2034        sendMessage(DRIVER_START_EVENT);
2035    }
2036
2037    void notifyDriverStopped() {
2038        sendMessage(DRIVER_STOP_EVENT);
2039    }
2040
2041    void notifyDriverHung() {
2042        setWifiEnabled(false);
2043        setWifiEnabled(true);
2044    }
2045
2046
2047    /********************************************************
2048     * HSM states
2049     *******************************************************/
2050
2051    class DefaultState extends HierarchicalState {
2052        @Override
2053        public boolean processMessage(Message message) {
2054            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
2055            SyncParams syncParams;
2056            switch (message.what) {
2057                    /* Synchronous call returns */
2058                case CMD_PING_SUPPLICANT:
2059                case CMD_REMOVE_NETWORK:
2060                case CMD_ENABLE_NETWORK:
2061                case CMD_DISABLE_NETWORK:
2062                case CMD_ADD_OR_UPDATE_NETWORK:
2063                case CMD_GET_RSSI:
2064                case CMD_GET_RSSI_APPROX:
2065                case CMD_GET_LINK_SPEED:
2066                case CMD_GET_MAC_ADDR:
2067                case CMD_SAVE_CONFIG:
2068                case CMD_CONNECTION_STATUS:
2069                case CMD_GET_NETWORK_CONFIG:
2070                    if (message.arg2 == SYNCHRONOUS_CALL) {
2071                        syncParams = (SyncParams) message.obj;
2072                        syncParams.mSyncReturn.boolValue = false;
2073                        syncParams.mSyncReturn.intValue = -1;
2074                        syncParams.mSyncReturn.stringValue = null;
2075                        syncParams.mSyncReturn.configList = null;
2076                        notifyOnMsgObject(message);
2077                    }
2078                    break;
2079                case CMD_ENABLE_RSSI_POLL:
2080                    mEnableRssiPolling = (message.arg1 == 1);
2081                    mSupplicantStateTracker.sendMessage(CMD_ENABLE_RSSI_POLL);
2082                    break;
2083                    /* Discard */
2084                case CMD_LOAD_DRIVER:
2085                case CMD_UNLOAD_DRIVER:
2086                case CMD_START_SUPPLICANT:
2087                case CMD_STOP_SUPPLICANT:
2088                case CMD_START_DRIVER:
2089                case CMD_STOP_DRIVER:
2090                case CMD_START_AP:
2091                case CMD_STOP_AP:
2092                case CMD_START_SCAN:
2093                case CMD_DISCONNECT:
2094                case CMD_RECONNECT:
2095                case CMD_REASSOCIATE:
2096                case CMD_RECONFIGURE_IP:
2097                case SUP_CONNECTION_EVENT:
2098                case SUP_DISCONNECTION_EVENT:
2099                case DRIVER_START_EVENT:
2100                case DRIVER_STOP_EVENT:
2101                case NETWORK_CONNECTION_EVENT:
2102                case NETWORK_DISCONNECTION_EVENT:
2103                case SCAN_RESULTS_EVENT:
2104                case SUPPLICANT_STATE_CHANGE_EVENT:
2105                case PASSWORD_MAY_BE_INCORRECT_EVENT:
2106                case CMD_BLACKLIST_NETWORK:
2107                case CMD_CLEAR_BLACKLIST:
2108                case CMD_SET_SCAN_MODE:
2109                case CMD_SET_SCAN_TYPE:
2110                case CMD_SET_POWER_MODE:
2111                case CMD_SET_BLUETOOTH_COEXISTENCE:
2112                case CMD_SET_BLUETOOTH_SCAN_MODE:
2113                case CMD_SET_NUM_ALLOWED_CHANNELS:
2114                case CMD_REQUEST_CM_WAKELOCK:
2115                case CMD_CONNECT_NETWORK:
2116                case CMD_SAVE_NETWORK:
2117                case CMD_FORGET_NETWORK:
2118                    break;
2119                default:
2120                    Log.e(TAG, "Error! unhandled message" + message);
2121                    break;
2122            }
2123            return HANDLED;
2124        }
2125    }
2126
2127    class InitialState extends HierarchicalState {
2128        @Override
2129        //TODO: could move logging into a common class
2130        public void enter() {
2131            if (DBG) Log.d(TAG, getName() + "\n");
2132            // [31-8] Reserved for future use
2133            // [7 - 0] HSM state change
2134            // 50021 wifi_state_changed (custom|1|5)
2135            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
2136
2137            if (WifiNative.isDriverLoaded()) {
2138                transitionTo(mDriverLoadedState);
2139            }
2140            else {
2141                transitionTo(mDriverUnloadedState);
2142            }
2143        }
2144    }
2145
2146    class DriverLoadingState extends HierarchicalState {
2147        @Override
2148        public void enter() {
2149            if (DBG) Log.d(TAG, getName() + "\n");
2150            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
2151
2152            final Message message = new Message();
2153            message.copyFrom(getCurrentMessage());
2154            /* TODO: add a timeout to fail when driver load is hung.
2155             * Similarly for driver unload.
2156             */
2157            new Thread(new Runnable() {
2158                public void run() {
2159                    sWakeLock.acquire();
2160                    //enabling state
2161                    switch(message.arg1) {
2162                        case WIFI_STATE_ENABLING:
2163                            setWifiState(WIFI_STATE_ENABLING);
2164                            break;
2165                        case WIFI_AP_STATE_ENABLING:
2166                            setWifiApState(WIFI_AP_STATE_ENABLING);
2167                            break;
2168                    }
2169
2170                    if(WifiNative.loadDriver()) {
2171                        Log.d(TAG, "Driver load successful");
2172                        sendMessage(CMD_LOAD_DRIVER_SUCCESS);
2173                    } else {
2174                        Log.e(TAG, "Failed to load driver!");
2175                        switch(message.arg1) {
2176                            case WIFI_STATE_ENABLING:
2177                                setWifiState(WIFI_STATE_UNKNOWN);
2178                                break;
2179                            case WIFI_AP_STATE_ENABLING:
2180                                setWifiApState(WIFI_AP_STATE_FAILED);
2181                                break;
2182                        }
2183                        sendMessage(CMD_LOAD_DRIVER_FAILURE);
2184                    }
2185                    sWakeLock.release();
2186                }
2187            }).start();
2188        }
2189
2190        @Override
2191        public boolean processMessage(Message message) {
2192            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
2193            switch (message.what) {
2194                case CMD_LOAD_DRIVER_SUCCESS:
2195                    transitionTo(mDriverLoadedState);
2196                    break;
2197                case CMD_LOAD_DRIVER_FAILURE:
2198                    transitionTo(mDriverFailedState);
2199                    break;
2200                case CMD_LOAD_DRIVER:
2201                case CMD_UNLOAD_DRIVER:
2202                case CMD_START_SUPPLICANT:
2203                case CMD_STOP_SUPPLICANT:
2204                case CMD_START_AP:
2205                case CMD_STOP_AP:
2206                case CMD_START_DRIVER:
2207                case CMD_STOP_DRIVER:
2208                case CMD_SET_SCAN_MODE:
2209                case CMD_SET_SCAN_TYPE:
2210                case CMD_SET_POWER_MODE:
2211                case CMD_SET_BLUETOOTH_COEXISTENCE:
2212                case CMD_SET_BLUETOOTH_SCAN_MODE:
2213                case CMD_SET_NUM_ALLOWED_CHANNELS:
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            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
2222            return HANDLED;
2223        }
2224    }
2225
2226    class DriverLoadedState extends HierarchicalState {
2227        @Override
2228        public void enter() {
2229            if (DBG) Log.d(TAG, getName() + "\n");
2230            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
2231        }
2232        @Override
2233        public boolean processMessage(Message message) {
2234            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
2235            switch(message.what) {
2236                case CMD_UNLOAD_DRIVER:
2237                    transitionTo(mDriverUnloadingState);
2238                    break;
2239                case CMD_START_SUPPLICANT:
2240                    if(WifiNative.startSupplicant()) {
2241                        Log.d(TAG, "Supplicant start successful");
2242                        mWifiMonitor.startMonitoring();
2243                        setWifiState(WIFI_STATE_ENABLED);
2244                        transitionTo(mWaitForSupState);
2245                    } else {
2246                        Log.e(TAG, "Failed to start supplicant!");
2247                        sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_STATE_UNKNOWN, 0));
2248                    }
2249                    break;
2250                case CMD_START_AP:
2251                    try {
2252                        nwService.startAccessPoint((WifiConfiguration) message.obj,
2253                                    mInterfaceName,
2254                                    SOFTAP_IFACE);
2255                    } catch(Exception e) {
2256                        Log.e(TAG, "Exception in startAccessPoint()");
2257                        sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_AP_STATE_FAILED, 0));
2258                        break;
2259                    }
2260                    Log.d(TAG, "Soft AP start successful");
2261                    setWifiApState(WIFI_AP_STATE_ENABLED);
2262                    transitionTo(mSoftApStartedState);
2263                    break;
2264                default:
2265                    return NOT_HANDLED;
2266            }
2267            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
2268            return HANDLED;
2269        }
2270    }
2271
2272    class DriverUnloadingState extends HierarchicalState {
2273        @Override
2274        public void enter() {
2275            if (DBG) Log.d(TAG, getName() + "\n");
2276            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
2277
2278            final Message message = new Message();
2279            message.copyFrom(getCurrentMessage());
2280            new Thread(new Runnable() {
2281                public void run() {
2282                    if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
2283                    sWakeLock.acquire();
2284                    if(WifiNative.unloadDriver()) {
2285                        Log.d(TAG, "Driver unload successful");
2286                        sendMessage(CMD_UNLOAD_DRIVER_SUCCESS);
2287
2288                        switch(message.arg1) {
2289                            case WIFI_STATE_DISABLED:
2290                            case WIFI_STATE_UNKNOWN:
2291                                setWifiState(message.arg1);
2292                                break;
2293                            case WIFI_AP_STATE_DISABLED:
2294                            case WIFI_AP_STATE_FAILED:
2295                                setWifiApState(message.arg1);
2296                                break;
2297                        }
2298                    } else {
2299                        Log.e(TAG, "Failed to unload driver!");
2300                        sendMessage(CMD_UNLOAD_DRIVER_FAILURE);
2301
2302                        switch(message.arg1) {
2303                            case WIFI_STATE_DISABLED:
2304                            case WIFI_STATE_UNKNOWN:
2305                                setWifiState(WIFI_STATE_UNKNOWN);
2306                                break;
2307                            case WIFI_AP_STATE_DISABLED:
2308                            case WIFI_AP_STATE_FAILED:
2309                                setWifiApState(WIFI_AP_STATE_FAILED);
2310                                break;
2311                        }
2312                    }
2313                    sWakeLock.release();
2314                }
2315            }).start();
2316        }
2317
2318        @Override
2319        public boolean processMessage(Message message) {
2320            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
2321            switch (message.what) {
2322                case CMD_UNLOAD_DRIVER_SUCCESS:
2323                    transitionTo(mDriverUnloadedState);
2324                    break;
2325                case CMD_UNLOAD_DRIVER_FAILURE:
2326                    transitionTo(mDriverFailedState);
2327                    break;
2328                case CMD_LOAD_DRIVER:
2329                case CMD_UNLOAD_DRIVER:
2330                case CMD_START_SUPPLICANT:
2331                case CMD_STOP_SUPPLICANT:
2332                case CMD_START_AP:
2333                case CMD_STOP_AP:
2334                case CMD_START_DRIVER:
2335                case CMD_STOP_DRIVER:
2336                case CMD_SET_SCAN_MODE:
2337                case CMD_SET_SCAN_TYPE:
2338                case CMD_SET_POWER_MODE:
2339                case CMD_SET_BLUETOOTH_COEXISTENCE:
2340                case CMD_SET_BLUETOOTH_SCAN_MODE:
2341                case CMD_SET_NUM_ALLOWED_CHANNELS:
2342                case CMD_START_PACKET_FILTERING:
2343                case CMD_STOP_PACKET_FILTERING:
2344                    deferMessage(message);
2345                    break;
2346                default:
2347                    return NOT_HANDLED;
2348            }
2349            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
2350            return HANDLED;
2351        }
2352    }
2353
2354    class DriverUnloadedState extends HierarchicalState {
2355        @Override
2356        public void enter() {
2357            if (DBG) Log.d(TAG, getName() + "\n");
2358            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
2359        }
2360        @Override
2361        public boolean processMessage(Message message) {
2362            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
2363            switch (message.what) {
2364                case CMD_LOAD_DRIVER:
2365                    transitionTo(mDriverLoadingState);
2366                    break;
2367                default:
2368                    return NOT_HANDLED;
2369            }
2370            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
2371            return HANDLED;
2372        }
2373    }
2374
2375    class DriverFailedState extends HierarchicalState {
2376        @Override
2377        public void enter() {
2378            Log.e(TAG, getName() + "\n");
2379            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
2380        }
2381        @Override
2382        public boolean processMessage(Message message) {
2383            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
2384            return NOT_HANDLED;
2385        }
2386    }
2387
2388
2389    class WaitForSupState extends HierarchicalState {
2390        @Override
2391        public void enter() {
2392            if (DBG) Log.d(TAG, getName() + "\n");
2393            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
2394        }
2395        @Override
2396        public boolean processMessage(Message message) {
2397            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
2398            switch(message.what) {
2399                case SUP_CONNECTION_EVENT:
2400                    Log.d(TAG, "Supplicant connection established");
2401                    mSupplicantStateTracker.resetSupplicantState();
2402                    /* Initialize data structures */
2403                    mLastBssid = null;
2404                    mLastNetworkId = -1;
2405                    mLastSignalLevel = -1;
2406
2407                    mWifiInfo.setMacAddress(WifiNative.getMacAddressCommand());
2408
2409                    //TODO: initialize and fix multicast filtering
2410                    //mWM.initializeMulticastFiltering();
2411
2412                    if (mBluetoothA2dp == null) {
2413                        mBluetoothA2dp = new BluetoothA2dp(mContext);
2414                    }
2415                    checkIsBluetoothPlaying();
2416
2417                    checkUseStaticIp();
2418                    sendSupplicantConnectionChangedBroadcast(true);
2419                    transitionTo(mDriverSupReadyState);
2420                    break;
2421                case CMD_STOP_SUPPLICANT:
2422                    Log.d(TAG, "Stop supplicant received");
2423                    WifiNative.stopSupplicant();
2424                    transitionTo(mDriverLoadedState);
2425                    break;
2426                    /* Fail soft ap when waiting for supplicant start */
2427                case CMD_START_AP:
2428                    Log.d(TAG, "Failed to start soft AP with a running supplicant");
2429                    setWifiApState(WIFI_AP_STATE_FAILED);
2430                    break;
2431                case CMD_START_DRIVER:
2432                case CMD_STOP_DRIVER:
2433                case CMD_SET_SCAN_MODE:
2434                case CMD_SET_SCAN_TYPE:
2435                case CMD_SET_POWER_MODE:
2436                case CMD_SET_BLUETOOTH_COEXISTENCE:
2437                case CMD_SET_BLUETOOTH_SCAN_MODE:
2438                case CMD_SET_NUM_ALLOWED_CHANNELS:
2439                case CMD_START_PACKET_FILTERING:
2440                case CMD_STOP_PACKET_FILTERING:
2441                    deferMessage(message);
2442                    break;
2443                case CMD_STOP_AP:
2444                case CMD_START_SUPPLICANT:
2445                case CMD_UNLOAD_DRIVER:
2446                    break;
2447                default:
2448                    return NOT_HANDLED;
2449            }
2450            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
2451            return HANDLED;
2452        }
2453    }
2454
2455    class DriverSupReadyState extends HierarchicalState {
2456        @Override
2457        public void enter() {
2458            if (DBG) Log.d(TAG, getName() + "\n");
2459            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
2460            /* Initialize for connect mode operation at start */
2461            mIsScanMode = false;
2462        }
2463        @Override
2464        public boolean processMessage(Message message) {
2465            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
2466            SyncParams syncParams;
2467            WifiConfiguration config;
2468            switch(message.what) {
2469                case CMD_STOP_SUPPLICANT:   /* Supplicant stopped by user */
2470                    Log.d(TAG, "Stop supplicant received");
2471                    WifiNative.stopSupplicant();
2472                    //$FALL-THROUGH$
2473                case SUP_DISCONNECTION_EVENT:  /* Supplicant died */
2474                    EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
2475                    WifiNative.closeSupplicantConnection();
2476                    handleNetworkDisconnect();
2477                    sendSupplicantConnectionChangedBroadcast(false);
2478                    mSupplicantStateTracker.resetSupplicantState();
2479                    transitionTo(mDriverLoadedState);
2480
2481                    /* When supplicant dies, unload driver and enter failed state */
2482                    //TODO: consider bringing up supplicant again
2483                    if (message.what == SUP_DISCONNECTION_EVENT) {
2484                        Log.d(TAG, "Supplicant died, unloading driver");
2485                        sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_STATE_UNKNOWN, 0));
2486                    }
2487                    break;
2488                case CMD_START_DRIVER:
2489                    EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
2490                    WifiNative.startDriverCommand();
2491                    transitionTo(mDriverStartingState);
2492                    break;
2493                case SCAN_RESULTS_EVENT:
2494                    setScanResults(WifiNative.scanResultsCommand());
2495                    sendScanResultsAvailableBroadcast();
2496                    break;
2497                case CMD_PING_SUPPLICANT:
2498                    syncParams = (SyncParams) message.obj;
2499                    syncParams.mSyncReturn.boolValue = WifiNative.pingCommand();
2500                    notifyOnMsgObject(message);
2501                    break;
2502                case CMD_ADD_OR_UPDATE_NETWORK:
2503                    EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
2504                    syncParams = (SyncParams) message.obj;
2505                    config = (WifiConfiguration) syncParams.mParameter;
2506                    syncParams.mSyncReturn.intValue = addOrUpdateNetworkNative(config);
2507                    notifyOnMsgObject(message);
2508                    sendSupplicantConfigChangedBroadcast();
2509                    break;
2510                case CMD_REMOVE_NETWORK:
2511                    EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
2512                    if (message.arg2 == SYNCHRONOUS_CALL) {
2513                        syncParams = (SyncParams) message.obj;
2514                        syncParams.mSyncReturn.boolValue = WifiNative.removeNetworkCommand(
2515                                message.arg1);
2516                        notifyOnMsgObject(message);
2517                    } else {
2518                        /* asynchronous handling */
2519                        WifiNative.removeNetworkCommand(message.arg1);
2520                    }
2521                    sendSupplicantConfigChangedBroadcast();
2522                    break;
2523                case CMD_ENABLE_NETWORK:
2524                    EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
2525                    if (message.arg2 == SYNCHRONOUS_CALL) {
2526                        syncParams = (SyncParams) message.obj;
2527                        EnableNetParams enableNetParams = (EnableNetParams) syncParams.mParameter;
2528                        syncParams.mSyncReturn.boolValue = WifiNative.enableNetworkCommand(
2529                                enableNetParams.netId, enableNetParams.disableOthers);
2530                        notifyOnMsgObject(message);
2531                    } else {
2532                        /* asynchronous handling */
2533                        WifiNative.enableNetworkCommand(message.arg1, message.arg2 == 1);
2534                    }
2535                    sendSupplicantConfigChangedBroadcast();
2536                    break;
2537                case CMD_DISABLE_NETWORK:
2538                    EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
2539                    if (message.arg2 == SYNCHRONOUS_CALL) {
2540                        syncParams = (SyncParams) message.obj;
2541                        syncParams.mSyncReturn.boolValue = WifiNative.disableNetworkCommand(
2542                                message.arg1);
2543                        notifyOnMsgObject(message);
2544                    } else {
2545                        /* asynchronous handling */
2546                        WifiNative.disableNetworkCommand(message.arg1);
2547                    }
2548                    sendSupplicantConfigChangedBroadcast();
2549                    break;
2550                case CMD_BLACKLIST_NETWORK:
2551                    EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
2552                    WifiNative.addToBlacklistCommand((String)message.obj);
2553                    break;
2554                case CMD_CLEAR_BLACKLIST:
2555                    WifiNative.clearBlacklistCommand();
2556                    break;
2557                case CMD_GET_NETWORK_CONFIG:
2558                    syncParams = (SyncParams) message.obj;
2559                    syncParams.mSyncReturn.configList = getConfiguredNetworksNative();
2560                    notifyOnMsgObject(message);
2561                    break;
2562                case CMD_SAVE_CONFIG:
2563                    if (message.arg2 == SYNCHRONOUS_CALL) {
2564                        syncParams = (SyncParams) message.obj;
2565                        syncParams.mSyncReturn.boolValue = WifiNative.saveConfigCommand();
2566                        notifyOnMsgObject(message);
2567                    } else {
2568                        /* asynchronous handling */
2569                        WifiNative.saveConfigCommand();
2570                    }
2571                    // Inform the backup manager about a data change
2572                    IBackupManager ibm = IBackupManager.Stub.asInterface(
2573                            ServiceManager.getService(Context.BACKUP_SERVICE));
2574                    if (ibm != null) {
2575                        try {
2576                            ibm.dataChanged("com.android.providers.settings");
2577                        } catch (Exception e) {
2578                            // Try again later
2579                        }
2580                    }
2581                    break;
2582                case CMD_CONNECTION_STATUS:
2583                    syncParams = (SyncParams) message.obj;
2584                    syncParams.mSyncReturn.stringValue = WifiNative.statusCommand();
2585                    notifyOnMsgObject(message);
2586                    break;
2587                case CMD_GET_MAC_ADDR:
2588                    syncParams = (SyncParams) message.obj;
2589                    syncParams.mSyncReturn.stringValue = WifiNative.getMacAddressCommand();
2590                    notifyOnMsgObject(message);
2591                    break;
2592                    /* Cannot start soft AP while in client mode */
2593                case CMD_START_AP:
2594                    Log.d(TAG, "Failed to start soft AP with a running supplicant");
2595                    EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
2596                    setWifiApState(WIFI_AP_STATE_FAILED);
2597                    break;
2598                case CMD_SET_SCAN_MODE:
2599                    mIsScanMode = (message.arg1 == SCAN_ONLY_MODE);
2600                    break;
2601                case CMD_SAVE_NETWORK:
2602                    config = (WifiConfiguration) message.obj;
2603                    int netId = addOrUpdateNetworkNative(config);
2604                    /* enable a new network */
2605                    if (config.networkId < 0) {
2606                        WifiNative.enableNetworkCommand(netId, false);
2607                    }
2608                    WifiNative.saveConfigCommand();
2609                    sendSupplicantConfigChangedBroadcast();
2610                    break;
2611                case CMD_FORGET_NETWORK:
2612                    WifiNative.removeNetworkCommand(message.arg1);
2613                    WifiNative.saveConfigCommand();
2614                    sendSupplicantConfigChangedBroadcast();
2615                    break;
2616                default:
2617                    return NOT_HANDLED;
2618            }
2619            return HANDLED;
2620        }
2621    }
2622
2623    class DriverStartingState extends HierarchicalState {
2624        @Override
2625        public void enter() {
2626            if (DBG) Log.d(TAG, getName() + "\n");
2627            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
2628        }
2629        @Override
2630        public boolean processMessage(Message message) {
2631            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
2632            switch(message.what) {
2633                case DRIVER_START_EVENT:
2634                    transitionTo(mDriverStartedState);
2635                    break;
2636                    /* Queue driver commands & connection events */
2637                case CMD_START_DRIVER:
2638                case CMD_STOP_DRIVER:
2639                case SUPPLICANT_STATE_CHANGE_EVENT:
2640                case NETWORK_CONNECTION_EVENT:
2641                case NETWORK_DISCONNECTION_EVENT:
2642                case PASSWORD_MAY_BE_INCORRECT_EVENT:
2643                case CMD_SET_SCAN_TYPE:
2644                case CMD_SET_POWER_MODE:
2645                case CMD_SET_BLUETOOTH_COEXISTENCE:
2646                case CMD_SET_BLUETOOTH_SCAN_MODE:
2647                case CMD_SET_NUM_ALLOWED_CHANNELS:
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            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
2660            return HANDLED;
2661        }
2662    }
2663
2664    class DriverStartedState extends HierarchicalState {
2665        @Override
2666        public void enter() {
2667            if (DBG) Log.d(TAG, getName() + "\n");
2668            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
2669
2670            try {
2671                mBatteryStats.noteWifiRunning();
2672            } catch (RemoteException ignore) {}
2673
2674            /* Initialize channel count */
2675            setNumAllowedChannels();
2676
2677            if (mIsScanMode) {
2678                WifiNative.setScanResultHandlingCommand(SCAN_ONLY_MODE);
2679                WifiNative.disconnectCommand();
2680                transitionTo(mScanModeState);
2681            } else {
2682                WifiNative.setScanResultHandlingCommand(CONNECT_MODE);
2683                /* If supplicant has already connected, before we could finish establishing
2684                 * the control channel connection, we miss all the supplicant events.
2685                 * Disconnect and reconnect when driver has started to ensure we receive
2686                 * all supplicant events.
2687                 *
2688                 * TODO: This is a bit unclean, ideally the supplicant should never
2689                 * connect until told to do so by the framework
2690                 */
2691                WifiNative.disconnectCommand();
2692                WifiNative.reconnectCommand();
2693                transitionTo(mConnectModeState);
2694            }
2695        }
2696        @Override
2697        public boolean processMessage(Message message) {
2698            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
2699            SyncParams syncParams;
2700            switch(message.what) {
2701                case CMD_SET_SCAN_TYPE:
2702                    if (message.arg1 == SCAN_ACTIVE) {
2703                        WifiNative.setScanModeCommand(true);
2704                    } else {
2705                        WifiNative.setScanModeCommand(false);
2706                    }
2707                    break;
2708                case CMD_SET_POWER_MODE:
2709                    WifiNative.setPowerModeCommand(message.arg1);
2710                    break;
2711                case CMD_SET_BLUETOOTH_COEXISTENCE:
2712                    WifiNative.setBluetoothCoexistenceModeCommand(message.arg1);
2713                    break;
2714                case CMD_SET_BLUETOOTH_SCAN_MODE:
2715                    WifiNative.setBluetoothCoexistenceScanModeCommand(message.arg1 == 1);
2716                    break;
2717                case CMD_SET_NUM_ALLOWED_CHANNELS:
2718                    mNumAllowedChannels = message.arg1;
2719                    WifiNative.setNumAllowedChannelsCommand(message.arg1);
2720                    break;
2721                case CMD_START_DRIVER:
2722                    /* Ignore another driver start */
2723                    break;
2724                case CMD_STOP_DRIVER:
2725                    WifiNative.stopDriverCommand();
2726                    transitionTo(mDriverStoppingState);
2727                    break;
2728                case CMD_REQUEST_CM_WAKELOCK:
2729                    if (mCm == null) {
2730                        mCm = (ConnectivityManager)mContext.getSystemService(
2731                                Context.CONNECTIVITY_SERVICE);
2732                    }
2733                    mCm.requestNetworkTransitionWakelock(TAG);
2734                    break;
2735                case CMD_START_PACKET_FILTERING:
2736                    WifiNative.startPacketFiltering();
2737                    break;
2738                case CMD_STOP_PACKET_FILTERING:
2739                    WifiNative.stopPacketFiltering();
2740                    break;
2741                default:
2742                    return NOT_HANDLED;
2743            }
2744            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
2745            return HANDLED;
2746        }
2747        @Override
2748        public void exit() {
2749            if (DBG) Log.d(TAG, getName() + "\n");
2750            try {
2751                mBatteryStats.noteWifiStopped();
2752            } catch (RemoteException ignore) { }
2753        }
2754    }
2755
2756    class DriverStoppingState extends HierarchicalState {
2757        @Override
2758        public void enter() {
2759            if (DBG) Log.d(TAG, getName() + "\n");
2760            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
2761        }
2762        @Override
2763        public boolean processMessage(Message message) {
2764            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
2765            switch(message.what) {
2766                case DRIVER_STOP_EVENT:
2767                    transitionTo(mDriverStoppedState);
2768                    break;
2769                    /* Queue driver commands */
2770                case CMD_START_DRIVER:
2771                case CMD_STOP_DRIVER:
2772                case CMD_SET_SCAN_TYPE:
2773                case CMD_SET_POWER_MODE:
2774                case CMD_SET_BLUETOOTH_COEXISTENCE:
2775                case CMD_SET_BLUETOOTH_SCAN_MODE:
2776                case CMD_SET_NUM_ALLOWED_CHANNELS:
2777                case CMD_START_PACKET_FILTERING:
2778                case CMD_STOP_PACKET_FILTERING:
2779                case CMD_START_SCAN:
2780                case CMD_DISCONNECT:
2781                case CMD_REASSOCIATE:
2782                case CMD_RECONNECT:
2783                    deferMessage(message);
2784                    break;
2785                default:
2786                    return NOT_HANDLED;
2787            }
2788            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
2789            return HANDLED;
2790        }
2791    }
2792
2793    class DriverStoppedState extends HierarchicalState {
2794        @Override
2795        public void enter() {
2796            if (DBG) Log.d(TAG, getName() + "\n");
2797            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
2798        }
2799        @Override
2800        public boolean processMessage(Message message) {
2801            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
2802            return NOT_HANDLED;
2803        }
2804    }
2805
2806    class ScanModeState extends HierarchicalState {
2807        @Override
2808        public void enter() {
2809            if (DBG) Log.d(TAG, getName() + "\n");
2810            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
2811        }
2812        @Override
2813        public boolean processMessage(Message message) {
2814            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
2815            SyncParams syncParams;
2816            switch(message.what) {
2817                case CMD_SET_SCAN_MODE:
2818                    if (message.arg1 == SCAN_ONLY_MODE) {
2819                        /* Ignore */
2820                        return HANDLED;
2821                    } else {
2822                        WifiNative.setScanResultHandlingCommand(message.arg1);
2823                        WifiNative.reconnectCommand();
2824                        mIsScanMode = false;
2825                        transitionTo(mDisconnectedState);
2826                    }
2827                    break;
2828                case CMD_START_SCAN:
2829                    WifiNative.scanCommand(message.arg1 == SCAN_ACTIVE);
2830                    break;
2831                    /* Ignore */
2832                case CMD_DISCONNECT:
2833                case CMD_RECONNECT:
2834                case CMD_REASSOCIATE:
2835                case SUPPLICANT_STATE_CHANGE_EVENT:
2836                case NETWORK_CONNECTION_EVENT:
2837                case NETWORK_DISCONNECTION_EVENT:
2838                    break;
2839                default:
2840                    return NOT_HANDLED;
2841            }
2842            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
2843            return HANDLED;
2844        }
2845    }
2846
2847    class ConnectModeState extends HierarchicalState {
2848        @Override
2849        public void enter() {
2850            if (DBG) Log.d(TAG, getName() + "\n");
2851            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
2852        }
2853        @Override
2854        public boolean processMessage(Message message) {
2855            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
2856            SyncParams syncParams;
2857            StateChangeResult stateChangeResult;
2858            switch(message.what) {
2859                case PASSWORD_MAY_BE_INCORRECT_EVENT:
2860                    mPasswordKeyMayBeIncorrect = true;
2861                    break;
2862                case SUPPLICANT_STATE_CHANGE_EVENT:
2863                    stateChangeResult = (StateChangeResult) message.obj;
2864                    mSupplicantStateTracker.handleEvent(stateChangeResult);
2865                    break;
2866                case CMD_START_SCAN:
2867                    /* We need to set scan type in completed state */
2868                    Message newMsg = obtainMessage();
2869                    newMsg.copyFrom(message);
2870                    mSupplicantStateTracker.sendMessage(newMsg);
2871                    break;
2872                    /* Do a redundant disconnect without transition */
2873                case CMD_DISCONNECT:
2874                    WifiNative.disconnectCommand();
2875                    break;
2876                case CMD_RECONNECT:
2877                    WifiNative.reconnectCommand();
2878                    break;
2879                case CMD_REASSOCIATE:
2880                    WifiNative.reassociateCommand();
2881                    break;
2882                case CMD_CONNECT_NETWORK:
2883                    int netId = message.arg1;
2884                    WifiConfiguration config = (WifiConfiguration) message.obj;
2885                    if (config != null) {
2886                        netId = addOrUpdateNetworkNative(config);
2887                    }
2888                    // Reset the priority of each network at start or if it goes too high.
2889                    if (mLastPriority == -1 || mLastPriority > 1000000) {
2890                        List<WifiConfiguration> configList = getConfiguredNetworksNative();
2891                        for (WifiConfiguration conf : configList) {
2892                            if (conf.networkId != -1) {
2893                                conf.priority = 0;
2894                                addOrUpdateNetworkNative(conf);
2895                            }
2896                        }
2897                        mLastPriority = 0;
2898                    }
2899
2900                    // Set to the highest priority and save the configuration.
2901                    config = new WifiConfiguration();
2902                    config.networkId = netId;
2903                    config.priority = ++mLastPriority;
2904
2905                    addOrUpdateNetworkNative(config);
2906                    WifiNative.saveConfigCommand();
2907
2908                    // Connect to network by disabling others.
2909                    WifiNative.enableNetworkCommand(netId, true);
2910                    WifiNative.reconnectCommand();
2911                    mEnableAllNetworks = true;
2912                    /* Dont send a supplicant config change broadcast here
2913                     * as it is better to not expose the temporary disabling
2914                     * of all networks
2915                     */
2916                    break;
2917                case SCAN_RESULTS_EVENT:
2918                    /* Set the scan setting back to "connect" mode */
2919                    WifiNative.setScanResultHandlingCommand(CONNECT_MODE);
2920                    /* Handle scan results */
2921                    return NOT_HANDLED;
2922                case NETWORK_CONNECTION_EVENT:
2923                    Log.d(TAG,"Network connection established");
2924                    stateChangeResult = (StateChangeResult) message.obj;
2925
2926                    mWifiInfo.setBSSID(mLastBssid = stateChangeResult.BSSID);
2927                    mWifiInfo.setNetworkId(stateChangeResult.networkId);
2928                    mLastNetworkId = stateChangeResult.networkId;
2929                    enableAllNetworks();
2930                    /* send event to CM & network change broadcast */
2931                    setDetailedState(DetailedState.OBTAINING_IPADDR);
2932                    sendNetworkStateChangeBroadcast(mLastBssid);
2933
2934                    transitionTo(mConnectingState);
2935                    break;
2936                case NETWORK_DISCONNECTION_EVENT:
2937                    Log.d(TAG,"Network connection lost");
2938                    enableAllNetworks();
2939                    handleNetworkDisconnect();
2940                    transitionTo(mDisconnectedState);
2941                    break;
2942                case CMD_GET_RSSI:
2943                    syncParams = (SyncParams) message.obj;
2944                    syncParams.mSyncReturn.intValue = WifiNative.getRssiCommand();
2945                    notifyOnMsgObject(message);
2946                    break;
2947                case CMD_GET_RSSI_APPROX:
2948                    syncParams = (SyncParams) message.obj;
2949                    syncParams.mSyncReturn.intValue = WifiNative.getRssiApproxCommand();
2950                    notifyOnMsgObject(message);
2951                    break;
2952                case CMD_GET_LINK_SPEED:
2953                    syncParams = (SyncParams) message.obj;
2954                    syncParams.mSyncReturn.intValue = WifiNative.getLinkSpeedCommand();
2955                    notifyOnMsgObject(message);
2956                    break;
2957                default:
2958                    return NOT_HANDLED;
2959            }
2960            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
2961            return HANDLED;
2962        }
2963    }
2964
2965    class ConnectingState extends HierarchicalState {
2966        boolean modifiedBluetoothCoexistenceMode;
2967        int powerMode;
2968        Thread mDhcpThread;
2969
2970        @Override
2971        public void enter() {
2972            if (DBG) Log.d(TAG, getName() + "\n");
2973            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
2974
2975            if (!mUseStaticIp) {
2976
2977                mDhcpThread = null;
2978                modifiedBluetoothCoexistenceMode = false;
2979                powerMode = DRIVER_POWER_MODE_AUTO;
2980
2981                if (shouldDisableCoexistenceMode()) {
2982                    /*
2983                     * There are problems setting the Wi-Fi driver's power
2984                     * mode to active when bluetooth coexistence mode is
2985                     * enabled or sense.
2986                     * <p>
2987                     * We set Wi-Fi to active mode when
2988                     * obtaining an IP address because we've found
2989                     * compatibility issues with some routers with low power
2990                     * mode.
2991                     * <p>
2992                     * In order for this active power mode to properly be set,
2993                     * we disable coexistence mode until we're done with
2994                     * obtaining an IP address.  One exception is if we
2995                     * are currently connected to a headset, since disabling
2996                     * coexistence would interrupt that connection.
2997                     */
2998                    modifiedBluetoothCoexistenceMode = true;
2999
3000                    // Disable the coexistence mode
3001                    WifiNative.setBluetoothCoexistenceModeCommand(
3002                            WifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED);
3003                }
3004
3005                powerMode =  WifiNative.getPowerModeCommand();
3006                if (powerMode < 0) {
3007                  // Handle the case where supplicant driver does not support
3008                  // getPowerModeCommand.
3009                    powerMode = DRIVER_POWER_MODE_AUTO;
3010                }
3011                if (powerMode != DRIVER_POWER_MODE_ACTIVE) {
3012                    WifiNative.setPowerModeCommand(DRIVER_POWER_MODE_ACTIVE);
3013                }
3014
3015                Log.d(TAG, "DHCP request started");
3016                mDhcpThread = new Thread(new Runnable() {
3017                    public void run() {
3018                        if (NetworkUtils.runDhcp(mInterfaceName, mDhcpInfo)) {
3019                            Log.d(TAG, "DHCP request succeeded");
3020                            sendMessage(CMD_IP_CONFIG_SUCCESS);
3021                        } else {
3022                            Log.d(TAG, "DHCP request failed: " +
3023                                    NetworkUtils.getDhcpError());
3024                            sendMessage(CMD_IP_CONFIG_FAILURE);
3025                        }
3026                    }
3027                });
3028                mDhcpThread.start();
3029            } else {
3030                if (NetworkUtils.configureInterface(mInterfaceName, mDhcpInfo)) {
3031                    Log.v(TAG, "Static IP configuration succeeded");
3032                    sendMessage(CMD_IP_CONFIG_SUCCESS);
3033                } else {
3034                    Log.v(TAG, "Static IP configuration failed");
3035                    sendMessage(CMD_IP_CONFIG_FAILURE);
3036                }
3037            }
3038         }
3039      @Override
3040      public boolean processMessage(Message message) {
3041          if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
3042
3043          switch(message.what) {
3044              case CMD_IP_CONFIG_SUCCESS:
3045                  mReconnectCount = 0;
3046                  mLastSignalLevel = -1; // force update of signal strength
3047                  mWifiInfo.setIpAddress(mDhcpInfo.ipAddress);
3048                  Log.d(TAG, "IP configuration: " + mDhcpInfo);
3049                  configureNetworkProperties();
3050                  setDetailedState(DetailedState.CONNECTED);
3051                  sendNetworkStateChangeBroadcast(mLastBssid);
3052                  //TODO: we could also detect an IP config change
3053                  // from a DHCP renewal and send out a config change
3054                  // broadcast
3055                  if (mConfigChanged) {
3056                      sendConfigChangeBroadcast();
3057                      mConfigChanged = false;
3058                  }
3059                  transitionTo(mConnectedState);
3060                  break;
3061              case CMD_IP_CONFIG_FAILURE:
3062                  mWifiInfo.setIpAddress(0);
3063
3064                  Log.e(TAG, "IP configuration failed");
3065                  /**
3066                   * If we've exceeded the maximum number of retries for DHCP
3067                   * to a given network, disable the network
3068                   */
3069                  if (++mReconnectCount > getMaxDhcpRetries()) {
3070                          Log.e(TAG, "Failed " +
3071                                  mReconnectCount + " times, Disabling " + mLastNetworkId);
3072                      WifiNative.disableNetworkCommand(mLastNetworkId);
3073                      sendSupplicantConfigChangedBroadcast();
3074                  }
3075
3076                  /* DHCP times out after about 30 seconds, we do a
3077                   * disconnect and an immediate reconnect to try again
3078                   */
3079                  WifiNative.disconnectCommand();
3080                  WifiNative.reconnectCommand();
3081                  transitionTo(mDisconnectingState);
3082                  break;
3083              case CMD_DISCONNECT:
3084                  WifiNative.disconnectCommand();
3085                  transitionTo(mDisconnectingState);
3086                  break;
3087                  /* Ignore */
3088              case NETWORK_CONNECTION_EVENT:
3089                  break;
3090              case CMD_STOP_DRIVER:
3091                  sendMessage(CMD_DISCONNECT);
3092                  deferMessage(message);
3093                  break;
3094              case CMD_SET_SCAN_MODE:
3095                  if (message.arg1 == SCAN_ONLY_MODE) {
3096                      sendMessage(CMD_DISCONNECT);
3097                      deferMessage(message);
3098                  }
3099                  break;
3100              case CMD_RECONFIGURE_IP:
3101                  deferMessage(message);
3102                  break;
3103              default:
3104                return NOT_HANDLED;
3105          }
3106          EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
3107          return HANDLED;
3108      }
3109
3110      @Override
3111      public void exit() {
3112          /* reset power state & bluetooth coexistence if on DHCP */
3113          if (!mUseStaticIp) {
3114              if (powerMode != DRIVER_POWER_MODE_ACTIVE) {
3115                  WifiNative.setPowerModeCommand(powerMode);
3116              }
3117
3118              if (modifiedBluetoothCoexistenceMode) {
3119                  // Set the coexistence mode back to its default value
3120                  WifiNative.setBluetoothCoexistenceModeCommand(
3121                          WifiNative.BLUETOOTH_COEXISTENCE_MODE_SENSE);
3122              }
3123          }
3124
3125      }
3126    }
3127
3128    class ConnectedState extends HierarchicalState {
3129        @Override
3130        public void enter() {
3131            if (DBG) Log.d(TAG, getName() + "\n");
3132            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
3133        }
3134        @Override
3135        public boolean processMessage(Message message) {
3136            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
3137            switch (message.what) {
3138                case CMD_DISCONNECT:
3139                    WifiNative.disconnectCommand();
3140                    transitionTo(mDisconnectingState);
3141                    break;
3142                case CMD_RECONFIGURE_IP:
3143                    Log.d(TAG,"Reconfiguring IP on connection");
3144                    NetworkUtils.resetConnections(mInterfaceName);
3145                    transitionTo(mConnectingState);
3146                    break;
3147                case CMD_STOP_DRIVER:
3148                    sendMessage(CMD_DISCONNECT);
3149                    deferMessage(message);
3150                    break;
3151                case CMD_SET_SCAN_MODE:
3152                    if (message.arg1 == SCAN_ONLY_MODE) {
3153                        sendMessage(CMD_DISCONNECT);
3154                        deferMessage(message);
3155                    }
3156                    break;
3157                    /* Ignore */
3158                case NETWORK_CONNECTION_EVENT:
3159                    break;
3160                default:
3161                    return NOT_HANDLED;
3162            }
3163            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
3164            return HANDLED;
3165        }
3166    }
3167
3168    class DisconnectingState extends HierarchicalState {
3169        @Override
3170        public void enter() {
3171            if (DBG) Log.d(TAG, getName() + "\n");
3172            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
3173        }
3174        @Override
3175        public boolean processMessage(Message message) {
3176            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
3177            switch (message.what) {
3178                case CMD_STOP_DRIVER: /* Stop driver only after disconnect handled */
3179                    deferMessage(message);
3180                    break;
3181                case CMD_SET_SCAN_MODE:
3182                    if (message.arg1 == SCAN_ONLY_MODE) {
3183                        deferMessage(message);
3184                    }
3185                    break;
3186                default:
3187                    return NOT_HANDLED;
3188            }
3189            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
3190            return HANDLED;
3191        }
3192    }
3193
3194    class DisconnectedState extends HierarchicalState {
3195        @Override
3196        public void enter() {
3197            if (DBG) Log.d(TAG, getName() + "\n");
3198            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
3199        }
3200        @Override
3201        public boolean processMessage(Message message) {
3202            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
3203            switch (message.what) {
3204                case CMD_SET_SCAN_MODE:
3205                    if (message.arg1 == SCAN_ONLY_MODE) {
3206                        WifiNative.setScanResultHandlingCommand(message.arg1);
3207                        //Supplicant disconnect to prevent further connects
3208                        WifiNative.disconnectCommand();
3209                        mIsScanMode = true;
3210                        transitionTo(mScanModeState);
3211                    }
3212                    break;
3213                    /* Ignore network disconnect */
3214                case NETWORK_DISCONNECTION_EVENT:
3215                    break;
3216                default:
3217                    return NOT_HANDLED;
3218            }
3219            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
3220            return HANDLED;
3221        }
3222    }
3223
3224    class SoftApStartedState extends HierarchicalState {
3225        @Override
3226        public void enter() {
3227            if (DBG) Log.d(TAG, getName() + "\n");
3228            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
3229        }
3230        @Override
3231        public boolean processMessage(Message message) {
3232            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
3233            switch(message.what) {
3234                case CMD_STOP_AP:
3235                    Log.d(TAG,"Stopping Soft AP");
3236                    setWifiApState(WIFI_AP_STATE_DISABLING);
3237                    try {
3238                        nwService.stopAccessPoint();
3239                    } catch(Exception e) {
3240                        Log.e(TAG, "Exception in stopAccessPoint()");
3241                    }
3242                    transitionTo(mDriverLoadedState);
3243                    break;
3244                case CMD_START_AP:
3245                    Log.d(TAG,"SoftAP set on a running access point");
3246                    try {
3247                        nwService.setAccessPoint((WifiConfiguration) message.obj,
3248                                    mInterfaceName,
3249                                    SOFTAP_IFACE);
3250                    } catch(Exception e) {
3251                        Log.e(TAG, "Exception in nwService during soft AP set");
3252                        try {
3253                            nwService.stopAccessPoint();
3254                        } catch (Exception ee) {
3255                            Slog.e(TAG, "Could not stop AP, :" + ee);
3256                        }
3257                        sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_AP_STATE_FAILED, 0));
3258                    }
3259                    break;
3260                /* Fail client mode operation when soft AP is enabled */
3261                case CMD_START_SUPPLICANT:
3262                    Log.e(TAG,"Cannot start supplicant with a running soft AP");
3263                    setWifiState(WIFI_STATE_UNKNOWN);
3264                    break;
3265                default:
3266                    return NOT_HANDLED;
3267            }
3268            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
3269            return HANDLED;
3270        }
3271    }
3272
3273
3274    class SupplicantStateTracker extends HierarchicalStateMachine {
3275
3276        private int mRssiPollToken = 0;
3277
3278        /**
3279         * The max number of the WPA supplicant loop iterations before we
3280         * decide that the loop should be terminated:
3281         */
3282        private static final int MAX_SUPPLICANT_LOOP_ITERATIONS = 4;
3283        private int mLoopDetectIndex = 0;
3284        private int mLoopDetectCount = 0;
3285
3286        /**
3287         *  Supplicant state change commands follow
3288         *  the ordinal values defined in SupplicantState.java
3289         */
3290        private static final int DISCONNECTED           = 0;
3291        private static final int INACTIVE               = 1;
3292        private static final int SCANNING               = 2;
3293        private static final int ASSOCIATING            = 3;
3294        private static final int ASSOCIATED             = 4;
3295        private static final int FOUR_WAY_HANDSHAKE     = 5;
3296        private static final int GROUP_HANDSHAKE        = 6;
3297        private static final int COMPLETED              = 7;
3298        private static final int DORMANT                = 8;
3299        private static final int UNINITIALIZED          = 9;
3300        private static final int INVALID                = 10;
3301
3302        private HierarchicalState mUninitializedState = new UninitializedState();
3303        private HierarchicalState mInitializedState = new InitializedState();;
3304        private HierarchicalState mInactiveState = new InactiveState();
3305        private HierarchicalState mDisconnectState = new DisconnectedState();
3306        private HierarchicalState mScanState = new ScanState();
3307        private HierarchicalState mConnectState = new ConnectState();
3308        private HierarchicalState mHandshakeState = new HandshakeState();
3309        private HierarchicalState mCompletedState = new CompletedState();
3310        private HierarchicalState mDormantState = new DormantState();
3311
3312        public SupplicantStateTracker(Context context, Handler target) {
3313            super(TAG, target.getLooper());
3314
3315            addState(mUninitializedState);
3316            addState(mInitializedState);
3317                addState(mInactiveState, mInitializedState);
3318                addState(mDisconnectState, mInitializedState);
3319                addState(mScanState, mInitializedState);
3320                addState(mConnectState, mInitializedState);
3321                    addState(mHandshakeState, mConnectState);
3322                    addState(mCompletedState, mConnectState);
3323                addState(mDormantState, mInitializedState);
3324
3325            setInitialState(mUninitializedState);
3326
3327            //start the state machine
3328            start();
3329        }
3330
3331        public void handleEvent(StateChangeResult stateChangeResult) {
3332            SupplicantState newState = (SupplicantState) stateChangeResult.state;
3333
3334            // Supplicant state change
3335            // [31-13] Reserved for future use
3336            // [8 - 0] Supplicant state (as defined in SupplicantState.java)
3337            // 50023 supplicant_state_changed (custom|1|5)
3338            EventLog.writeEvent(EVENTLOG_SUPPLICANT_STATE_CHANGED, newState.ordinal());
3339
3340            sendMessage(obtainMessage(newState.ordinal(), stateChangeResult));
3341        }
3342
3343        public void resetSupplicantState() {
3344            transitionTo(mUninitializedState);
3345        }
3346
3347        private void resetLoopDetection() {
3348            mLoopDetectCount = 0;
3349            mLoopDetectIndex = 0;
3350        }
3351
3352        private boolean handleTransition(Message msg) {
3353            if (DBG) Log.d(TAG, getName() + msg.toString() + "\n");
3354            switch (msg.what) {
3355                case DISCONNECTED:
3356                    transitionTo(mDisconnectState);
3357                    break;
3358                case SCANNING:
3359                    transitionTo(mScanState);
3360                    break;
3361                case ASSOCIATING:
3362                    StateChangeResult stateChangeResult = (StateChangeResult) msg.obj;
3363                    /* BSSID is valid only in ASSOCIATING state */
3364                    mWifiInfo.setBSSID(stateChangeResult.BSSID);
3365                    //$FALL-THROUGH$
3366                case ASSOCIATED:
3367                case FOUR_WAY_HANDSHAKE:
3368                case GROUP_HANDSHAKE:
3369                    transitionTo(mHandshakeState);
3370                    break;
3371                case COMPLETED:
3372                    transitionTo(mCompletedState);
3373                    break;
3374                case DORMANT:
3375                    transitionTo(mDormantState);
3376                    break;
3377                case INACTIVE:
3378                    transitionTo(mInactiveState);
3379                    break;
3380                case UNINITIALIZED:
3381                case INVALID:
3382                    transitionTo(mUninitializedState);
3383                    break;
3384                default:
3385                    return NOT_HANDLED;
3386            }
3387            StateChangeResult stateChangeResult = (StateChangeResult) msg.obj;
3388            SupplicantState supState = (SupplicantState) stateChangeResult.state;
3389            setDetailedState(WifiInfo.getDetailedStateOf(supState));
3390            mWifiInfo.setSupplicantState(supState);
3391            mWifiInfo.setNetworkId(stateChangeResult.networkId);
3392            //TODO: Modify WifiMonitor to report SSID on events
3393            //mWifiInfo.setSSID()
3394            return HANDLED;
3395        }
3396
3397        /********************************************************
3398         * HSM states
3399         *******************************************************/
3400
3401        class InitializedState extends HierarchicalState {
3402            @Override
3403             public void enter() {
3404                 if (DBG) Log.d(TAG, getName() + "\n");
3405             }
3406            @Override
3407            public boolean processMessage(Message message) {
3408                if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
3409                switch (message.what) {
3410                    case CMD_START_SCAN:
3411                        WifiNative.scanCommand(message.arg1 == SCAN_ACTIVE);
3412                        break;
3413                    default:
3414                        if (DBG) Log.w(TAG, "Ignoring " + message);
3415                        break;
3416                }
3417                return HANDLED;
3418            }
3419        }
3420
3421        class UninitializedState extends HierarchicalState {
3422            @Override
3423             public void enter() {
3424                 if (DBG) Log.d(TAG, getName() + "\n");
3425                 mNetworkInfo.setIsAvailable(false);
3426                 resetLoopDetection();
3427                 mPasswordKeyMayBeIncorrect = false;
3428             }
3429            @Override
3430            public boolean processMessage(Message message) {
3431                switch(message.what) {
3432                    default:
3433                        if (!handleTransition(message)) {
3434                            if (DBG) Log.w(TAG, "Ignoring " + message);
3435                        }
3436                        break;
3437                }
3438                return HANDLED;
3439            }
3440            @Override
3441            public void exit() {
3442                mNetworkInfo.setIsAvailable(true);
3443            }
3444        }
3445
3446        class InactiveState extends HierarchicalState {
3447            @Override
3448             public void enter() {
3449                 if (DBG) Log.d(TAG, getName() + "\n");
3450                 Message message = getCurrentMessage();
3451                 StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
3452
3453                 mNetworkInfo.setIsAvailable(false);
3454                 resetLoopDetection();
3455                 mPasswordKeyMayBeIncorrect = false;
3456
3457                 sendSupplicantStateChangedBroadcast(stateChangeResult, false);
3458             }
3459            @Override
3460            public boolean processMessage(Message message) {
3461                return handleTransition(message);
3462            }
3463            @Override
3464            public void exit() {
3465                mNetworkInfo.setIsAvailable(true);
3466            }
3467        }
3468
3469
3470        class DisconnectedState extends HierarchicalState {
3471            @Override
3472             public void enter() {
3473                 if (DBG) Log.d(TAG, getName() + "\n");
3474                 Message message = getCurrentMessage();
3475                 StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
3476
3477                 resetLoopDetection();
3478
3479                 /* If a disconnect event happens after a password key failure
3480                  * event, disable the network
3481                  */
3482                 if (mPasswordKeyMayBeIncorrect) {
3483                     Log.d(TAG, "Failed to authenticate, disabling network " +
3484                             mWifiInfo.getNetworkId());
3485                     WifiNative.disableNetworkCommand(mWifiInfo.getNetworkId());
3486                     mPasswordKeyMayBeIncorrect = false;
3487                     sendSupplicantStateChangedBroadcast(stateChangeResult, true);
3488                     sendSupplicantConfigChangedBroadcast();
3489                 }
3490                 else {
3491                     sendSupplicantStateChangedBroadcast(stateChangeResult, false);
3492                 }
3493             }
3494            @Override
3495            public boolean processMessage(Message message) {
3496                return handleTransition(message);
3497            }
3498        }
3499
3500        class ScanState extends HierarchicalState {
3501            @Override
3502             public void enter() {
3503                 if (DBG) Log.d(TAG, getName() + "\n");
3504                 Message message = getCurrentMessage();
3505                 StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
3506
3507                 mPasswordKeyMayBeIncorrect = false;
3508                 resetLoopDetection();
3509                 sendSupplicantStateChangedBroadcast(stateChangeResult, false);
3510             }
3511            @Override
3512            public boolean processMessage(Message message) {
3513                return handleTransition(message);
3514            }
3515        }
3516
3517        class ConnectState extends HierarchicalState {
3518            @Override
3519             public void enter() {
3520                 if (DBG) Log.d(TAG, getName() + "\n");
3521             }
3522            @Override
3523            public boolean processMessage(Message message) {
3524                switch (message.what) {
3525                    case CMD_START_SCAN:
3526                        WifiNative.setScanResultHandlingCommand(SCAN_ONLY_MODE);
3527                        WifiNative.scanCommand(message.arg1 == SCAN_ACTIVE);
3528                        break;
3529                    default:
3530                        return NOT_HANDLED;
3531                }
3532                return HANDLED;
3533            }
3534        }
3535
3536        class HandshakeState extends HierarchicalState {
3537            @Override
3538             public void enter() {
3539                 if (DBG) Log.d(TAG, getName() + "\n");
3540                 final Message message = getCurrentMessage();
3541                 StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
3542
3543                 if (mLoopDetectIndex > message.what) {
3544                     mLoopDetectCount++;
3545                 }
3546                 if (mLoopDetectCount > MAX_SUPPLICANT_LOOP_ITERATIONS) {
3547                     WifiNative.disableNetworkCommand(stateChangeResult.networkId);
3548                     sendSupplicantConfigChangedBroadcast();
3549                     mLoopDetectCount = 0;
3550                 }
3551
3552                 mLoopDetectIndex = message.what;
3553
3554                 mPasswordKeyMayBeIncorrect = false;
3555                 sendSupplicantStateChangedBroadcast(stateChangeResult, false);
3556             }
3557            @Override
3558            public boolean processMessage(Message message) {
3559                return handleTransition(message);
3560            }
3561        }
3562
3563        class CompletedState extends HierarchicalState {
3564            @Override
3565             public void enter() {
3566                 if (DBG) Log.d(TAG, getName() + "\n");
3567                 Message message = getCurrentMessage();
3568                 StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
3569
3570                 mRssiPollToken++;
3571                 if (mEnableRssiPolling) {
3572                     sendMessageDelayed(obtainMessage(CMD_RSSI_POLL, mRssiPollToken, 0),
3573                             POLL_RSSI_INTERVAL_MSECS);
3574                 }
3575
3576                 resetLoopDetection();
3577
3578                 mPasswordKeyMayBeIncorrect = false;
3579                 sendSupplicantStateChangedBroadcast(stateChangeResult, false);
3580             }
3581            @Override
3582            public boolean processMessage(Message message) {
3583                switch(message.what) {
3584                    case ASSOCIATING:
3585                    case ASSOCIATED:
3586                    case FOUR_WAY_HANDSHAKE:
3587                    case GROUP_HANDSHAKE:
3588                    case COMPLETED:
3589                        break;
3590                    case CMD_RSSI_POLL:
3591                        if (message.arg1 == mRssiPollToken) {
3592                            // Get Info and continue polling
3593                            requestPolledInfo();
3594                            sendMessageDelayed(obtainMessage(CMD_RSSI_POLL, mRssiPollToken, 0),
3595                                    POLL_RSSI_INTERVAL_MSECS);
3596                        } else {
3597                            // Polling has completed
3598                        }
3599                        break;
3600                    case CMD_ENABLE_RSSI_POLL:
3601                        mRssiPollToken++;
3602                        if (mEnableRssiPolling) {
3603                            // first poll
3604                            requestPolledInfo();
3605                            sendMessageDelayed(obtainMessage(CMD_RSSI_POLL, mRssiPollToken, 0),
3606                                    POLL_RSSI_INTERVAL_MSECS);
3607                        }
3608                        break;
3609                    default:
3610                        return handleTransition(message);
3611                }
3612                return HANDLED;
3613            }
3614        }
3615
3616        class DormantState extends HierarchicalState {
3617            @Override
3618            public void enter() {
3619                if (DBG) Log.d(TAG, getName() + "\n");
3620                Message message = getCurrentMessage();
3621                StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
3622
3623                resetLoopDetection();
3624                mPasswordKeyMayBeIncorrect = false;
3625
3626                sendSupplicantStateChangedBroadcast(stateChangeResult, false);
3627
3628                /* TODO: reconnect is now being handled at DHCP failure handling
3629                 * If we run into issues with staying in Dormant state, might
3630                 * need a reconnect here
3631                 */
3632            }
3633            @Override
3634            public boolean processMessage(Message message) {
3635                return handleTransition(message);
3636            }
3637        }
3638    }
3639}
3640