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