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