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