WifiStateMachine.java revision e4c56c9655bf936454e2f3ee434aacb403876c7d
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.AlarmManager;
41import android.app.PendingIntent;
42import android.net.LinkAddress;
43import android.net.NetworkInfo;
44import android.net.DhcpInfo;
45import android.net.NetworkUtils;
46import android.net.ConnectivityManager;
47import android.net.NetworkInfo.DetailedState;
48import android.net.LinkProperties;
49import android.net.wifi.NetworkUpdateResult;
50import android.net.wifi.WpsResult.Status;
51import android.os.Binder;
52import android.os.Message;
53import android.os.IBinder;
54import android.os.INetworkManagementService;
55import android.os.PowerManager;
56import android.os.SystemProperties;
57import android.os.RemoteException;
58import android.os.ServiceManager;
59import android.os.Process;
60import android.os.WorkSource;
61import android.provider.Settings;
62import android.util.EventLog;
63import android.util.Log;
64import android.util.Slog;
65import android.app.backup.IBackupManager;
66import android.bluetooth.BluetoothAdapter;
67import android.content.BroadcastReceiver;
68import android.content.Intent;
69import android.content.Context;
70import android.content.IntentFilter;
71
72import com.android.internal.app.IBatteryStats;
73import com.android.internal.util.AsyncChannel;
74import com.android.internal.util.HierarchicalState;
75import com.android.internal.util.HierarchicalStateMachine;
76
77
78import java.util.ArrayList;
79import java.util.LinkedHashMap;
80import java.util.List;
81import java.util.Map;
82import java.util.concurrent.atomic.AtomicInteger;
83import java.util.regex.Pattern;
84
85/**
86 * Track the state of Wifi connectivity. All event handling is done here,
87 * and all changes in connectivity state are initiated here.
88 *
89 * @hide
90 */
91public class WifiStateMachine extends HierarchicalStateMachine {
92
93    private static final String TAG = "WifiStateMachine";
94    private static final String NETWORKTYPE = "WIFI";
95    private static final boolean DBG = false;
96
97    /* TODO: fetch a configurable interface */
98    private static final String SOFTAP_IFACE = "wl0.1";
99
100    private WifiMonitor mWifiMonitor;
101    private INetworkManagementService nwService;
102    private ConnectivityManager mCm;
103
104    /* Scan results handling */
105    private List<ScanResult> mScanResults;
106    private static final Pattern scanResultPattern = Pattern.compile("\t+");
107    private static final int SCAN_RESULT_CACHE_SIZE = 80;
108    private final LinkedHashMap<String, ScanResult> mScanResultCache;
109
110    private String mInterfaceName;
111
112    private int mLastSignalLevel = -1;
113    private String mLastBssid;
114    private int mLastNetworkId;
115    private boolean mEnableRssiPolling = false;
116    private int mRssiPollToken = 0;
117    private int mReconnectCount = 0;
118    private boolean mIsScanMode = false;
119
120    private boolean mBluetoothConnectionActive = false;
121
122    /**
123     * Interval in milliseconds between polling for RSSI
124     * and linkspeed information
125     */
126    private static final int POLL_RSSI_INTERVAL_MSECS = 3000;
127
128    /**
129     * Delay between supplicant restarts upon failure to establish connection
130     */
131    private static final int SUPPLICANT_RESTART_INTERVAL_MSECS = 5000;
132
133    /**
134     * Number of times we attempt to restart supplicant
135     */
136    private static final int SUPPLICANT_RESTART_TRIES = 5;
137
138    private int mSupplicantRestartCount = 0;
139
140    private LinkProperties mLinkProperties;
141
142    // Wakelock held during wifi start/stop and driver load/unload
143    private PowerManager.WakeLock mWakeLock;
144
145    private Context mContext;
146
147    private DhcpInfo mDhcpInfo;
148    private WifiInfo mWifiInfo;
149    private NetworkInfo mNetworkInfo;
150    private SupplicantStateTracker mSupplicantStateTracker;
151    private WpsStateMachine mWpsStateMachine;
152
153    private AlarmManager mAlarmManager;
154    private PendingIntent mScanIntent;
155    /* Tracks current frequency mode */
156    private AtomicInteger mFrequencyBand = new AtomicInteger(WifiManager.WIFI_FREQUENCY_BAND_AUTO);
157
158    // Channel for sending replies.
159    private AsyncChannel mReplyChannel = new AsyncChannel();
160
161    // Event log tags (must be in sync with event-log-tags)
162    private static final int EVENTLOG_WIFI_STATE_CHANGED        = 50021;
163    private static final int EVENTLOG_WIFI_EVENT_HANDLED        = 50022;
164    private static final int EVENTLOG_SUPPLICANT_STATE_CHANGED  = 50023;
165
166    /* Load the driver */
167    static final int CMD_LOAD_DRIVER                      = 1;
168    /* Unload the driver */
169    static final int CMD_UNLOAD_DRIVER                    = 2;
170    /* Indicates driver load succeeded */
171    static final int CMD_LOAD_DRIVER_SUCCESS              = 3;
172    /* Indicates driver load failed */
173    static final int CMD_LOAD_DRIVER_FAILURE              = 4;
174    /* Indicates driver unload succeeded */
175    static final int CMD_UNLOAD_DRIVER_SUCCESS            = 5;
176    /* Indicates driver unload failed */
177    static final int CMD_UNLOAD_DRIVER_FAILURE            = 6;
178
179    /* Start the supplicant */
180    static final int CMD_START_SUPPLICANT                 = 11;
181    /* Stop the supplicant */
182    static final int CMD_STOP_SUPPLICANT                  = 12;
183    /* Start the driver */
184    static final int CMD_START_DRIVER                     = 13;
185    /* Start the driver */
186    static final int CMD_STOP_DRIVER                      = 14;
187    /* Indicates DHCP succeded */
188    static final int CMD_IP_CONFIG_SUCCESS                = 15;
189    /* Indicates DHCP failed */
190    static final int CMD_IP_CONFIG_FAILURE                = 16;
191
192    /* Start the soft access point */
193    static final int CMD_START_AP                         = 21;
194    /* Stop the soft access point */
195    static final int CMD_STOP_AP                          = 22;
196
197    static final int CMD_BLUETOOTH_ADAPTER_STATE_CHANGE   = 23;
198
199    /* Supplicant events */
200    /* Connection to supplicant established */
201    static final int SUP_CONNECTION_EVENT                 = 31;
202    /* Connection to supplicant lost */
203    static final int SUP_DISCONNECTION_EVENT              = 32;
204    /* Driver start completed */
205    static final int DRIVER_START_EVENT                   = 33;
206    /* Driver stop completed */
207    static final int DRIVER_STOP_EVENT                    = 34;
208    /* Network connection completed */
209    static final int NETWORK_CONNECTION_EVENT             = 36;
210    /* Network disconnection completed */
211    static final int NETWORK_DISCONNECTION_EVENT          = 37;
212    /* Scan results are available */
213    static final int SCAN_RESULTS_EVENT                   = 38;
214    /* Supplicate state changed */
215    static final int SUPPLICANT_STATE_CHANGE_EVENT        = 39;
216    /* Password may be incorrect */
217    static final int PASSWORD_MAY_BE_INCORRECT_EVENT      = 40;
218
219    /* Supplicant commands */
220    /* Is supplicant alive ? */
221    static final int CMD_PING_SUPPLICANT                  = 51;
222    /* Add/update a network configuration */
223    static final int CMD_ADD_OR_UPDATE_NETWORK            = 52;
224    /* Delete a network */
225    static final int CMD_REMOVE_NETWORK                   = 53;
226    /* Enable a network. The device will attempt a connection to the given network. */
227    static final int CMD_ENABLE_NETWORK                   = 54;
228    /* Enable all networks */
229    static final int CMD_ENABLE_ALL_NETWORKS              = 55;
230    /* Disable a network. The device does not attempt a connection to the given network. */
231    static final int CMD_DISABLE_NETWORK                  = 56;
232    /* Blacklist network. De-prioritizes the given BSSID for connection. */
233    static final int CMD_BLACKLIST_NETWORK                = 57;
234    /* Clear the blacklist network list */
235    static final int CMD_CLEAR_BLACKLIST                  = 58;
236    /* Save configuration */
237    static final int CMD_SAVE_CONFIG                      = 59;
238
239    /* Supplicant commands after driver start*/
240    /* Initiate a scan */
241    static final int CMD_START_SCAN                       = 71;
242    /* Set scan mode. CONNECT_MODE or SCAN_ONLY_MODE */
243    static final int CMD_SET_SCAN_MODE                    = 72;
244    /* Set scan type. SCAN_ACTIVE or SCAN_PASSIVE */
245    static final int CMD_SET_SCAN_TYPE                    = 73;
246    /* Disconnect from a network */
247    static final int CMD_DISCONNECT                       = 74;
248    /* Reconnect to a network */
249    static final int CMD_RECONNECT                        = 75;
250    /* Reassociate to a network */
251    static final int CMD_REASSOCIATE                      = 76;
252    /* Controls power mode and suspend mode optimizations
253     *
254     * When high perf mode is enabled, power mode is set to
255     * POWER_MODE_ACTIVE and suspend mode optimizations are disabled
256     *
257     * When high perf mode is disabled, power mode is set to
258     * POWER_MODE_AUTO and suspend mode optimizations are enabled
259     *
260     * Suspend mode optimizations include:
261     * - packet filtering
262     * - turn off roaming
263     * - DTIM wake up settings
264     */
265    static final int CMD_SET_HIGH_PERF_MODE               = 77;
266    /* Set the country code */
267    static final int CMD_SET_COUNTRY_CODE                 = 80;
268    /* Request connectivity manager wake lock before driver stop */
269    static final int CMD_REQUEST_CM_WAKELOCK              = 81;
270    /* Enables RSSI poll */
271    static final int CMD_ENABLE_RSSI_POLL                 = 82;
272    /* RSSI poll */
273    static final int CMD_RSSI_POLL                        = 83;
274    /* Set up packet filtering */
275    static final int CMD_START_PACKET_FILTERING           = 84;
276    /* Clear packet filter */
277    static final int CMD_STOP_PACKET_FILTERING            = 85;
278    /* Connect to a specified network (network id
279     * or WifiConfiguration) This involves increasing
280     * the priority of the network, enabling the network
281     * (while disabling others) and issuing a reconnect.
282     * Note that CMD_RECONNECT just does a reconnect to
283     * an existing network. All the networks get enabled
284     * upon a successful connection or a failure.
285     */
286    static final int CMD_CONNECT_NETWORK                  = 86;
287    /* Save the specified network. This involves adding
288     * an enabled network (if new) and updating the
289     * config and issuing a save on supplicant config.
290     */
291    static final int CMD_SAVE_NETWORK                     = 87;
292    /* Delete the specified network. This involves
293     * removing the network and issuing a save on
294     * supplicant config.
295     */
296    static final int CMD_FORGET_NETWORK                   = 88;
297    /* Start Wi-Fi protected setup */
298    static final int CMD_START_WPS                        = 89;
299    /* Set the frequency band */
300    static final int CMD_SET_FREQUENCY_BAND               = 90;
301
302    /* Commands from/to the SupplicantStateTracker */
303    /* Reset the supplicant state tracker */
304    static final int CMD_RESET_SUPPLICANT_STATE           = 111;
305
306    /* Commands/events reported by WpsStateMachine */
307    /* Indicates the completion of WPS activity */
308    static final int WPS_COMPLETED_EVENT                  = 121;
309    /* Reset the WPS state machine */
310    static final int CMD_RESET_WPS_STATE                  = 122;
311
312    private static final int CONNECT_MODE   = 1;
313    private static final int SCAN_ONLY_MODE = 2;
314
315    private static final int SCAN_ACTIVE = 1;
316    private static final int SCAN_PASSIVE = 2;
317
318    private static final int SUCCESS = 1;
319    private static final int FAILURE = -1;
320
321    /**
322     * The maximum number of times we will retry a connection to an access point
323     * for which we have failed in acquiring an IP address from DHCP. A value of
324     * N means that we will make N+1 connection attempts in all.
325     * <p>
326     * See {@link Settings.Secure#WIFI_MAX_DHCP_RETRY_COUNT}. This is the default
327     * value if a Settings value is not present.
328     */
329    private static final int DEFAULT_MAX_DHCP_RETRIES = 9;
330
331    private static final int POWER_MODE_ACTIVE = 1;
332    private static final int POWER_MODE_AUTO = 0;
333
334    /**
335     * See {@link Settings.Secure#WIFI_SCAN_INTERVAL_MS}. This is the default value if a
336     * Settings.Secure value is not present.
337     */
338    private static final long DEFAULT_SCAN_INTERVAL_MS = 60 * 1000; /* 1 minute */
339
340    /* Default parent state */
341    private HierarchicalState mDefaultState = new DefaultState();
342    /* Temporary initial state */
343    private HierarchicalState mInitialState = new InitialState();
344    /* Unloading the driver */
345    private HierarchicalState mDriverUnloadingState = new DriverUnloadingState();
346    /* Loading the driver */
347    private HierarchicalState mDriverUnloadedState = new DriverUnloadedState();
348    /* Driver load/unload failed */
349    private HierarchicalState mDriverFailedState = new DriverFailedState();
350    /* Driver loading */
351    private HierarchicalState mDriverLoadingState = new DriverLoadingState();
352    /* Driver loaded */
353    private HierarchicalState mDriverLoadedState = new DriverLoadedState();
354    /* Driver loaded, waiting for supplicant to start */
355    private HierarchicalState mSupplicantStartingState = new SupplicantStartingState();
356    /* Driver loaded and supplicant ready */
357    private HierarchicalState mSupplicantStartedState = new SupplicantStartedState();
358    /* Waiting for supplicant to stop and monitor to exit */
359    private HierarchicalState mSupplicantStoppingState = new SupplicantStoppingState();
360    /* Driver start issued, waiting for completed event */
361    private HierarchicalState mDriverStartingState = new DriverStartingState();
362    /* Driver started */
363    private HierarchicalState mDriverStartedState = new DriverStartedState();
364    /* Driver stopping */
365    private HierarchicalState mDriverStoppingState = new DriverStoppingState();
366    /* Driver stopped */
367    private HierarchicalState mDriverStoppedState = new DriverStoppedState();
368    /* Scan for networks, no connection will be established */
369    private HierarchicalState mScanModeState = new ScanModeState();
370    /* Connecting to an access point */
371    private HierarchicalState mConnectModeState = new ConnectModeState();
372    /* Fetching IP after network connection (assoc+auth complete) */
373    private HierarchicalState mConnectingState = new ConnectingState();
374    /* Connected with IP addr */
375    private HierarchicalState mConnectedState = new ConnectedState();
376    /* disconnect issued, waiting for network disconnect confirmation */
377    private HierarchicalState mDisconnectingState = new DisconnectingState();
378    /* Network is not connected, supplicant assoc+auth is not complete */
379    private HierarchicalState mDisconnectedState = new DisconnectedState();
380    /* Waiting for WPS to be completed*/
381    private HierarchicalState mWaitForWpsCompletionState = new WaitForWpsCompletionState();
382
383    /* Soft Ap is running */
384    private HierarchicalState mSoftApStartedState = new SoftApStartedState();
385
386
387    /**
388     * One of  {@link WifiManager#WIFI_STATE_DISABLED},
389     *         {@link WifiManager#WIFI_STATE_DISABLING},
390     *         {@link WifiManager#WIFI_STATE_ENABLED},
391     *         {@link WifiManager#WIFI_STATE_ENABLING},
392     *         {@link WifiManager#WIFI_STATE_UNKNOWN}
393     *
394     */
395    private final AtomicInteger mWifiState = new AtomicInteger(WIFI_STATE_DISABLED);
396
397    /**
398     * One of  {@link WifiManager#WIFI_AP_STATE_DISABLED},
399     *         {@link WifiManager#WIFI_AP_STATE_DISABLING},
400     *         {@link WifiManager#WIFI_AP_STATE_ENABLED},
401     *         {@link WifiManager#WIFI_AP_STATE_ENABLING},
402     *         {@link WifiManager#WIFI_AP_STATE_FAILED}
403     *
404     */
405    private final AtomicInteger mWifiApState = new AtomicInteger(WIFI_AP_STATE_DISABLED);
406
407    private final AtomicInteger mLastEnableUid = new AtomicInteger(Process.myUid());
408    private final AtomicInteger mLastApEnableUid = new AtomicInteger(Process.myUid());
409
410    private static final int SCAN_REQUEST = 0;
411    private static final String ACTION_START_SCAN =
412        "com.android.server.WifiManager.action.START_SCAN";
413
414    /**
415     * Keep track of whether WIFI is running.
416     */
417    private boolean mIsRunning = false;
418
419    /**
420     * Keep track of whether we last told the battery stats we had started.
421     */
422    private boolean mReportedRunning = false;
423
424    /**
425     * Most recently set source of starting WIFI.
426     */
427    private final WorkSource mRunningWifiUids = new WorkSource();
428
429    /**
430     * The last reported UIDs that were responsible for starting WIFI.
431     */
432    private final WorkSource mLastRunningWifiUids = new WorkSource();
433
434    private final IBatteryStats mBatteryStats;
435
436    public WifiStateMachine(Context context) {
437        super(TAG);
438
439        mContext = context;
440
441        mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, NETWORKTYPE, "");
442        mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo"));
443
444        IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
445        nwService = INetworkManagementService.Stub.asInterface(b);
446
447        mWifiMonitor = new WifiMonitor(this);
448        mDhcpInfo = new DhcpInfo();
449        mWifiInfo = new WifiInfo();
450        mInterfaceName = SystemProperties.get("wifi.interface", "tiwlan0");
451        mSupplicantStateTracker = new SupplicantStateTracker(context, this, getHandler());
452        mWpsStateMachine = new WpsStateMachine(context, this, getHandler());
453        mLinkProperties = new LinkProperties();
454
455        mNetworkInfo.setIsAvailable(false);
456        mLinkProperties.clear();
457        mLastBssid = null;
458        mLastNetworkId = -1;
459        mLastSignalLevel = -1;
460
461        mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
462        Intent scanIntent = new Intent(ACTION_START_SCAN, null);
463        mScanIntent = PendingIntent.getBroadcast(mContext, SCAN_REQUEST, scanIntent, 0);
464
465        mContext.registerReceiver(
466                new BroadcastReceiver() {
467                    @Override
468                    public void onReceive(Context context, Intent intent) {
469                        startScan(false);
470                    }
471                },
472                new IntentFilter(ACTION_START_SCAN));
473
474        mScanResultCache = new LinkedHashMap<String, ScanResult>(
475            SCAN_RESULT_CACHE_SIZE, 0.75f, true) {
476                /*
477                 * Limit the cache size by SCAN_RESULT_CACHE_SIZE
478                 * elements
479                 */
480                @Override
481                public boolean removeEldestEntry(Map.Entry eldest) {
482                    return SCAN_RESULT_CACHE_SIZE < this.size();
483                }
484        };
485
486        PowerManager powerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
487        mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
488
489        addState(mDefaultState);
490            addState(mInitialState, mDefaultState);
491            addState(mDriverUnloadingState, mDefaultState);
492            addState(mDriverUnloadedState, mDefaultState);
493                addState(mDriverFailedState, mDriverUnloadedState);
494            addState(mDriverLoadingState, mDefaultState);
495            addState(mDriverLoadedState, mDefaultState);
496            addState(mSupplicantStartingState, mDefaultState);
497            addState(mSupplicantStartedState, mDefaultState);
498                addState(mDriverStartingState, mSupplicantStartedState);
499                addState(mDriverStartedState, mSupplicantStartedState);
500                    addState(mScanModeState, mDriverStartedState);
501                    addState(mConnectModeState, mDriverStartedState);
502                        addState(mConnectingState, mConnectModeState);
503                        addState(mConnectedState, mConnectModeState);
504                        addState(mDisconnectingState, mConnectModeState);
505                        addState(mDisconnectedState, mConnectModeState);
506                        addState(mWaitForWpsCompletionState, mConnectModeState);
507                addState(mDriverStoppingState, mSupplicantStartedState);
508                addState(mDriverStoppedState, mSupplicantStartedState);
509            addState(mSupplicantStoppingState, mDefaultState);
510            addState(mSoftApStartedState, mDefaultState);
511
512        setInitialState(mInitialState);
513
514        if (DBG) setDbg(true);
515
516        //start the state machine
517        start();
518    }
519
520    /*********************************************************
521     * Methods exposed for public use
522     ********************************************************/
523
524    /**
525     * TODO: doc
526     */
527    public boolean syncPingSupplicant(AsyncChannel channel) {
528        Message resultMsg = channel.sendMessageSynchronously(CMD_PING_SUPPLICANT);
529        boolean result = (resultMsg.arg1 != FAILURE);
530        resultMsg.recycle();
531        return result;
532    }
533
534    /**
535     * TODO: doc
536     */
537    public void startScan(boolean forceActive) {
538        sendMessage(obtainMessage(CMD_START_SCAN, forceActive ?
539                SCAN_ACTIVE : SCAN_PASSIVE, 0));
540    }
541
542    /**
543     * TODO: doc
544     */
545    public void setWifiEnabled(boolean enable) {
546        mLastEnableUid.set(Binder.getCallingUid());
547        if (enable) {
548            /* Argument is the state that is entered prior to load */
549            sendMessage(obtainMessage(CMD_LOAD_DRIVER, WIFI_STATE_ENABLING, 0));
550            sendMessage(CMD_START_SUPPLICANT);
551        } else {
552            sendMessage(CMD_STOP_SUPPLICANT);
553            /* Argument is the state that is entered upon success */
554            sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_STATE_DISABLED, 0));
555        }
556    }
557
558    /**
559     * TODO: doc
560     */
561    public void setWifiApEnabled(WifiConfiguration wifiConfig, boolean enable) {
562        mLastApEnableUid.set(Binder.getCallingUid());
563        if (enable) {
564            /* Argument is the state that is entered prior to load */
565            sendMessage(obtainMessage(CMD_LOAD_DRIVER, WIFI_AP_STATE_ENABLING, 0));
566            sendMessage(obtainMessage(CMD_START_AP, wifiConfig));
567        } else {
568            sendMessage(CMD_STOP_AP);
569            /* Argument is the state that is entered upon success */
570            sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_AP_STATE_DISABLED, 0));
571        }
572    }
573
574    /**
575     * TODO: doc
576     */
577    public int syncGetWifiState() {
578        return mWifiState.get();
579    }
580
581    /**
582     * TODO: doc
583     */
584    public String syncGetWifiStateByName() {
585        switch (mWifiState.get()) {
586            case WIFI_STATE_DISABLING:
587                return "disabling";
588            case WIFI_STATE_DISABLED:
589                return "disabled";
590            case WIFI_STATE_ENABLING:
591                return "enabling";
592            case WIFI_STATE_ENABLED:
593                return "enabled";
594            case WIFI_STATE_UNKNOWN:
595                return "unknown state";
596            default:
597                return "[invalid state]";
598        }
599    }
600
601    /**
602     * TODO: doc
603     */
604    public int syncGetWifiApState() {
605        return mWifiApState.get();
606    }
607
608    /**
609     * TODO: doc
610     */
611    public String syncGetWifiApStateByName() {
612        switch (mWifiApState.get()) {
613            case WIFI_AP_STATE_DISABLING:
614                return "disabling";
615            case WIFI_AP_STATE_DISABLED:
616                return "disabled";
617            case WIFI_AP_STATE_ENABLING:
618                return "enabling";
619            case WIFI_AP_STATE_ENABLED:
620                return "enabled";
621            case WIFI_AP_STATE_FAILED:
622                return "failed";
623            default:
624                return "[invalid state]";
625        }
626    }
627
628    /**
629     * Get status information for the current connection, if any.
630     * @return a {@link WifiInfo} object containing information about the current connection
631     *
632     */
633    public WifiInfo syncRequestConnectionInfo() {
634        return mWifiInfo;
635    }
636
637    public DhcpInfo syncGetDhcpInfo() {
638        synchronized (mDhcpInfo) {
639            return new DhcpInfo(mDhcpInfo);
640        }
641    }
642
643    /**
644     * TODO: doc
645     */
646    public void setDriverStart(boolean enable) {
647        if (enable) {
648            sendMessage(CMD_START_DRIVER);
649        } else {
650            sendMessage(CMD_STOP_DRIVER);
651        }
652    }
653
654    /**
655     * TODO: doc
656     */
657    public void setScanOnlyMode(boolean enable) {
658      if (enable) {
659          sendMessage(obtainMessage(CMD_SET_SCAN_MODE, SCAN_ONLY_MODE, 0));
660      } else {
661          sendMessage(obtainMessage(CMD_SET_SCAN_MODE, CONNECT_MODE, 0));
662      }
663    }
664
665    /**
666     * TODO: doc
667     */
668    public void setScanType(boolean active) {
669      if (active) {
670          sendMessage(obtainMessage(CMD_SET_SCAN_TYPE, SCAN_ACTIVE, 0));
671      } else {
672          sendMessage(obtainMessage(CMD_SET_SCAN_TYPE, SCAN_PASSIVE, 0));
673      }
674    }
675
676    /**
677     * TODO: doc
678     */
679    public List<ScanResult> syncGetScanResultsList() {
680        return mScanResults;
681    }
682
683    /**
684     * Disconnect from Access Point
685     */
686    public void disconnectCommand() {
687        sendMessage(CMD_DISCONNECT);
688    }
689
690    /**
691     * Initiate a reconnection to AP
692     */
693    public void reconnectCommand() {
694        sendMessage(CMD_RECONNECT);
695    }
696
697    /**
698     * Initiate a re-association to AP
699     */
700    public void reassociateCommand() {
701        sendMessage(CMD_REASSOCIATE);
702    }
703
704    /**
705     * Add a network synchronously
706     *
707     * @return network id of the new network
708     */
709    public int syncAddOrUpdateNetwork(AsyncChannel channel, WifiConfiguration config) {
710        Message resultMsg = channel.sendMessageSynchronously(CMD_ADD_OR_UPDATE_NETWORK, config);
711        int result = resultMsg.arg1;
712        resultMsg.recycle();
713        return result;
714    }
715
716    public List<WifiConfiguration> syncGetConfiguredNetworks() {
717        return WifiConfigStore.getConfiguredNetworks();
718    }
719
720    /**
721     * Delete a network
722     *
723     * @param networkId id of the network to be removed
724     */
725    public boolean syncRemoveNetwork(AsyncChannel channel, int networkId) {
726        Message resultMsg = channel.sendMessageSynchronously(CMD_REMOVE_NETWORK, networkId);
727        boolean result = (resultMsg.arg1 != FAILURE);
728        resultMsg.recycle();
729        return result;
730    }
731
732    /**
733     * Enable a network
734     *
735     * @param netId network id of the network
736     * @param disableOthers true, if all other networks have to be disabled
737     * @return {@code true} if the operation succeeds, {@code false} otherwise
738     */
739    public boolean syncEnableNetwork(AsyncChannel channel, int netId, boolean disableOthers) {
740        Message resultMsg = channel.sendMessageSynchronously(CMD_ENABLE_NETWORK, netId,
741                disableOthers ? 1 : 0);
742        boolean result = (resultMsg.arg1 != FAILURE);
743        resultMsg.recycle();
744        return result;
745    }
746
747    /**
748     * Disable a network
749     *
750     * @param netId network id of the network
751     * @return {@code true} if the operation succeeds, {@code false} otherwise
752     */
753    public boolean syncDisableNetwork(AsyncChannel channel, int netId) {
754        Message resultMsg = channel.sendMessageSynchronously(CMD_DISABLE_NETWORK, netId);
755        boolean result = (resultMsg.arg1 != FAILURE);
756        resultMsg.recycle();
757        return result;
758    }
759
760    /**
761     * Blacklist a BSSID. This will avoid the AP if there are
762     * alternate APs to connect
763     *
764     * @param bssid BSSID of the network
765     */
766    public void addToBlacklist(String bssid) {
767        sendMessage(obtainMessage(CMD_BLACKLIST_NETWORK, bssid));
768    }
769
770    /**
771     * Clear the blacklist list
772     *
773     */
774    public void clearBlacklist() {
775        sendMessage(obtainMessage(CMD_CLEAR_BLACKLIST));
776    }
777
778    public void connectNetwork(int netId) {
779        sendMessage(obtainMessage(CMD_CONNECT_NETWORK, netId, 0));
780    }
781
782    public void connectNetwork(WifiConfiguration wifiConfig) {
783        /* arg1 is used to indicate netId, force a netId value of -1 when
784         * we are passing a configuration since the default value of
785         * 0 is a valid netId
786         */
787        sendMessage(obtainMessage(CMD_CONNECT_NETWORK, -1, 0, wifiConfig));
788    }
789
790    public void saveNetwork(WifiConfiguration wifiConfig) {
791        sendMessage(obtainMessage(CMD_SAVE_NETWORK, wifiConfig));
792    }
793
794    public void forgetNetwork(int netId) {
795        sendMessage(obtainMessage(CMD_FORGET_NETWORK, netId, 0));
796    }
797
798    public WpsResult startWps(AsyncChannel channel, WpsConfiguration config) {
799        WpsResult result;
800        switch (config.setup) {
801            case PIN_FROM_DEVICE:
802            case PBC:
803            case PIN_FROM_ACCESS_POINT:
804                //TODO: will go away with AsyncChannel use from settings
805                Message resultMsg = channel.sendMessageSynchronously(CMD_START_WPS, config);
806                result = (WpsResult) resultMsg.obj;
807                resultMsg.recycle();
808                break;
809            default:
810                result = new WpsResult(Status.FAILURE);
811                break;
812        }
813        return result;
814    }
815
816    public void enableRssiPolling(boolean enabled) {
817       sendMessage(obtainMessage(CMD_ENABLE_RSSI_POLL, enabled ? 1 : 0, 0));
818    }
819
820    public void enableAllNetworks() {
821        sendMessage(CMD_ENABLE_ALL_NETWORKS);
822    }
823
824    /**
825     * Start packet filtering
826     */
827    public void startPacketFiltering() {
828        sendMessage(CMD_START_PACKET_FILTERING);
829    }
830
831    /**
832     * Stop packet filtering
833     */
834    public void stopPacketFiltering() {
835        sendMessage(CMD_STOP_PACKET_FILTERING);
836    }
837
838    /**
839     * Set high performance mode of operation.
840     * Enabling would set active power mode and disable suspend optimizations;
841     * disabling would set auto power mode and enable suspend optimizations
842     * @param enable true if enable, false otherwise
843     */
844    public void setHighPerfModeEnabled(boolean enable) {
845        sendMessage(obtainMessage(CMD_SET_HIGH_PERF_MODE, enable ? 1 : 0, 0));
846    }
847
848    /**
849     * Set the country code
850     * @param countryCode following ISO 3166 format
851     * @param persist {@code true} if the setting should be remembered.
852     */
853    public void setCountryCode(String countryCode, boolean persist) {
854        if (persist) {
855            Settings.Secure.putString(mContext.getContentResolver(),
856                    Settings.Secure.WIFI_COUNTRY_CODE,
857                    countryCode);
858        }
859        sendMessage(obtainMessage(CMD_SET_COUNTRY_CODE, countryCode));
860    }
861
862    /**
863     * Set the operational frequency band
864     * @param band
865     * @param persist {@code true} if the setting should be remembered.
866     */
867    public void setFrequencyBand(int band, boolean persist) {
868        if (persist) {
869            Settings.Secure.putInt(mContext.getContentResolver(),
870                    Settings.Secure.WIFI_FREQUENCY_BAND,
871                    band);
872        }
873        sendMessage(obtainMessage(CMD_SET_FREQUENCY_BAND, band, 0));
874    }
875
876    /**
877     * Returns the operational frequency band
878     */
879    public int getFrequencyBand() {
880        return mFrequencyBand.get();
881    }
882
883    /**
884     * Send a message indicating bluetooth adapter connection state changed
885     */
886    public void sendBluetoothAdapterStateChange(int state) {
887        sendMessage(obtainMessage(CMD_BLUETOOTH_ADAPTER_STATE_CHANGE, state, 0));
888    }
889
890    /**
891     * Save configuration on supplicant
892     *
893     * @return {@code true} if the operation succeeds, {@code false} otherwise
894     *
895     * TODO: deprecate this
896     */
897    public boolean syncSaveConfig(AsyncChannel channel) {
898        Message resultMsg = channel.sendMessageSynchronously(CMD_SAVE_CONFIG);
899        boolean result = (resultMsg.arg1 != FAILURE);
900        resultMsg.recycle();
901        return result;
902    }
903
904    /**
905     * Request a wakelock with connectivity service to
906     * keep the device awake until we hand-off from wifi
907     * to an alternate network
908     */
909    public void requestCmWakeLock() {
910        sendMessage(CMD_REQUEST_CM_WAKELOCK);
911    }
912
913    public void updateBatteryWorkSource(WorkSource newSource) {
914        synchronized (mRunningWifiUids) {
915            try {
916                if (newSource != null) {
917                    mRunningWifiUids.set(newSource);
918                }
919                if (mIsRunning) {
920                    if (mReportedRunning) {
921                        // If the work source has changed since last time, need
922                        // to remove old work from battery stats.
923                        if (mLastRunningWifiUids.diff(mRunningWifiUids)) {
924                            mBatteryStats.noteWifiRunningChanged(mLastRunningWifiUids,
925                                    mRunningWifiUids);
926                            mLastRunningWifiUids.set(mRunningWifiUids);
927                        }
928                    } else {
929                        // Now being started, report it.
930                        mBatteryStats.noteWifiRunning(mRunningWifiUids);
931                        mLastRunningWifiUids.set(mRunningWifiUids);
932                        mReportedRunning = true;
933                    }
934                } else {
935                    if (mReportedRunning) {
936                        // Last reported we were running, time to stop.
937                        mBatteryStats.noteWifiStopped(mLastRunningWifiUids);
938                        mLastRunningWifiUids.clear();
939                        mReportedRunning = false;
940                    }
941                }
942                mWakeLock.setWorkSource(newSource);
943            } catch (RemoteException ignore) {
944            }
945        }
946    }
947
948    @Override
949    public String toString() {
950        StringBuffer sb = new StringBuffer();
951        String LS = System.getProperty("line.separator");
952        sb.append("current HSM state: ").append(getCurrentState().getName()).append(LS);
953        sb.append("mLinkProperties ").append(mLinkProperties).append(LS);
954        sb.append("mWifiInfo ").append(mWifiInfo).append(LS);
955        sb.append("mDhcpInfo ").append(mDhcpInfo).append(LS);
956        sb.append("mNetworkInfo ").append(mNetworkInfo).append(LS);
957        sb.append("mLastSignalLevel ").append(mLastSignalLevel).append(LS);
958        sb.append("mLastBssid ").append(mLastBssid).append(LS);
959        sb.append("mLastNetworkId ").append(mLastNetworkId).append(LS);
960        sb.append("mReconnectCount ").append(mReconnectCount).append(LS);
961        sb.append("mIsScanMode ").append(mIsScanMode).append(LS);
962        sb.append("Supplicant status").append(LS)
963                .append(WifiNative.statusCommand()).append(LS).append(LS);
964
965        sb.append(WifiConfigStore.dump());
966        return sb.toString();
967    }
968
969    /*********************************************************
970     * Internal private functions
971     ********************************************************/
972
973    /**
974     * Set the country code from the system setting value, if any.
975     */
976    private void setCountryCode() {
977        String countryCode = Settings.Secure.getString(mContext.getContentResolver(),
978                Settings.Secure.WIFI_COUNTRY_CODE);
979        if (countryCode != null && !countryCode.isEmpty()) {
980            setCountryCode(countryCode, false);
981        } else {
982            //use driver default
983        }
984    }
985
986    /**
987     * Set the frequency band from the system setting value, if any.
988     */
989    private void setFrequencyBand() {
990        int band = Settings.Secure.getInt(mContext.getContentResolver(),
991                Settings.Secure.WIFI_FREQUENCY_BAND, WifiManager.WIFI_FREQUENCY_BAND_AUTO);
992        setFrequencyBand(band, false);
993    }
994
995    private void setWifiState(int wifiState) {
996        final int previousWifiState = mWifiState.get();
997
998        try {
999            if (wifiState == WIFI_STATE_ENABLED) {
1000                mBatteryStats.noteWifiOn();
1001            } else if (wifiState == WIFI_STATE_DISABLED) {
1002                mBatteryStats.noteWifiOff();
1003            }
1004        } catch (RemoteException e) {
1005            Log.e(TAG, "Failed to note battery stats in wifi");
1006        }
1007
1008        mWifiState.set(wifiState);
1009
1010        if (DBG) Log.d(TAG, "setWifiState: " + syncGetWifiStateByName());
1011
1012        final Intent intent = new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION);
1013        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1014        intent.putExtra(WifiManager.EXTRA_WIFI_STATE, wifiState);
1015        intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_STATE, previousWifiState);
1016        mContext.sendStickyBroadcast(intent);
1017    }
1018
1019    private void setWifiApState(int wifiApState) {
1020        final int previousWifiApState = mWifiApState.get();
1021
1022        try {
1023            if (wifiApState == WIFI_AP_STATE_ENABLED) {
1024                mBatteryStats.noteWifiOn();
1025            } else if (wifiApState == WIFI_AP_STATE_DISABLED) {
1026                mBatteryStats.noteWifiOff();
1027            }
1028        } catch (RemoteException e) {
1029            Log.d(TAG, "Failed to note battery stats in wifi");
1030        }
1031
1032        // Update state
1033        mWifiApState.set(wifiApState);
1034
1035        if (DBG) Log.d(TAG, "setWifiApState: " + syncGetWifiApStateByName());
1036
1037        final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
1038        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1039        intent.putExtra(WifiManager.EXTRA_WIFI_AP_STATE, wifiApState);
1040        intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_AP_STATE, previousWifiApState);
1041        mContext.sendStickyBroadcast(intent);
1042    }
1043
1044    /**
1045     * Parse the scan result line passed to us by wpa_supplicant (helper).
1046     * @param line the line to parse
1047     * @return the {@link ScanResult} object
1048     */
1049    private ScanResult parseScanResult(String line) {
1050        ScanResult scanResult = null;
1051        if (line != null) {
1052            /*
1053             * Cache implementation (LinkedHashMap) is not synchronized, thus,
1054             * must synchronized here!
1055             */
1056            synchronized (mScanResultCache) {
1057                String[] result = scanResultPattern.split(line);
1058                if (3 <= result.length && result.length <= 5) {
1059                    String bssid = result[0];
1060                    // bssid | frequency | level | flags | ssid
1061                    int frequency;
1062                    int level;
1063                    try {
1064                        frequency = Integer.parseInt(result[1]);
1065                        level = Integer.parseInt(result[2]);
1066                        /* some implementations avoid negative values by adding 256
1067                         * so we need to adjust for that here.
1068                         */
1069                        if (level > 0) level -= 256;
1070                    } catch (NumberFormatException e) {
1071                        frequency = 0;
1072                        level = 0;
1073                    }
1074
1075                    /*
1076                     * The formatting of the results returned by
1077                     * wpa_supplicant is intended to make the fields
1078                     * line up nicely when printed,
1079                     * not to make them easy to parse. So we have to
1080                     * apply some heuristics to figure out which field
1081                     * is the SSID and which field is the flags.
1082                     */
1083                    String ssid;
1084                    String flags;
1085                    if (result.length == 4) {
1086                        if (result[3].charAt(0) == '[') {
1087                            flags = result[3];
1088                            ssid = "";
1089                        } else {
1090                            flags = "";
1091                            ssid = result[3];
1092                        }
1093                    } else if (result.length == 5) {
1094                        flags = result[3];
1095                        ssid = result[4];
1096                    } else {
1097                        // Here, we must have 3 fields: no flags and ssid
1098                        // set
1099                        flags = "";
1100                        ssid = "";
1101                    }
1102
1103                    // bssid + ssid is the hash key
1104                    String key = bssid + ssid;
1105                    scanResult = mScanResultCache.get(key);
1106                    if (scanResult != null) {
1107                        scanResult.level = level;
1108                        scanResult.SSID = ssid;
1109                        scanResult.capabilities = flags;
1110                        scanResult.frequency = frequency;
1111                    } else {
1112                        // Do not add scan results that have no SSID set
1113                        if (0 < ssid.trim().length()) {
1114                            scanResult =
1115                                new ScanResult(
1116                                    ssid, bssid, flags, level, frequency);
1117                            mScanResultCache.put(key, scanResult);
1118                        }
1119                    }
1120                } else {
1121                    Log.w(TAG, "Misformatted scan result text with " +
1122                          result.length + " fields: " + line);
1123                }
1124            }
1125        }
1126
1127        return scanResult;
1128    }
1129
1130    /**
1131     * scanResults input format
1132     * 00:bb:cc:dd:cc:ee       2427    166     [WPA-EAP-TKIP][WPA2-EAP-CCMP]   Net1
1133     * 00:bb:cc:dd:cc:ff       2412    165     [WPA-EAP-TKIP][WPA2-EAP-CCMP]   Net2
1134     */
1135    private void setScanResults(String scanResults) {
1136        if (scanResults == null) {
1137            return;
1138        }
1139
1140        List<ScanResult> scanList = new ArrayList<ScanResult>();
1141
1142        int lineCount = 0;
1143
1144        int scanResultsLen = scanResults.length();
1145        // Parse the result string, keeping in mind that the last line does
1146        // not end with a newline.
1147        for (int lineBeg = 0, lineEnd = 0; lineEnd <= scanResultsLen; ++lineEnd) {
1148            if (lineEnd == scanResultsLen || scanResults.charAt(lineEnd) == '\n') {
1149                ++lineCount;
1150
1151                if (lineCount == 1) {
1152                    lineBeg = lineEnd + 1;
1153                    continue;
1154                }
1155                if (lineEnd > lineBeg) {
1156                    String line = scanResults.substring(lineBeg, lineEnd);
1157                    ScanResult scanResult = parseScanResult(line);
1158                    if (scanResult != null) {
1159                        scanList.add(scanResult);
1160                    } else {
1161                        //TODO: hidden network handling
1162                    }
1163                }
1164                lineBeg = lineEnd + 1;
1165            }
1166        }
1167
1168        mScanResults = scanList;
1169    }
1170
1171    private String fetchSSID() {
1172        String status = WifiNative.statusCommand();
1173        if (status == null) {
1174            return null;
1175        }
1176        // extract ssid from a series of "name=value"
1177        String[] lines = status.split("\n");
1178        for (String line : lines) {
1179            String[] prop = line.split(" *= *");
1180            if (prop.length < 2) continue;
1181            String name = prop[0];
1182            String value = prop[1];
1183            if (name.equalsIgnoreCase("ssid")) return value;
1184        }
1185        return null;
1186    }
1187
1188    /*
1189     * Fetch RSSI and linkspeed on current connection
1190     */
1191    private void fetchRssiAndLinkSpeedNative() {
1192        int newRssi = WifiNative.getRssiCommand();
1193        if (newRssi != -1 && -200 < newRssi && newRssi < 256) { // screen out invalid values
1194            /* some implementations avoid negative values by adding 256
1195             * so we need to adjust for that here.
1196             */
1197            if (newRssi > 0) newRssi -= 256;
1198            mWifiInfo.setRssi(newRssi);
1199            /*
1200             * Rather then sending the raw RSSI out every time it
1201             * changes, we precalculate the signal level that would
1202             * be displayed in the status bar, and only send the
1203             * broadcast if that much more coarse-grained number
1204             * changes. This cuts down greatly on the number of
1205             * broadcasts, at the cost of not mWifiInforming others
1206             * interested in RSSI of all the changes in signal
1207             * level.
1208             */
1209            // TODO: The second arg to the call below needs to be a symbol somewhere, but
1210            // it's actually the size of an array of icons that's private
1211            // to StatusBar Policy.
1212            int newSignalLevel = WifiManager.calculateSignalLevel(newRssi, 4);
1213            if (newSignalLevel != mLastSignalLevel) {
1214                sendRssiChangeBroadcast(newRssi);
1215            }
1216            mLastSignalLevel = newSignalLevel;
1217        } else {
1218            mWifiInfo.setRssi(-200);
1219        }
1220        int newLinkSpeed = WifiNative.getLinkSpeedCommand();
1221        if (newLinkSpeed != -1) {
1222            mWifiInfo.setLinkSpeed(newLinkSpeed);
1223        }
1224    }
1225
1226    private void setHighPerfModeEnabledNative(boolean enable) {
1227        if(!WifiNative.setSuspendOptimizationsCommand(!enable)) {
1228            Log.e(TAG, "set suspend optimizations failed!");
1229        }
1230        if (enable) {
1231            if (!WifiNative.setPowerModeCommand(POWER_MODE_ACTIVE)) {
1232                Log.e(TAG, "set power mode active failed!");
1233            }
1234        } else {
1235            if (!WifiNative.setPowerModeCommand(POWER_MODE_AUTO)) {
1236                Log.e(TAG, "set power mode auto failed!");
1237            }
1238        }
1239    }
1240
1241    private void configureLinkProperties() {
1242        if (WifiConfigStore.isUsingStaticIp(mLastNetworkId)) {
1243            mLinkProperties = WifiConfigStore.getLinkProperties(mLastNetworkId);
1244        } else {
1245            // TODO - fix this for v6
1246            synchronized (mDhcpInfo) {
1247                mLinkProperties.addLinkAddress(new LinkAddress(
1248                        NetworkUtils.intToInetAddress(mDhcpInfo.ipAddress),
1249                        NetworkUtils.intToInetAddress(mDhcpInfo.netmask)));
1250                mLinkProperties.setGateway(NetworkUtils.intToInetAddress(mDhcpInfo.gateway));
1251                mLinkProperties.addDns(NetworkUtils.intToInetAddress(mDhcpInfo.dns1));
1252                mLinkProperties.addDns(NetworkUtils.intToInetAddress(mDhcpInfo.dns2));
1253            }
1254            mLinkProperties.setHttpProxy(WifiConfigStore.getProxyProperties(mLastNetworkId));
1255        }
1256        mLinkProperties.setInterfaceName(mInterfaceName);
1257        Log.d(TAG, "netId=" + mLastNetworkId  + " Link configured: " + mLinkProperties.toString());
1258    }
1259
1260    private int getMaxDhcpRetries() {
1261        return Settings.Secure.getInt(mContext.getContentResolver(),
1262                                      Settings.Secure.WIFI_MAX_DHCP_RETRY_COUNT,
1263                                      DEFAULT_MAX_DHCP_RETRIES);
1264    }
1265
1266    private void sendScanResultsAvailableBroadcast() {
1267        Intent intent = new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
1268        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1269        mContext.sendBroadcast(intent);
1270    }
1271
1272    private void sendRssiChangeBroadcast(final int newRssi) {
1273        Intent intent = new Intent(WifiManager.RSSI_CHANGED_ACTION);
1274        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1275        intent.putExtra(WifiManager.EXTRA_NEW_RSSI, newRssi);
1276        mContext.sendBroadcast(intent);
1277    }
1278
1279    private void sendNetworkStateChangeBroadcast(String bssid) {
1280        Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
1281        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
1282                | Intent.FLAG_RECEIVER_REPLACE_PENDING);
1283        intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, mNetworkInfo);
1284        intent.putExtra(WifiManager.EXTRA_LINK_PROPERTIES, mLinkProperties);
1285        if (bssid != null)
1286            intent.putExtra(WifiManager.EXTRA_BSSID, bssid);
1287        mContext.sendStickyBroadcast(intent);
1288    }
1289
1290    private void sendLinkConfigurationChangedBroadcast() {
1291        Intent intent = new Intent(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION);
1292        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1293        intent.putExtra(WifiManager.EXTRA_LINK_PROPERTIES, mLinkProperties);
1294        mContext.sendBroadcast(intent);
1295    }
1296
1297    private void sendSupplicantConnectionChangedBroadcast(boolean connected) {
1298        Intent intent = new Intent(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
1299        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1300        intent.putExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, connected);
1301        mContext.sendBroadcast(intent);
1302    }
1303
1304    /**
1305     * Record the detailed state of a network.
1306     * @param state the new @{code DetailedState}
1307     */
1308    private void setNetworkDetailedState(NetworkInfo.DetailedState state) {
1309        Log.d(TAG, "setDetailed state, old ="
1310                + mNetworkInfo.getDetailedState() + " and new state=" + state);
1311        if (state != mNetworkInfo.getDetailedState()) {
1312            mNetworkInfo.setDetailedState(state, null, null);
1313        }
1314    }
1315
1316    private DetailedState getNetworkDetailedState() {
1317        return mNetworkInfo.getDetailedState();
1318    }
1319
1320    /**
1321     * Resets the Wi-Fi Connections by clearing any state, resetting any sockets
1322     * using the interface, stopping DHCP & disabling interface
1323     */
1324    private void handleNetworkDisconnect() {
1325        Log.d(TAG, "Reset connections and stopping DHCP");
1326
1327        /*
1328         * Reset connections & stop DHCP
1329         */
1330        NetworkUtils.resetConnections(mInterfaceName);
1331
1332        if (!NetworkUtils.stopDhcp(mInterfaceName)) {
1333            Log.e(TAG, "Could not stop DHCP");
1334        }
1335
1336        /* Disable interface */
1337        NetworkUtils.disableInterface(mInterfaceName);
1338
1339        /* send event to CM & network change broadcast */
1340        setNetworkDetailedState(DetailedState.DISCONNECTED);
1341        sendNetworkStateChangeBroadcast(mLastBssid);
1342
1343        /* Reset data structures */
1344        mWifiInfo.setIpAddress(0);
1345        mWifiInfo.setBSSID(null);
1346        mWifiInfo.setSSID(null);
1347        mWifiInfo.setNetworkId(-1);
1348
1349        /* Clear network properties */
1350        mLinkProperties.clear();
1351
1352        mLastBssid= null;
1353        mLastNetworkId = -1;
1354
1355    }
1356
1357
1358    /*********************************************************
1359     * Notifications from WifiMonitor
1360     ********************************************************/
1361
1362    /**
1363     * Stores supplicant state change information passed from WifiMonitor
1364     */
1365    static class StateChangeResult {
1366        StateChangeResult(int networkId, String BSSID, SupplicantState state) {
1367            this.state = state;
1368            this.BSSID = BSSID;
1369            this.networkId = networkId;
1370        }
1371        int networkId;
1372        String BSSID;
1373        SupplicantState state;
1374    }
1375
1376    /**
1377     * Send the tracker a notification that a user-entered password key
1378     * may be incorrect (i.e., caused authentication to fail).
1379     */
1380    void notifyPasswordKeyMayBeIncorrect() {
1381        sendMessage(PASSWORD_MAY_BE_INCORRECT_EVENT);
1382    }
1383
1384    /**
1385     * Send the tracker a notification that a connection to the supplicant
1386     * daemon has been established.
1387     */
1388    void notifySupplicantConnection() {
1389        sendMessage(SUP_CONNECTION_EVENT);
1390    }
1391
1392    /**
1393     * Send the tracker a notification that connection to the supplicant
1394     * daemon is lost
1395     */
1396    void notifySupplicantLost() {
1397        sendMessage(SUP_DISCONNECTION_EVENT);
1398    }
1399
1400    /**
1401     * Send the tracker a notification that the state of Wifi connectivity
1402     * has changed.
1403     * @param networkId the configured network on which the state change occurred
1404     * @param newState the new network state
1405     * @param BSSID when the new state is {@link DetailedState#CONNECTED
1406     * NetworkInfo.DetailedState.CONNECTED},
1407     * this is the MAC address of the access point. Otherwise, it
1408     * is {@code null}.
1409     */
1410    void notifyNetworkStateChange(DetailedState newState, String BSSID, int networkId) {
1411        if (newState == NetworkInfo.DetailedState.CONNECTED) {
1412            sendMessage(obtainMessage(NETWORK_CONNECTION_EVENT, networkId, 0, BSSID));
1413        } else {
1414            sendMessage(obtainMessage(NETWORK_DISCONNECTION_EVENT, networkId, 0, BSSID));
1415        }
1416    }
1417
1418    /**
1419     * Send the tracker a notification that the state of the supplicant
1420     * has changed.
1421     * @param networkId the configured network on which the state change occurred
1422     * @param newState the new {@code SupplicantState}
1423     */
1424    void notifySupplicantStateChange(int networkId, String BSSID, SupplicantState newState) {
1425        sendMessage(obtainMessage(SUPPLICANT_STATE_CHANGE_EVENT,
1426                new StateChangeResult(networkId, BSSID, newState)));
1427    }
1428
1429    /**
1430     * Send the tracker a notification that a scan has completed, and results
1431     * are available.
1432     */
1433    void notifyScanResultsAvailable() {
1434        /**
1435         * Switch scan mode over to passive.
1436         * Turning off scan-only mode happens only in "Connect" mode
1437         */
1438        setScanType(false);
1439        sendMessage(SCAN_RESULTS_EVENT);
1440    }
1441
1442    void notifyDriverStarted() {
1443        sendMessage(DRIVER_START_EVENT);
1444    }
1445
1446    void notifyDriverStopped() {
1447        sendMessage(DRIVER_STOP_EVENT);
1448    }
1449
1450    void notifyDriverHung() {
1451        setWifiEnabled(false);
1452        setWifiEnabled(true);
1453    }
1454
1455    /********************************************************
1456     * HSM states
1457     *******************************************************/
1458
1459    class DefaultState extends HierarchicalState {
1460        @Override
1461        public boolean processMessage(Message message) {
1462            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
1463            switch (message.what) {
1464                case CMD_BLUETOOTH_ADAPTER_STATE_CHANGE:
1465                    mBluetoothConnectionActive = (message.arg1 !=
1466                            BluetoothAdapter.STATE_DISCONNECTED);
1467                    break;
1468                    /* Synchronous call returns */
1469                case CMD_PING_SUPPLICANT:
1470                case CMD_ENABLE_NETWORK:
1471                case CMD_DISABLE_NETWORK:
1472                case CMD_ADD_OR_UPDATE_NETWORK:
1473                case CMD_REMOVE_NETWORK:
1474                case CMD_SAVE_CONFIG:
1475                    mReplyChannel.replyToMessage(message, message.what, FAILURE);
1476                    break;
1477                case CMD_ENABLE_RSSI_POLL:
1478                    mEnableRssiPolling = (message.arg1 == 1);
1479                    break;
1480                    /* Discard */
1481                case CMD_LOAD_DRIVER:
1482                case CMD_UNLOAD_DRIVER:
1483                case CMD_START_SUPPLICANT:
1484                case CMD_STOP_SUPPLICANT:
1485                case CMD_START_DRIVER:
1486                case CMD_STOP_DRIVER:
1487                case CMD_START_AP:
1488                case CMD_STOP_AP:
1489                case CMD_START_SCAN:
1490                case CMD_DISCONNECT:
1491                case CMD_RECONNECT:
1492                case CMD_REASSOCIATE:
1493                case SUP_CONNECTION_EVENT:
1494                case SUP_DISCONNECTION_EVENT:
1495                case DRIVER_START_EVENT:
1496                case DRIVER_STOP_EVENT:
1497                case NETWORK_CONNECTION_EVENT:
1498                case NETWORK_DISCONNECTION_EVENT:
1499                case SCAN_RESULTS_EVENT:
1500                case SUPPLICANT_STATE_CHANGE_EVENT:
1501                case PASSWORD_MAY_BE_INCORRECT_EVENT:
1502                case CMD_BLACKLIST_NETWORK:
1503                case CMD_CLEAR_BLACKLIST:
1504                case CMD_SET_SCAN_MODE:
1505                case CMD_SET_SCAN_TYPE:
1506                case CMD_SET_HIGH_PERF_MODE:
1507                case CMD_SET_COUNTRY_CODE:
1508                case CMD_SET_FREQUENCY_BAND:
1509                case CMD_REQUEST_CM_WAKELOCK:
1510                case CMD_CONNECT_NETWORK:
1511                case CMD_SAVE_NETWORK:
1512                case CMD_FORGET_NETWORK:
1513                case CMD_RSSI_POLL:
1514                case CMD_ENABLE_ALL_NETWORKS:
1515                    break;
1516                case CMD_START_WPS:
1517                    /* Return failure when the state machine cannot handle WPS initiation*/
1518                    mReplyChannel.replyToMessage(message, message.what,
1519                                new WpsResult(Status.FAILURE));
1520                    break;
1521                default:
1522                    Log.e(TAG, "Error! unhandled message" + message);
1523                    break;
1524            }
1525            return HANDLED;
1526        }
1527    }
1528
1529    class InitialState extends HierarchicalState {
1530        @Override
1531        //TODO: could move logging into a common class
1532        public void enter() {
1533            if (DBG) Log.d(TAG, getName() + "\n");
1534            // [31-8] Reserved for future use
1535            // [7 - 0] HSM state change
1536            // 50021 wifi_state_changed (custom|1|5)
1537            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
1538
1539            if (WifiNative.isDriverLoaded()) {
1540                transitionTo(mDriverLoadedState);
1541            }
1542            else {
1543                transitionTo(mDriverUnloadedState);
1544            }
1545        }
1546    }
1547
1548    class DriverLoadingState extends HierarchicalState {
1549        @Override
1550        public void enter() {
1551            if (DBG) Log.d(TAG, getName() + "\n");
1552            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
1553
1554            final Message message = new Message();
1555            message.copyFrom(getCurrentMessage());
1556            /* TODO: add a timeout to fail when driver load is hung.
1557             * Similarly for driver unload.
1558             */
1559            new Thread(new Runnable() {
1560                public void run() {
1561                    mWakeLock.acquire();
1562                    //enabling state
1563                    switch(message.arg1) {
1564                        case WIFI_STATE_ENABLING:
1565                            setWifiState(WIFI_STATE_ENABLING);
1566                            break;
1567                        case WIFI_AP_STATE_ENABLING:
1568                            setWifiApState(WIFI_AP_STATE_ENABLING);
1569                            break;
1570                    }
1571
1572                    if(WifiNative.loadDriver()) {
1573                        Log.d(TAG, "Driver load successful");
1574                        sendMessage(CMD_LOAD_DRIVER_SUCCESS);
1575                    } else {
1576                        Log.e(TAG, "Failed to load driver!");
1577                        switch(message.arg1) {
1578                            case WIFI_STATE_ENABLING:
1579                                setWifiState(WIFI_STATE_UNKNOWN);
1580                                break;
1581                            case WIFI_AP_STATE_ENABLING:
1582                                setWifiApState(WIFI_AP_STATE_FAILED);
1583                                break;
1584                        }
1585                        sendMessage(CMD_LOAD_DRIVER_FAILURE);
1586                    }
1587                    mWakeLock.release();
1588                }
1589            }).start();
1590        }
1591
1592        @Override
1593        public boolean processMessage(Message message) {
1594            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
1595            switch (message.what) {
1596                case CMD_LOAD_DRIVER_SUCCESS:
1597                    transitionTo(mDriverLoadedState);
1598                    break;
1599                case CMD_LOAD_DRIVER_FAILURE:
1600                    transitionTo(mDriverFailedState);
1601                    break;
1602                case CMD_LOAD_DRIVER:
1603                case CMD_UNLOAD_DRIVER:
1604                case CMD_START_SUPPLICANT:
1605                case CMD_STOP_SUPPLICANT:
1606                case CMD_START_AP:
1607                case CMD_STOP_AP:
1608                case CMD_START_DRIVER:
1609                case CMD_STOP_DRIVER:
1610                case CMD_SET_SCAN_MODE:
1611                case CMD_SET_SCAN_TYPE:
1612                case CMD_SET_HIGH_PERF_MODE:
1613                case CMD_SET_COUNTRY_CODE:
1614                case CMD_SET_FREQUENCY_BAND:
1615                case CMD_START_PACKET_FILTERING:
1616                case CMD_STOP_PACKET_FILTERING:
1617                    deferMessage(message);
1618                    break;
1619                default:
1620                    return NOT_HANDLED;
1621            }
1622            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
1623            return HANDLED;
1624        }
1625    }
1626
1627    class DriverLoadedState extends HierarchicalState {
1628        @Override
1629        public void enter() {
1630            if (DBG) Log.d(TAG, getName() + "\n");
1631            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
1632        }
1633        @Override
1634        public boolean processMessage(Message message) {
1635            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
1636            switch(message.what) {
1637                case CMD_UNLOAD_DRIVER:
1638                    transitionTo(mDriverUnloadingState);
1639                    break;
1640                case CMD_START_SUPPLICANT:
1641                    if(WifiNative.startSupplicant()) {
1642                        Log.d(TAG, "Supplicant start successful");
1643                        mWifiMonitor.startMonitoring();
1644                        transitionTo(mSupplicantStartingState);
1645                    } else {
1646                        Log.e(TAG, "Failed to start supplicant!");
1647                        sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_STATE_UNKNOWN, 0));
1648                    }
1649                    break;
1650                case CMD_START_AP:
1651                    try {
1652                        nwService.startAccessPoint((WifiConfiguration) message.obj,
1653                                    mInterfaceName,
1654                                    SOFTAP_IFACE);
1655                    } catch(Exception e) {
1656                        Log.e(TAG, "Exception in startAccessPoint()");
1657                        sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_AP_STATE_FAILED, 0));
1658                        break;
1659                    }
1660                    Log.d(TAG, "Soft AP start successful");
1661                    setWifiApState(WIFI_AP_STATE_ENABLED);
1662                    transitionTo(mSoftApStartedState);
1663                    break;
1664                default:
1665                    return NOT_HANDLED;
1666            }
1667            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
1668            return HANDLED;
1669        }
1670    }
1671
1672    class DriverUnloadingState extends HierarchicalState {
1673        @Override
1674        public void enter() {
1675            if (DBG) Log.d(TAG, getName() + "\n");
1676            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
1677
1678            final Message message = new Message();
1679            message.copyFrom(getCurrentMessage());
1680            new Thread(new Runnable() {
1681                public void run() {
1682                    if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
1683                    mWakeLock.acquire();
1684                    if(WifiNative.unloadDriver()) {
1685                        Log.d(TAG, "Driver unload successful");
1686                        sendMessage(CMD_UNLOAD_DRIVER_SUCCESS);
1687
1688                        switch(message.arg1) {
1689                            case WIFI_STATE_DISABLED:
1690                            case WIFI_STATE_UNKNOWN:
1691                                setWifiState(message.arg1);
1692                                break;
1693                            case WIFI_AP_STATE_DISABLED:
1694                            case WIFI_AP_STATE_FAILED:
1695                                setWifiApState(message.arg1);
1696                                break;
1697                        }
1698                    } else {
1699                        Log.e(TAG, "Failed to unload driver!");
1700                        sendMessage(CMD_UNLOAD_DRIVER_FAILURE);
1701
1702                        switch(message.arg1) {
1703                            case WIFI_STATE_DISABLED:
1704                            case WIFI_STATE_UNKNOWN:
1705                                setWifiState(WIFI_STATE_UNKNOWN);
1706                                break;
1707                            case WIFI_AP_STATE_DISABLED:
1708                            case WIFI_AP_STATE_FAILED:
1709                                setWifiApState(WIFI_AP_STATE_FAILED);
1710                                break;
1711                        }
1712                    }
1713                    mWakeLock.release();
1714                }
1715            }).start();
1716        }
1717
1718        @Override
1719        public boolean processMessage(Message message) {
1720            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
1721            switch (message.what) {
1722                case CMD_UNLOAD_DRIVER_SUCCESS:
1723                    transitionTo(mDriverUnloadedState);
1724                    break;
1725                case CMD_UNLOAD_DRIVER_FAILURE:
1726                    transitionTo(mDriverFailedState);
1727                    break;
1728                case CMD_LOAD_DRIVER:
1729                case CMD_UNLOAD_DRIVER:
1730                case CMD_START_SUPPLICANT:
1731                case CMD_STOP_SUPPLICANT:
1732                case CMD_START_AP:
1733                case CMD_STOP_AP:
1734                case CMD_START_DRIVER:
1735                case CMD_STOP_DRIVER:
1736                case CMD_SET_SCAN_MODE:
1737                case CMD_SET_SCAN_TYPE:
1738                case CMD_SET_HIGH_PERF_MODE:
1739                case CMD_SET_COUNTRY_CODE:
1740                case CMD_SET_FREQUENCY_BAND:
1741                case CMD_START_PACKET_FILTERING:
1742                case CMD_STOP_PACKET_FILTERING:
1743                    deferMessage(message);
1744                    break;
1745                default:
1746                    return NOT_HANDLED;
1747            }
1748            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
1749            return HANDLED;
1750        }
1751    }
1752
1753    class DriverUnloadedState extends HierarchicalState {
1754        @Override
1755        public void enter() {
1756            if (DBG) Log.d(TAG, getName() + "\n");
1757            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
1758        }
1759        @Override
1760        public boolean processMessage(Message message) {
1761            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
1762            switch (message.what) {
1763                case CMD_LOAD_DRIVER:
1764                    transitionTo(mDriverLoadingState);
1765                    break;
1766                default:
1767                    return NOT_HANDLED;
1768            }
1769            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
1770            return HANDLED;
1771        }
1772    }
1773
1774    class DriverFailedState extends HierarchicalState {
1775        @Override
1776        public void enter() {
1777            Log.e(TAG, getName() + "\n");
1778            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
1779        }
1780        @Override
1781        public boolean processMessage(Message message) {
1782            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
1783            return NOT_HANDLED;
1784        }
1785    }
1786
1787
1788    class SupplicantStartingState extends HierarchicalState {
1789        @Override
1790        public void enter() {
1791            if (DBG) Log.d(TAG, getName() + "\n");
1792            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
1793        }
1794        @Override
1795        public boolean processMessage(Message message) {
1796            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
1797            switch(message.what) {
1798                case SUP_CONNECTION_EVENT:
1799                    Log.d(TAG, "Supplicant connection established");
1800                    setWifiState(WIFI_STATE_ENABLED);
1801                    mSupplicantRestartCount = 0;
1802                    /* Reset the supplicant state to indicate the supplicant
1803                     * state is not known at this time */
1804                    mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
1805                    mWpsStateMachine.sendMessage(CMD_RESET_WPS_STATE);
1806                    /* Initialize data structures */
1807                    mLastBssid = null;
1808                    mLastNetworkId = -1;
1809                    mLastSignalLevel = -1;
1810
1811                    mWifiInfo.setMacAddress(WifiNative.getMacAddressCommand());
1812
1813                    WifiConfigStore.initialize(mContext);
1814
1815                    //TODO: initialize and fix multicast filtering
1816                    //mWM.initializeMulticastFiltering();
1817
1818                    sendSupplicantConnectionChangedBroadcast(true);
1819                    transitionTo(mDriverStartedState);
1820                    break;
1821                case SUP_DISCONNECTION_EVENT:
1822                    if (++mSupplicantRestartCount <= SUPPLICANT_RESTART_TRIES) {
1823                        Log.e(TAG, "Failed to setup control channel, restart supplicant");
1824                        WifiNative.killSupplicant();
1825                        transitionTo(mDriverLoadedState);
1826                        sendMessageDelayed(CMD_START_SUPPLICANT, SUPPLICANT_RESTART_INTERVAL_MSECS);
1827                    } else {
1828                        mSupplicantRestartCount = 0;
1829                        Log.e(TAG, "Failed " + mSupplicantRestartCount +
1830                                " times to start supplicant, unload driver");
1831                        transitionTo(mDriverLoadedState);
1832                        sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_STATE_UNKNOWN, 0));
1833                    }
1834                    break;
1835                case CMD_LOAD_DRIVER:
1836                case CMD_UNLOAD_DRIVER:
1837                case CMD_START_SUPPLICANT:
1838                case CMD_STOP_SUPPLICANT:
1839                case CMD_START_AP:
1840                case CMD_STOP_AP:
1841                case CMD_START_DRIVER:
1842                case CMD_STOP_DRIVER:
1843                case CMD_SET_SCAN_MODE:
1844                case CMD_SET_SCAN_TYPE:
1845                case CMD_SET_HIGH_PERF_MODE:
1846                case CMD_SET_COUNTRY_CODE:
1847                case CMD_SET_FREQUENCY_BAND:
1848                case CMD_START_PACKET_FILTERING:
1849                case CMD_STOP_PACKET_FILTERING:
1850                    deferMessage(message);
1851                    break;
1852                default:
1853                    return NOT_HANDLED;
1854            }
1855            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
1856            return HANDLED;
1857        }
1858    }
1859
1860    class SupplicantStartedState extends HierarchicalState {
1861        @Override
1862        public void enter() {
1863            if (DBG) Log.d(TAG, getName() + "\n");
1864            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
1865            /* Initialize for connect mode operation at start */
1866            mIsScanMode = false;
1867            /* Wifi is available as long as we have a connection to supplicant */
1868            mNetworkInfo.setIsAvailable(true);
1869        }
1870        @Override
1871        public boolean processMessage(Message message) {
1872            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
1873            WifiConfiguration config;
1874            boolean eventLoggingEnabled = true;
1875            switch(message.what) {
1876                case CMD_STOP_SUPPLICANT:   /* Supplicant stopped by user */
1877                    Log.d(TAG, "stopping supplicant");
1878                    if (!WifiNative.stopSupplicant()) {
1879                        Log.e(TAG, "Failed to stop supplicant, issue kill");
1880                        WifiNative.killSupplicant();
1881                    }
1882                    mNetworkInfo.setIsAvailable(false);
1883                    handleNetworkDisconnect();
1884                    setWifiState(WIFI_STATE_DISABLING);
1885                    sendSupplicantConnectionChangedBroadcast(false);
1886                    mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
1887                    mWpsStateMachine.sendMessage(CMD_RESET_WPS_STATE);
1888                    transitionTo(mSupplicantStoppingState);
1889                    break;
1890                case SUP_DISCONNECTION_EVENT:  /* Supplicant connection lost */
1891                    Log.e(TAG, "Connection lost, restart supplicant");
1892                    WifiNative.killSupplicant();
1893                    WifiNative.closeSupplicantConnection();
1894                    mNetworkInfo.setIsAvailable(false);
1895                    handleNetworkDisconnect();
1896                    sendSupplicantConnectionChangedBroadcast(false);
1897                    mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
1898                    mWpsStateMachine.sendMessage(CMD_RESET_WPS_STATE);
1899                    transitionTo(mDriverLoadedState);
1900                    sendMessageDelayed(CMD_START_SUPPLICANT, SUPPLICANT_RESTART_INTERVAL_MSECS);
1901                    break;
1902                case SCAN_RESULTS_EVENT:
1903                    eventLoggingEnabled = false;
1904                    setScanResults(WifiNative.scanResultsCommand());
1905                    sendScanResultsAvailableBroadcast();
1906                    break;
1907                case CMD_PING_SUPPLICANT:
1908                    boolean ok = WifiNative.pingCommand();
1909                    mReplyChannel.replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
1910                    break;
1911                case CMD_ADD_OR_UPDATE_NETWORK:
1912                    config = (WifiConfiguration) message.obj;
1913                    mReplyChannel.replyToMessage(message, CMD_ADD_OR_UPDATE_NETWORK,
1914                            WifiConfigStore.addOrUpdateNetwork(config));
1915                    break;
1916                case CMD_REMOVE_NETWORK:
1917                    ok = WifiConfigStore.removeNetwork(message.arg1);
1918                    mReplyChannel.replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
1919                    break;
1920                case CMD_ENABLE_NETWORK:
1921                    ok = WifiConfigStore.enableNetwork(message.arg1, message.arg2 == 1);
1922                    mReplyChannel.replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
1923                    break;
1924                case CMD_ENABLE_ALL_NETWORKS:
1925                    WifiConfigStore.enableAllNetworks();
1926                    break;
1927                case CMD_DISABLE_NETWORK:
1928                    ok = WifiConfigStore.disableNetwork(message.arg1);
1929                    mReplyChannel.replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
1930                    break;
1931                case CMD_BLACKLIST_NETWORK:
1932                    WifiNative.addToBlacklistCommand((String)message.obj);
1933                    break;
1934                case CMD_CLEAR_BLACKLIST:
1935                    WifiNative.clearBlacklistCommand();
1936                    break;
1937                case CMD_SAVE_CONFIG:
1938                    ok = WifiConfigStore.saveConfig();
1939                    mReplyChannel.replyToMessage(message, CMD_SAVE_CONFIG, ok ? SUCCESS : FAILURE);
1940
1941                    // Inform the backup manager about a data change
1942                    IBackupManager ibm = IBackupManager.Stub.asInterface(
1943                            ServiceManager.getService(Context.BACKUP_SERVICE));
1944                    if (ibm != null) {
1945                        try {
1946                            ibm.dataChanged("com.android.providers.settings");
1947                        } catch (Exception e) {
1948                            // Try again later
1949                        }
1950                    }
1951                    break;
1952                    /* Cannot start soft AP while in client mode */
1953                case CMD_START_AP:
1954                    Log.d(TAG, "Failed to start soft AP with a running supplicant");
1955                    setWifiApState(WIFI_AP_STATE_FAILED);
1956                    break;
1957                case CMD_SET_SCAN_MODE:
1958                    mIsScanMode = (message.arg1 == SCAN_ONLY_MODE);
1959                    break;
1960                case CMD_SAVE_NETWORK:
1961                    config = (WifiConfiguration) message.obj;
1962                    WifiConfigStore.saveNetwork(config);
1963                    break;
1964                case CMD_FORGET_NETWORK:
1965                    WifiConfigStore.forgetNetwork(message.arg1);
1966                    break;
1967                default:
1968                    return NOT_HANDLED;
1969            }
1970            if (eventLoggingEnabled) {
1971                EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
1972            }
1973            return HANDLED;
1974        }
1975
1976        @Override
1977        public void exit() {
1978            mNetworkInfo.setIsAvailable(false);
1979        }
1980    }
1981
1982    class SupplicantStoppingState extends HierarchicalState {
1983        @Override
1984        public void enter() {
1985            if (DBG) Log.d(TAG, getName() + "\n");
1986            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
1987        }
1988        @Override
1989        public boolean processMessage(Message message) {
1990            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
1991            switch(message.what) {
1992                case SUP_CONNECTION_EVENT:
1993                    Log.e(TAG, "Supplicant connection received while stopping");
1994                    break;
1995                case SUP_DISCONNECTION_EVENT:
1996                    Log.d(TAG, "Supplicant connection lost");
1997                    WifiNative.closeSupplicantConnection();
1998                    transitionTo(mDriverLoadedState);
1999                    break;
2000                case CMD_LOAD_DRIVER:
2001                case CMD_UNLOAD_DRIVER:
2002                case CMD_START_SUPPLICANT:
2003                case CMD_STOP_SUPPLICANT:
2004                case CMD_START_AP:
2005                case CMD_STOP_AP:
2006                case CMD_START_DRIVER:
2007                case CMD_STOP_DRIVER:
2008                case CMD_SET_SCAN_MODE:
2009                case CMD_SET_SCAN_TYPE:
2010                case CMD_SET_HIGH_PERF_MODE:
2011                case CMD_SET_COUNTRY_CODE:
2012                case CMD_SET_FREQUENCY_BAND:
2013                case CMD_START_PACKET_FILTERING:
2014                case CMD_STOP_PACKET_FILTERING:
2015                    deferMessage(message);
2016                    break;
2017                default:
2018                    return NOT_HANDLED;
2019            }
2020            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
2021            return HANDLED;
2022        }
2023    }
2024
2025    class DriverStartingState extends HierarchicalState {
2026        @Override
2027        public void enter() {
2028            if (DBG) Log.d(TAG, getName() + "\n");
2029            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
2030        }
2031        @Override
2032        public boolean processMessage(Message message) {
2033            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
2034            switch(message.what) {
2035                case DRIVER_START_EVENT:
2036                    transitionTo(mDriverStartedState);
2037                    break;
2038                    /* Queue driver commands & connection events */
2039                case CMD_START_DRIVER:
2040                case CMD_STOP_DRIVER:
2041                case SUPPLICANT_STATE_CHANGE_EVENT:
2042                case NETWORK_CONNECTION_EVENT:
2043                case NETWORK_DISCONNECTION_EVENT:
2044                case PASSWORD_MAY_BE_INCORRECT_EVENT:
2045                case CMD_SET_SCAN_TYPE:
2046                case CMD_SET_HIGH_PERF_MODE:
2047                case CMD_SET_COUNTRY_CODE:
2048                case CMD_SET_FREQUENCY_BAND:
2049                case CMD_START_PACKET_FILTERING:
2050                case CMD_STOP_PACKET_FILTERING:
2051                case CMD_START_SCAN:
2052                case CMD_DISCONNECT:
2053                case CMD_REASSOCIATE:
2054                case CMD_RECONNECT:
2055                    deferMessage(message);
2056                    break;
2057                default:
2058                    return NOT_HANDLED;
2059            }
2060            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
2061            return HANDLED;
2062        }
2063    }
2064
2065    class DriverStartedState extends HierarchicalState {
2066        @Override
2067        public void enter() {
2068            if (DBG) Log.d(TAG, getName() + "\n");
2069            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
2070
2071            mIsRunning = true;
2072            updateBatteryWorkSource(null);
2073
2074            /**
2075             * Enable bluetooth coexistence scan mode when bluetooth connection is active.
2076             * When this mode is on, some of the low-level scan parameters used by the
2077             * driver are changed to reduce interference with bluetooth
2078             */
2079            WifiNative.setBluetoothCoexistenceScanModeCommand(mBluetoothConnectionActive);
2080            /* set country code */
2081            setCountryCode();
2082            /* set frequency band of operation */
2083            setFrequencyBand();
2084            /* initialize network state */
2085            setNetworkDetailedState(DetailedState.DISCONNECTED);
2086
2087            if (mIsScanMode) {
2088                WifiNative.setScanResultHandlingCommand(SCAN_ONLY_MODE);
2089                WifiNative.disconnectCommand();
2090                transitionTo(mScanModeState);
2091            } else {
2092                WifiNative.setScanResultHandlingCommand(CONNECT_MODE);
2093                WifiNative.reconnectCommand();
2094                transitionTo(mDisconnectedState);
2095            }
2096        }
2097        @Override
2098        public boolean processMessage(Message message) {
2099            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
2100            boolean eventLoggingEnabled = true;
2101            switch(message.what) {
2102                case CMD_SET_SCAN_TYPE:
2103                    if (message.arg1 == SCAN_ACTIVE) {
2104                        WifiNative.setScanModeCommand(true);
2105                    } else {
2106                        WifiNative.setScanModeCommand(false);
2107                    }
2108                    break;
2109                case CMD_START_SCAN:
2110                    eventLoggingEnabled = false;
2111                    WifiNative.scanCommand(message.arg1 == SCAN_ACTIVE);
2112                    break;
2113                case CMD_SET_HIGH_PERF_MODE:
2114                    setHighPerfModeEnabledNative(message.arg1 == 1);
2115                    break;
2116                case CMD_SET_COUNTRY_CODE:
2117                    String country = (String) message.obj;
2118                    Log.d(TAG, "set country code " + country);
2119                    if (!WifiNative.setCountryCodeCommand(country.toUpperCase())) {
2120                        Log.e(TAG, "Failed to set country code " + country);
2121                    }
2122                    break;
2123                case CMD_SET_FREQUENCY_BAND:
2124                    int band =  message.arg1;
2125                    Log.d(TAG, "set frequency band " + band);
2126                    if (WifiNative.setBandCommand(band)) {
2127                        mFrequencyBand.set(band);
2128                        //Fetch the latest scan results when frequency band is set
2129                        startScan(true);
2130                    } else {
2131                        Log.e(TAG, "Failed to set frequency band " + band);
2132                    }
2133                    break;
2134                case CMD_BLUETOOTH_ADAPTER_STATE_CHANGE:
2135                    mBluetoothConnectionActive = (message.arg1 !=
2136                            BluetoothAdapter.STATE_DISCONNECTED);
2137                    WifiNative.setBluetoothCoexistenceScanModeCommand(mBluetoothConnectionActive);
2138                    break;
2139                case CMD_STOP_DRIVER:
2140                    mWakeLock.acquire();
2141                    WifiNative.stopDriverCommand();
2142                    transitionTo(mDriverStoppingState);
2143                    mWakeLock.release();
2144                    break;
2145                case CMD_START_PACKET_FILTERING:
2146                    WifiNative.startPacketFiltering();
2147                    break;
2148                case CMD_STOP_PACKET_FILTERING:
2149                    WifiNative.stopPacketFiltering();
2150                    break;
2151                default:
2152                    return NOT_HANDLED;
2153            }
2154            if (eventLoggingEnabled) {
2155                EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
2156            }
2157            return HANDLED;
2158        }
2159        @Override
2160        public void exit() {
2161            if (DBG) Log.d(TAG, getName() + "\n");
2162            mIsRunning = false;
2163            updateBatteryWorkSource(null);
2164            mScanResults = null;
2165        }
2166    }
2167
2168    class DriverStoppingState extends HierarchicalState {
2169        @Override
2170        public void enter() {
2171            if (DBG) Log.d(TAG, getName() + "\n");
2172            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
2173        }
2174        @Override
2175        public boolean processMessage(Message message) {
2176            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
2177            switch(message.what) {
2178                case DRIVER_STOP_EVENT:
2179                    transitionTo(mDriverStoppedState);
2180                    break;
2181                    /* Queue driver commands */
2182                case CMD_START_DRIVER:
2183                case CMD_STOP_DRIVER:
2184                case CMD_SET_SCAN_TYPE:
2185                case CMD_SET_HIGH_PERF_MODE:
2186                case CMD_SET_COUNTRY_CODE:
2187                case CMD_SET_FREQUENCY_BAND:
2188                case CMD_START_PACKET_FILTERING:
2189                case CMD_STOP_PACKET_FILTERING:
2190                case CMD_START_SCAN:
2191                case CMD_DISCONNECT:
2192                case CMD_REASSOCIATE:
2193                case CMD_RECONNECT:
2194                    deferMessage(message);
2195                    break;
2196                default:
2197                    return NOT_HANDLED;
2198            }
2199            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
2200            return HANDLED;
2201        }
2202    }
2203
2204    class DriverStoppedState extends HierarchicalState {
2205        @Override
2206        public void enter() {
2207            if (DBG) Log.d(TAG, getName() + "\n");
2208            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
2209        }
2210        @Override
2211        public boolean processMessage(Message message) {
2212            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
2213            switch (message.what) {
2214                case CMD_START_DRIVER:
2215                    mWakeLock.acquire();
2216                    WifiNative.startDriverCommand();
2217                    transitionTo(mDriverStartingState);
2218                    mWakeLock.release();
2219                    break;
2220                default:
2221                    return NOT_HANDLED;
2222            }
2223            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
2224            return HANDLED;
2225        }
2226    }
2227
2228    class ScanModeState extends HierarchicalState {
2229        @Override
2230        public void enter() {
2231            if (DBG) Log.d(TAG, getName() + "\n");
2232            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
2233        }
2234        @Override
2235        public boolean processMessage(Message message) {
2236            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
2237            switch(message.what) {
2238                case CMD_SET_SCAN_MODE:
2239                    if (message.arg1 == SCAN_ONLY_MODE) {
2240                        /* Ignore */
2241                        return HANDLED;
2242                    } else {
2243                        WifiNative.setScanResultHandlingCommand(message.arg1);
2244                        WifiNative.reconnectCommand();
2245                        mIsScanMode = false;
2246                        transitionTo(mDisconnectedState);
2247                    }
2248                    break;
2249                    /* Ignore */
2250                case CMD_DISCONNECT:
2251                case CMD_RECONNECT:
2252                case CMD_REASSOCIATE:
2253                case SUPPLICANT_STATE_CHANGE_EVENT:
2254                case NETWORK_CONNECTION_EVENT:
2255                case NETWORK_DISCONNECTION_EVENT:
2256                    break;
2257                default:
2258                    return NOT_HANDLED;
2259            }
2260            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
2261            return HANDLED;
2262        }
2263    }
2264
2265    class ConnectModeState extends HierarchicalState {
2266        @Override
2267        public void enter() {
2268            if (DBG) Log.d(TAG, getName() + "\n");
2269            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
2270        }
2271        @Override
2272        public boolean processMessage(Message message) {
2273            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
2274            StateChangeResult stateChangeResult;
2275            switch(message.what) {
2276                case PASSWORD_MAY_BE_INCORRECT_EVENT:
2277                    mSupplicantStateTracker.sendMessage(PASSWORD_MAY_BE_INCORRECT_EVENT);
2278                    break;
2279                case SUPPLICANT_STATE_CHANGE_EVENT:
2280                    stateChangeResult = (StateChangeResult) message.obj;
2281                    SupplicantState state = stateChangeResult.state;
2282                    // Supplicant state change
2283                    // [31-13] Reserved for future use
2284                    // [8 - 0] Supplicant state (as defined in SupplicantState.java)
2285                    // 50023 supplicant_state_changed (custom|1|5)
2286                    EventLog.writeEvent(EVENTLOG_SUPPLICANT_STATE_CHANGED, state.ordinal());
2287                    mWifiInfo.setSupplicantState(state);
2288                    mWifiInfo.setNetworkId(stateChangeResult.networkId);
2289                    if (state == SupplicantState.ASSOCIATING) {
2290                        /* BSSID is valid only in ASSOCIATING state */
2291                        mWifiInfo.setBSSID(stateChangeResult.BSSID);
2292                    }
2293
2294                    mSupplicantStateTracker.sendMessage(Message.obtain(message));
2295                    mWpsStateMachine.sendMessage(Message.obtain(message));
2296                    break;
2297                    /* Do a redundant disconnect without transition */
2298                case CMD_DISCONNECT:
2299                    WifiNative.disconnectCommand();
2300                    break;
2301                case CMD_RECONNECT:
2302                    WifiNative.reconnectCommand();
2303                    break;
2304                case CMD_REASSOCIATE:
2305                    WifiNative.reassociateCommand();
2306                    break;
2307                case CMD_CONNECT_NETWORK:
2308                    int netId = message.arg1;
2309                    WifiConfiguration config = (WifiConfiguration) message.obj;
2310
2311                    /* We connect to a specific network by issuing a select
2312                     * to the WifiConfigStore. This enables the network,
2313                     * while disabling all other networks in the supplicant.
2314                     * Disabling a connected network will cause a disconnection
2315                     * from the network. A reconnectCommand() will then initiate
2316                     * a connection to the enabled network.
2317                     */
2318                    if (config != null) {
2319                        WifiConfigStore.selectNetwork(config);
2320                    } else {
2321                        WifiConfigStore.selectNetwork(netId);
2322                    }
2323
2324                    /* The state tracker handles enabling networks upon completion/failure */
2325                    mSupplicantStateTracker.sendMessage(CMD_CONNECT_NETWORK);
2326
2327                    WifiNative.reconnectCommand();
2328
2329                    /* Expect a disconnection from the old connection */
2330                    transitionTo(mDisconnectingState);
2331                    break;
2332                case CMD_START_WPS:
2333                    mWpsStateMachine.sendMessage(Message.obtain(message));
2334                    transitionTo(mWaitForWpsCompletionState);
2335                    break;
2336                case SCAN_RESULTS_EVENT:
2337                    /* Set the scan setting back to "connect" mode */
2338                    WifiNative.setScanResultHandlingCommand(CONNECT_MODE);
2339                    /* Handle scan results */
2340                    return NOT_HANDLED;
2341                case NETWORK_CONNECTION_EVENT:
2342                    Log.d(TAG,"Network connection established");
2343                    mLastNetworkId = message.arg1;
2344                    mLastBssid = (String) message.obj;
2345
2346                    //TODO: make supplicant modification to push this in events
2347                    mWifiInfo.setSSID(fetchSSID());
2348                    mWifiInfo.setBSSID(mLastBssid);
2349                    mWifiInfo.setNetworkId(mLastNetworkId);
2350                    /* send event to CM & network change broadcast */
2351                    setNetworkDetailedState(DetailedState.OBTAINING_IPADDR);
2352                    sendNetworkStateChangeBroadcast(mLastBssid);
2353                    transitionTo(mConnectingState);
2354                    break;
2355                case NETWORK_DISCONNECTION_EVENT:
2356                    Log.d(TAG,"Network connection lost");
2357                    handleNetworkDisconnect();
2358                    transitionTo(mDisconnectedState);
2359                    break;
2360                default:
2361                    return NOT_HANDLED;
2362            }
2363            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
2364            return HANDLED;
2365        }
2366    }
2367
2368    class ConnectingState extends HierarchicalState {
2369        boolean mModifiedBluetoothCoexistenceMode;
2370        int mPowerMode;
2371        boolean mUseStaticIp;
2372        Thread mDhcpThread;
2373
2374        @Override
2375        public void enter() {
2376            if (DBG) Log.d(TAG, getName() + "\n");
2377            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
2378            mUseStaticIp = WifiConfigStore.isUsingStaticIp(mLastNetworkId);
2379            if (!mUseStaticIp) {
2380                mDhcpThread = null;
2381                mModifiedBluetoothCoexistenceMode = false;
2382                mPowerMode = POWER_MODE_AUTO;
2383
2384                if (!mBluetoothConnectionActive) {
2385                    /*
2386                     * There are problems setting the Wi-Fi driver's power
2387                     * mode to active when bluetooth coexistence mode is
2388                     * enabled or sense.
2389                     * <p>
2390                     * We set Wi-Fi to active mode when
2391                     * obtaining an IP address because we've found
2392                     * compatibility issues with some routers with low power
2393                     * mode.
2394                     * <p>
2395                     * In order for this active power mode to properly be set,
2396                     * we disable coexistence mode until we're done with
2397                     * obtaining an IP address.  One exception is if we
2398                     * are currently connected to a headset, since disabling
2399                     * coexistence would interrupt that connection.
2400                     */
2401                    mModifiedBluetoothCoexistenceMode = true;
2402
2403                    // Disable the coexistence mode
2404                    WifiNative.setBluetoothCoexistenceModeCommand(
2405                            WifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED);
2406                }
2407
2408                mPowerMode =  WifiNative.getPowerModeCommand();
2409                if (mPowerMode < 0) {
2410                  // Handle the case where supplicant driver does not support
2411                  // getPowerModeCommand.
2412                    mPowerMode = POWER_MODE_AUTO;
2413                }
2414                if (mPowerMode != POWER_MODE_ACTIVE) {
2415                    WifiNative.setPowerModeCommand(POWER_MODE_ACTIVE);
2416                }
2417
2418                Log.d(TAG, "DHCP request started");
2419                mDhcpThread = new Thread(new Runnable() {
2420                    public void run() {
2421                        DhcpInfo dhcpInfo = new DhcpInfo();
2422                        if (NetworkUtils.runDhcp(mInterfaceName, dhcpInfo)) {
2423                            Log.d(TAG, "DHCP request succeeded");
2424                            synchronized (mDhcpInfo) {
2425                                mDhcpInfo = dhcpInfo;
2426                            }
2427                            sendMessage(CMD_IP_CONFIG_SUCCESS);
2428                        } else {
2429                            Log.d(TAG, "DHCP request failed: " +
2430                                    NetworkUtils.getDhcpError());
2431                            sendMessage(CMD_IP_CONFIG_FAILURE);
2432                        }
2433                    }
2434                });
2435                mDhcpThread.start();
2436            } else {
2437                DhcpInfo dhcpInfo = WifiConfigStore.getIpConfiguration(mLastNetworkId);
2438                if (NetworkUtils.configureInterface(mInterfaceName, dhcpInfo)) {
2439                    Log.v(TAG, "Static IP configuration succeeded");
2440                    synchronized (mDhcpInfo) {
2441                        mDhcpInfo = dhcpInfo;
2442                    }
2443                    sendMessage(CMD_IP_CONFIG_SUCCESS);
2444                } else {
2445                    Log.v(TAG, "Static IP configuration failed");
2446                    sendMessage(CMD_IP_CONFIG_FAILURE);
2447                }
2448            }
2449         }
2450      @Override
2451      public boolean processMessage(Message message) {
2452          if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
2453
2454          switch(message.what) {
2455              case CMD_IP_CONFIG_SUCCESS:
2456                  mLastSignalLevel = -1; // force update of signal strength
2457                  synchronized (mDhcpInfo) {
2458                      mWifiInfo.setIpAddress(mDhcpInfo.ipAddress);
2459                  }
2460                  configureLinkProperties();
2461                  if (getNetworkDetailedState() == DetailedState.CONNECTED) {
2462                      sendLinkConfigurationChangedBroadcast();
2463                  } else {
2464                      setNetworkDetailedState(DetailedState.CONNECTED);
2465                      sendNetworkStateChangeBroadcast(mLastBssid);
2466                  }
2467                  //TODO: The framework is not detecting a DHCP renewal and a possible
2468                  //IP change. we should detect this and send out a config change broadcast
2469                  transitionTo(mConnectedState);
2470                  break;
2471              case CMD_IP_CONFIG_FAILURE:
2472                  mWifiInfo.setIpAddress(0);
2473
2474                  Log.e(TAG, "IP configuration failed");
2475                  /**
2476                   * If we've exceeded the maximum number of retries for DHCP
2477                   * to a given network, disable the network
2478                   */
2479                  if (++mReconnectCount > getMaxDhcpRetries()) {
2480                      Log.e(TAG, "Failed " +
2481                              mReconnectCount + " times, Disabling " + mLastNetworkId);
2482                      WifiConfigStore.disableNetwork(mLastNetworkId);
2483                      mReconnectCount = 0;
2484                  }
2485
2486                  /* DHCP times out after about 30 seconds, we do a
2487                   * disconnect and an immediate reconnect to try again
2488                   */
2489                  WifiNative.disconnectCommand();
2490                  WifiNative.reconnectCommand();
2491                  transitionTo(mDisconnectingState);
2492                  break;
2493              case CMD_DISCONNECT:
2494                  WifiNative.disconnectCommand();
2495                  transitionTo(mDisconnectingState);
2496                  break;
2497                  /* Ignore connection to same network */
2498              case CMD_CONNECT_NETWORK:
2499                  int netId = message.arg1;
2500                  if (mWifiInfo.getNetworkId() == netId) {
2501                      break;
2502                  }
2503                  return NOT_HANDLED;
2504              case CMD_SAVE_NETWORK:
2505                  deferMessage(message);
2506                  break;
2507                  /* Ignore */
2508              case NETWORK_CONNECTION_EVENT:
2509                  break;
2510              case CMD_STOP_DRIVER:
2511                  sendMessage(CMD_DISCONNECT);
2512                  deferMessage(message);
2513                  break;
2514              case CMD_SET_SCAN_MODE:
2515                  if (message.arg1 == SCAN_ONLY_MODE) {
2516                      sendMessage(CMD_DISCONNECT);
2517                      deferMessage(message);
2518                  }
2519                  break;
2520                  /* Defer scan when IP is being fetched */
2521              case CMD_START_SCAN:
2522                  deferMessage(message);
2523                  break;
2524                  /* Defer any power mode changes since we must keep active power mode at DHCP */
2525              case CMD_SET_HIGH_PERF_MODE:
2526                  deferMessage(message);
2527                  break;
2528              default:
2529                return NOT_HANDLED;
2530          }
2531          EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
2532          return HANDLED;
2533      }
2534
2535      @Override
2536      public void exit() {
2537          /* reset power state & bluetooth coexistence if on DHCP */
2538          if (!mUseStaticIp) {
2539              if (mPowerMode != POWER_MODE_ACTIVE) {
2540                  WifiNative.setPowerModeCommand(mPowerMode);
2541              }
2542
2543              if (mModifiedBluetoothCoexistenceMode) {
2544                  // Set the coexistence mode back to its default value
2545                  WifiNative.setBluetoothCoexistenceModeCommand(
2546                          WifiNative.BLUETOOTH_COEXISTENCE_MODE_SENSE);
2547              }
2548          }
2549
2550      }
2551    }
2552
2553    class ConnectedState extends HierarchicalState {
2554        @Override
2555        public void enter() {
2556            if (DBG) Log.d(TAG, getName() + "\n");
2557            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
2558            mRssiPollToken++;
2559            if (mEnableRssiPolling) {
2560                sendMessage(obtainMessage(WifiStateMachine.CMD_RSSI_POLL, mRssiPollToken, 0));
2561            }
2562        }
2563        @Override
2564        public boolean processMessage(Message message) {
2565            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
2566            boolean eventLoggingEnabled = true;
2567            switch (message.what) {
2568                case CMD_DISCONNECT:
2569                    WifiNative.disconnectCommand();
2570                    transitionTo(mDisconnectingState);
2571                    break;
2572                case CMD_STOP_DRIVER:
2573                    sendMessage(CMD_DISCONNECT);
2574                    deferMessage(message);
2575                    break;
2576                case CMD_REQUEST_CM_WAKELOCK:
2577                    if (mCm == null) {
2578                        mCm = (ConnectivityManager)mContext.getSystemService(
2579                                Context.CONNECTIVITY_SERVICE);
2580                    }
2581                    mCm.requestNetworkTransitionWakelock(TAG);
2582                    break;
2583                case CMD_SET_SCAN_MODE:
2584                    if (message.arg1 == SCAN_ONLY_MODE) {
2585                        sendMessage(CMD_DISCONNECT);
2586                        deferMessage(message);
2587                    }
2588                    break;
2589                case CMD_START_SCAN:
2590                    eventLoggingEnabled = false;
2591                    /* When the network is connected, re-scanning can trigger
2592                     * a reconnection. Put it in scan-only mode during scan.
2593                     * When scan results are received, the mode is switched
2594                     * back to CONNECT_MODE.
2595                     */
2596                    WifiNative.setScanResultHandlingCommand(SCAN_ONLY_MODE);
2597                    WifiNative.scanCommand(message.arg1 == SCAN_ACTIVE);
2598                    break;
2599                    /* Ignore connection to same network */
2600                case CMD_CONNECT_NETWORK:
2601                    int netId = message.arg1;
2602                    if (mWifiInfo.getNetworkId() == netId) {
2603                        break;
2604                    }
2605                    return NOT_HANDLED;
2606                case CMD_SAVE_NETWORK:
2607                    WifiConfiguration config = (WifiConfiguration) message.obj;
2608                    NetworkUpdateResult result = WifiConfigStore.saveNetwork(config);
2609                    if (mWifiInfo.getNetworkId() == result.getNetworkId()) {
2610                        if (result.hasIpChanged()) {
2611                            Log.d(TAG,"Reconfiguring IP on connection");
2612                            NetworkUtils.resetConnections(mInterfaceName);
2613                            transitionTo(mConnectingState);
2614                        }
2615                        if (result.hasProxyChanged()) {
2616                            Log.d(TAG,"Reconfiguring proxy on connection");
2617                            configureLinkProperties();
2618                            sendLinkConfigurationChangedBroadcast();
2619                        }
2620                    }
2621                    break;
2622                    /* Ignore */
2623                case NETWORK_CONNECTION_EVENT:
2624                    break;
2625                case CMD_RSSI_POLL:
2626                    eventLoggingEnabled = false;
2627                    if (message.arg1 == mRssiPollToken) {
2628                        // Get Info and continue polling
2629                        fetchRssiAndLinkSpeedNative();
2630                        sendMessageDelayed(obtainMessage(WifiStateMachine.CMD_RSSI_POLL,
2631                                mRssiPollToken, 0), POLL_RSSI_INTERVAL_MSECS);
2632                    } else {
2633                        // Polling has completed
2634                    }
2635                    break;
2636                case CMD_ENABLE_RSSI_POLL:
2637                    mEnableRssiPolling = (message.arg1 == 1);
2638                    mRssiPollToken++;
2639                    if (mEnableRssiPolling) {
2640                        // first poll
2641                        fetchRssiAndLinkSpeedNative();
2642                        sendMessageDelayed(obtainMessage(WifiStateMachine.CMD_RSSI_POLL,
2643                                mRssiPollToken, 0), POLL_RSSI_INTERVAL_MSECS);
2644                    }
2645                    break;
2646                default:
2647                    return NOT_HANDLED;
2648            }
2649            if (eventLoggingEnabled) {
2650                EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
2651            }
2652            return HANDLED;
2653        }
2654    }
2655
2656    class DisconnectingState extends HierarchicalState {
2657        @Override
2658        public void enter() {
2659            if (DBG) Log.d(TAG, getName() + "\n");
2660            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
2661        }
2662        @Override
2663        public boolean processMessage(Message message) {
2664            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
2665            switch (message.what) {
2666                case CMD_STOP_DRIVER: /* Stop driver only after disconnect handled */
2667                    deferMessage(message);
2668                    break;
2669                case CMD_SET_SCAN_MODE:
2670                    if (message.arg1 == SCAN_ONLY_MODE) {
2671                        deferMessage(message);
2672                    }
2673                    break;
2674                    /* Handle in  DisconnectedState */
2675                case SUPPLICANT_STATE_CHANGE_EVENT:
2676                    deferMessage(message);
2677                    break;
2678                default:
2679                    return NOT_HANDLED;
2680            }
2681            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
2682            return HANDLED;
2683        }
2684    }
2685
2686    class DisconnectedState extends HierarchicalState {
2687        @Override
2688        public void enter() {
2689            if (DBG) Log.d(TAG, getName() + "\n");
2690            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
2691
2692            /**
2693             * In a disconnected state, an infrequent scan that wakes
2694             * up the device is needed to ensure a user connects to
2695             * an access point on the move
2696             */
2697            long scanMs = Settings.Secure.getLong(mContext.getContentResolver(),
2698                    Settings.Secure.WIFI_SCAN_INTERVAL_MS, DEFAULT_SCAN_INTERVAL_MS);
2699
2700            mAlarmManager.setRepeating(AlarmManager.RTC_WAKEUP,
2701                    System.currentTimeMillis() + scanMs, scanMs, mScanIntent);
2702        }
2703        @Override
2704        public boolean processMessage(Message message) {
2705            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
2706            switch (message.what) {
2707                case CMD_SET_SCAN_MODE:
2708                    if (message.arg1 == SCAN_ONLY_MODE) {
2709                        WifiNative.setScanResultHandlingCommand(message.arg1);
2710                        //Supplicant disconnect to prevent further connects
2711                        WifiNative.disconnectCommand();
2712                        mIsScanMode = true;
2713                        transitionTo(mScanModeState);
2714                    }
2715                    break;
2716                    /* Ignore network disconnect */
2717                case NETWORK_DISCONNECTION_EVENT:
2718                    break;
2719                case SUPPLICANT_STATE_CHANGE_EVENT:
2720                    StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
2721                    setNetworkDetailedState(WifiInfo.getDetailedStateOf(stateChangeResult.state));
2722                    /* DriverStartedState does the rest of the handling */
2723                    return NOT_HANDLED;
2724                default:
2725                    return NOT_HANDLED;
2726            }
2727            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
2728            return HANDLED;
2729        }
2730
2731        @Override
2732        public void exit() {
2733            mAlarmManager.cancel(mScanIntent);
2734        }
2735    }
2736
2737    class WaitForWpsCompletionState extends HierarchicalState {
2738        @Override
2739        public void enter() {
2740            if (DBG) Log.d(TAG, getName() + "\n");
2741            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
2742        }
2743        @Override
2744        public boolean processMessage(Message message) {
2745            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
2746            switch (message.what) {
2747                /* Defer all commands that can cause connections to a different network
2748                 * or put the state machine out of connect mode
2749                 */
2750                case CMD_STOP_DRIVER:
2751                case CMD_SET_SCAN_MODE:
2752                case CMD_CONNECT_NETWORK:
2753                case CMD_ENABLE_NETWORK:
2754                case CMD_RECONNECT:
2755                case CMD_REASSOCIATE:
2756                case NETWORK_CONNECTION_EVENT: /* Handled after IP & proxy update */
2757                    deferMessage(message);
2758                    break;
2759                case NETWORK_DISCONNECTION_EVENT:
2760                    Log.d(TAG,"Network connection lost");
2761                    handleNetworkDisconnect();
2762                    break;
2763                case WPS_COMPLETED_EVENT:
2764                    /* we are still disconnected until we see a network connection
2765                     * notification */
2766                    transitionTo(mDisconnectedState);
2767                    break;
2768                default:
2769                    return NOT_HANDLED;
2770            }
2771            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
2772            return HANDLED;
2773        }
2774    }
2775
2776    class SoftApStartedState extends HierarchicalState {
2777        @Override
2778        public void enter() {
2779            if (DBG) Log.d(TAG, getName() + "\n");
2780            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
2781        }
2782        @Override
2783        public boolean processMessage(Message message) {
2784            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
2785            switch(message.what) {
2786                case CMD_STOP_AP:
2787                    Log.d(TAG,"Stopping Soft AP");
2788                    setWifiApState(WIFI_AP_STATE_DISABLING);
2789                    try {
2790                        nwService.stopAccessPoint();
2791                    } catch(Exception e) {
2792                        Log.e(TAG, "Exception in stopAccessPoint()");
2793                    }
2794                    transitionTo(mDriverLoadedState);
2795                    break;
2796                case CMD_START_AP:
2797                    Log.d(TAG,"SoftAP set on a running access point");
2798                    try {
2799                        nwService.setAccessPoint((WifiConfiguration) message.obj,
2800                                    mInterfaceName,
2801                                    SOFTAP_IFACE);
2802                    } catch(Exception e) {
2803                        Log.e(TAG, "Exception in nwService during soft AP set");
2804                        try {
2805                            nwService.stopAccessPoint();
2806                        } catch (Exception ee) {
2807                            Slog.e(TAG, "Could not stop AP, :" + ee);
2808                        }
2809                        sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_AP_STATE_FAILED, 0));
2810                    }
2811                    break;
2812                /* Fail client mode operation when soft AP is enabled */
2813                case CMD_START_SUPPLICANT:
2814                    Log.e(TAG,"Cannot start supplicant with a running soft AP");
2815                    setWifiState(WIFI_STATE_UNKNOWN);
2816                    break;
2817                default:
2818                    return NOT_HANDLED;
2819            }
2820            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
2821            return HANDLED;
2822        }
2823    }
2824}
2825