WifiStateTracker.java revision f897b443fed5c88c6b12e3fa1da72f231db67673
1/*
2 * Copyright (C) 2008 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
25import android.app.ActivityManagerNative;
26import android.net.NetworkInfo;
27import android.net.NetworkStateTracker;
28import android.net.DhcpInfo;
29import android.net.NetworkUtils;
30import android.net.ConnectivityManager;
31import android.net.NetworkInfo.DetailedState;
32import android.net.NetworkInfo.State;
33import android.os.Message;
34import android.os.Parcelable;
35import android.os.Handler;
36import android.os.HandlerThread;
37import android.os.SystemProperties;
38import android.os.Looper;
39import android.os.RemoteException;
40import android.os.ServiceManager;
41import android.provider.Settings;
42import android.text.TextUtils;
43import android.util.EventLog;
44import android.util.Log;
45import android.util.Config;
46import android.app.Notification;
47import android.app.PendingIntent;
48import android.bluetooth.BluetoothDevice;
49import android.bluetooth.BluetoothHeadset;
50import android.bluetooth.BluetoothA2dp;
51import android.content.ContentResolver;
52import android.content.Intent;
53import android.content.Context;
54import android.database.ContentObserver;
55import com.android.internal.app.IBatteryStats;
56
57import java.net.UnknownHostException;
58import java.util.ArrayList;
59import java.util.List;
60import java.util.Set;
61import java.util.concurrent.atomic.AtomicInteger;
62
63/**
64 * Track the state of Wifi connectivity. All event handling is done here,
65 * and all changes in connectivity state are initiated here.
66 *
67 * @hide
68 */
69public class WifiStateTracker extends NetworkStateTracker {
70
71    private static final boolean LOCAL_LOGD = Config.LOGD || false;
72
73    private static final String TAG = "WifiStateTracker";
74
75    // Event log tags (must be in sync with event-log-tags)
76    private static final int EVENTLOG_NETWORK_STATE_CHANGED = 50021;
77    private static final int EVENTLOG_SUPPLICANT_STATE_CHANGED = 50022;
78    private static final int EVENTLOG_DRIVER_STATE_CHANGED = 50023;
79    private static final int EVENTLOG_INTERFACE_CONFIGURATION_STATE_CHANGED = 50024;
80    private static final int EVENTLOG_SUPPLICANT_CONNECTION_STATE_CHANGED = 50025;
81
82    // Event codes
83    private static final int EVENT_SUPPLICANT_CONNECTION             = 1;
84    private static final int EVENT_SUPPLICANT_DISCONNECT             = 2;
85    private static final int EVENT_SUPPLICANT_STATE_CHANGED          = 3;
86    private static final int EVENT_NETWORK_STATE_CHANGED             = 4;
87    private static final int EVENT_SCAN_RESULTS_AVAILABLE            = 5;
88    private static final int EVENT_INTERFACE_CONFIGURATION_SUCCEEDED = 6;
89    private static final int EVENT_INTERFACE_CONFIGURATION_FAILED    = 7;
90    private static final int EVENT_POLL_INTERVAL                     = 8;
91    private static final int EVENT_DHCP_START                        = 9;
92    private static final int EVENT_DEFERRED_DISCONNECT               = 10;
93    private static final int EVENT_DEFERRED_RECONNECT                = 11;
94    /**
95     * The driver is started or stopped. The object will be the state: true for
96     * started, false for stopped.
97     */
98    private static final int EVENT_DRIVER_STATE_CHANGED              = 12;
99    private static final int EVENT_PASSWORD_KEY_MAY_BE_INCORRECT     = 13;
100    private static final int EVENT_MAYBE_START_SCAN_POST_DISCONNECT  = 14;
101
102    /**
103     * The driver state indication.
104     */
105    private static final int DRIVER_STARTED                          = 0;
106    private static final int DRIVER_STOPPED                          = 1;
107    private static final int DRIVER_HUNG                             = 2;
108
109    /**
110     * Interval in milliseconds between polling for connection
111     * status items that are not sent via asynchronous events.
112     * An example is RSSI (signal strength).
113     */
114    private static final int POLL_STATUS_INTERVAL_MSECS = 3000;
115
116    /**
117     * The max number of the WPA supplicant loop iterations before we
118     * decide that the loop should be terminated:
119     */
120    private static final int MAX_SUPPLICANT_LOOP_ITERATIONS = 4;
121
122    /**
123     * When a DISCONNECT event is received, we defer handling it to
124     * allow for the possibility that the DISCONNECT is about to
125     * be followed shortly by a CONNECT to the same network we were
126     * just connected to. In such a case, we don't want to report
127     * the network as down, nor do we want to reconfigure the network
128     * interface, etc. If we get a CONNECT event for another network
129     * within the delay window, we immediately handle the pending
130     * disconnect before processing the CONNECT.<p/>
131     * The five second delay is chosen somewhat arbitrarily, but is
132     * meant to cover most of the cases where a DISCONNECT/CONNECT
133     * happens to a network.
134     */
135    private static final int DISCONNECT_DELAY_MSECS = 5000;
136    /**
137     * When the supplicant goes idle after we do an explicit disconnect
138     * following a DHCP failure, we need to kick the supplicant into
139     * trying to associate with access points.
140     */
141    private static final int RECONNECT_DELAY_MSECS = 2000;
142
143    /**
144     * When the supplicant disconnects from an AP it sometimes forgets
145     * to restart scanning.  Wait this delay before asking it to start
146     * scanning (in case it forgot).  15 sec is the standard delay between
147     * scans.
148     */
149    private static final int KICKSTART_SCANNING_DELAY_MSECS = 15000;
150
151    /**
152     * The maximum number of times we will retry a connection to an access point
153     * for which we have failed in acquiring an IP address from DHCP. A value of
154     * N means that we will make N+1 connection attempts in all.
155     * <p>
156     * See {@link Settings.Secure#WIFI_MAX_DHCP_RETRY_COUNT}. This is the default
157     * value if a Settings value is not present.
158     */
159    private static final int DEFAULT_MAX_DHCP_RETRIES = 9;
160
161    private static final int DRIVER_POWER_MODE_AUTO = 0;
162    private static final int DRIVER_POWER_MODE_ACTIVE = 1;
163
164    /**
165     * The current WPA supplicant loop state (used to detect looping behavior):
166     */
167    private SupplicantState mSupplicantLoopState = SupplicantState.DISCONNECTED;
168
169    /**
170     * The current number of WPA supplicant loop iterations:
171     */
172    private int mNumSupplicantLoopIterations = 0;
173
174    /**
175     * The current number of supplicant state changes.  This is used to determine
176     * if we've received any new info since we found out it was DISCONNECTED or
177     * INACTIVE.  If we haven't for X ms, we then request a scan - it should have
178     * done that automatically, but sometimes some firmware does not.
179     */
180    private int mNumSupplicantStateChanges = 0;
181
182    /**
183     * True if we received an event that that a password-key may be incorrect.
184     * If the next incoming supplicant state change event is DISCONNECT,
185     * broadcast a message that we have a possible password error and disable
186     * the network.
187     */
188    private boolean mPasswordKeyMayBeIncorrect = false;
189
190    public static final int SUPPL_SCAN_HANDLING_NORMAL = 1;
191    public static final int SUPPL_SCAN_HANDLING_LIST_ONLY = 2;
192
193    private WifiMonitor mWifiMonitor;
194    private WifiInfo mWifiInfo;
195    private List<ScanResult> mScanResults;
196    private WifiManager mWM;
197    private boolean mHaveIpAddress;
198    private boolean mObtainingIpAddress;
199    private boolean mTornDownByConnMgr;
200    /**
201     * A DISCONNECT event has been received, but processing it
202     * is being deferred.
203     */
204    private boolean mDisconnectPending;
205    /**
206     * An operation has been performed as a result of which we expect the next event
207     * will be a DISCONNECT.
208     */
209    private boolean mDisconnectExpected;
210    private DhcpHandler mDhcpTarget;
211    private DhcpInfo mDhcpInfo;
212    private int mLastSignalLevel = -1;
213    private String mLastBssid;
214    private String mLastSsid;
215    private int mLastNetworkId = -1;
216    private boolean mUseStaticIp = false;
217    private int mReconnectCount;
218
219    // used to store the (non-persisted) num determined during device boot
220    // (from mcc or other phone info) before the driver is started.
221    private int mNumAllowedChannels = 0;
222
223    // Variables relating to the 'available networks' notification
224
225    /**
226     * The icon to show in the 'available networks' notification. This will also
227     * be the ID of the Notification given to the NotificationManager.
228     */
229    private static final int ICON_NETWORKS_AVAILABLE =
230            com.android.internal.R.drawable.stat_notify_wifi_in_range;
231    /**
232     * When a notification is shown, we wait this amount before possibly showing it again.
233     */
234    private final long NOTIFICATION_REPEAT_DELAY_MS;
235    /**
236     * Whether the user has set the setting to show the 'available networks' notification.
237     */
238    private boolean mNotificationEnabled;
239    /**
240     * Observes the user setting to keep {@link #mNotificationEnabled} in sync.
241     */
242    private NotificationEnabledSettingObserver mNotificationEnabledSettingObserver;
243    /**
244     * The {@link System#currentTimeMillis()} must be at least this value for us
245     * to show the notification again.
246     */
247    private long mNotificationRepeatTime;
248    /**
249     * The Notification object given to the NotificationManager.
250     */
251    private Notification mNotification;
252    /**
253     * Whether the notification is being shown, as set by us. That is, if the
254     * user cancels the notification, we will not receive the callback so this
255     * will still be true. We only guarantee if this is false, then the
256     * notification is not showing.
257     */
258    private boolean mNotificationShown;
259    /**
260     * The number of continuous scans that must occur before consider the
261     * supplicant in a scanning state. This allows supplicant to associate with
262     * remembered networks that are in the scan results.
263     */
264    private static final int NUM_SCANS_BEFORE_ACTUALLY_SCANNING = 3;
265    /**
266     * The number of scans since the last network state change. When this
267     * exceeds {@link #NUM_SCANS_BEFORE_ACTUALLY_SCANNING}, we consider the
268     * supplicant to actually be scanning. When the network state changes to
269     * something other than scanning, we reset this to 0.
270     */
271    private int mNumScansSinceNetworkStateChange;
272    /**
273     * Observes the static IP address settings.
274     */
275    private SettingsObserver mSettingsObserver;
276
277    private boolean mIsScanModeActive;
278    private boolean mEnableRssiPolling;
279
280    /**
281     * One of  {@link WifiManager#WIFI_STATE_DISABLED},
282     *         {@link WifiManager#WIFI_STATE_DISABLING},
283     *         {@link WifiManager#WIFI_STATE_ENABLED},
284     *         {@link WifiManager#WIFI_STATE_ENABLING},
285     *         {@link WifiManager#WIFI_STATE_UNKNOWN}
286     *
287     * getWifiState() is not synchronized to make sure it's always fast,
288     * even when the instance lock is held on other slow operations.
289     * Use a atomic variable for state.
290     */
291    private final AtomicInteger mWifiState = new AtomicInteger(WIFI_STATE_UNKNOWN);
292
293    // Wi-Fi run states:
294    private static final int RUN_STATE_STARTING = 1;
295    private static final int RUN_STATE_RUNNING  = 2;
296    private static final int RUN_STATE_STOPPING = 3;
297    private static final int RUN_STATE_STOPPED  = 4;
298
299    private static final String mRunStateNames[] = {
300            "Starting",
301            "Running",
302            "Stopping",
303            "Stopped"
304    };
305    private int mRunState;
306
307    private final IBatteryStats mBatteryStats;
308
309    private boolean mIsScanOnly;
310
311    private BluetoothA2dp mBluetoothA2dp;
312
313    private String mInterfaceName;
314    private static String LS = System.getProperty("line.separator");
315
316    private static String[] sDnsPropNames;
317
318    /**
319     * A structure for supplying information about a supplicant state
320     * change in the STATE_CHANGE event message that comes from the
321     * WifiMonitor
322     * thread.
323     */
324    private static class SupplicantStateChangeResult {
325        SupplicantStateChangeResult(int networkId, String BSSID, SupplicantState state) {
326            this.state = state;
327            this.BSSID = BSSID;
328            this.networkId = networkId;
329        }
330        int networkId;
331        String BSSID;
332        SupplicantState state;
333    }
334
335    /**
336     * A structure for supplying information about a connection in
337     * the CONNECTED event message that comes from the WifiMonitor
338     * thread.
339     */
340    private static class NetworkStateChangeResult {
341        NetworkStateChangeResult(DetailedState state, String BSSID, int networkId) {
342            this.state = state;
343            this.BSSID = BSSID;
344            this.networkId = networkId;
345        }
346        DetailedState state;
347        String BSSID;
348        int networkId;
349    }
350
351    public WifiStateTracker(Context context, Handler target) {
352        super(context, target, ConnectivityManager.TYPE_WIFI, 0, "WIFI", "");
353
354        mWifiInfo = new WifiInfo();
355        mWifiMonitor = new WifiMonitor(this);
356        mHaveIpAddress = false;
357        mObtainingIpAddress = false;
358        setTornDownByConnMgr(false);
359        mDisconnectPending = false;
360        mScanResults = new ArrayList<ScanResult>();
361        // Allocate DHCP info object once, and fill it in on each request
362        mDhcpInfo = new DhcpInfo();
363        mRunState = RUN_STATE_STARTING;
364
365        // Setting is in seconds
366        NOTIFICATION_REPEAT_DELAY_MS = Settings.Secure.getInt(context.getContentResolver(),
367                Settings.Secure.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY, 900) * 1000l;
368        mNotificationEnabledSettingObserver = new NotificationEnabledSettingObserver(new Handler());
369        mNotificationEnabledSettingObserver.register();
370
371        mSettingsObserver = new SettingsObserver(new Handler());
372
373        mInterfaceName = SystemProperties.get("wifi.interface", "tiwlan0");
374        sDnsPropNames = new String[] {
375            "dhcp." + mInterfaceName + ".dns1",
376            "dhcp." + mInterfaceName + ".dns2"
377        };
378        mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo"));
379
380    }
381
382    /**
383     * Helper method: sets the supplicant state and keeps the network
384     * info updated.
385     * @param state the new state
386     */
387    private void setSupplicantState(SupplicantState state) {
388        mWifiInfo.setSupplicantState(state);
389        updateNetworkInfo();
390        checkPollTimer();
391    }
392
393    public SupplicantState getSupplicantState() {
394        return mWifiInfo.getSupplicantState();
395    }
396
397    /**
398     * Helper method: sets the supplicant state and keeps the network
399     * info updated (string version).
400     * @param stateName the string name of the new state
401     */
402    private void setSupplicantState(String stateName) {
403        mWifiInfo.setSupplicantState(stateName);
404        updateNetworkInfo();
405        checkPollTimer();
406    }
407
408    /**
409     * Helper method: sets the boolean indicating that the connection
410     * manager asked the network to be torn down (and so only the connection
411     * manager can set it up again).
412     * network info updated.
413     * @param flag {@code true} if explicitly disabled.
414     */
415    private void setTornDownByConnMgr(boolean flag) {
416        mTornDownByConnMgr = flag;
417        updateNetworkInfo();
418    }
419
420    /**
421     * Return the IP addresses of the DNS servers available for the WLAN
422     * network interface.
423     * @return a list of DNS addresses, with no holes.
424     */
425    public String[] getNameServers() {
426        return getNameServerList(sDnsPropNames);
427    }
428
429    /**
430     * Return the name of our WLAN network interface.
431     * @return the name of our interface.
432     */
433    public String getInterfaceName() {
434        return mInterfaceName;
435    }
436
437    /**
438     * Return the system properties name associated with the tcp buffer sizes
439     * for this network.
440     */
441    public String getTcpBufferSizesPropName() {
442        return "net.tcp.buffersize.wifi";
443    }
444
445    public void startMonitoring() {
446        /*
447         * Get a handle on the WifiManager. This cannot be done in our
448         * constructor, because the Wifi service is not yet registered.
449         */
450        mWM = (WifiManager)mContext.getSystemService(Context.WIFI_SERVICE);
451    }
452
453    public void startEventLoop() {
454        mWifiMonitor.startMonitoring();
455    }
456
457    /**
458     * Wi-Fi is considered available as long as we have a connection to the
459     * supplicant daemon and there is at least one enabled network. If a teardown
460     * was explicitly requested, then Wi-Fi can be restarted with a reconnect
461     * request, so it is considered available. If the driver has been stopped
462     * for any reason other than a teardown request, Wi-Fi is considered
463     * unavailable.
464     * @return {@code true} if Wi-Fi connections are possible
465     */
466    public synchronized boolean isAvailable() {
467        /*
468         * TODO: Need to also look at scan results to see whether we're
469         * in range of any access points. If we have scan results that
470         * are no more than N seconds old, use those, otherwise, initiate
471         * a scan and wait for the results. This only matters if we
472         * allow mobile to be the preferred network.
473         */
474        SupplicantState suppState = mWifiInfo.getSupplicantState();
475        return suppState != SupplicantState.UNINITIALIZED &&
476                suppState != SupplicantState.INACTIVE &&
477                (mTornDownByConnMgr || !isDriverStopped());
478    }
479
480    /**
481     * {@inheritDoc}
482     * There are currently no defined Wi-Fi subtypes.
483     */
484    public int getNetworkSubtype() {
485        return 0;
486    }
487
488    /**
489     * Helper method: updates the network info object to keep it in sync with
490     * the Wi-Fi state tracker.
491     */
492    private void updateNetworkInfo() {
493        mNetworkInfo.setIsAvailable(isAvailable());
494    }
495
496    /**
497     * Report whether the Wi-Fi connection is fully configured for data.
498     * @return {@code true} if the {@link SupplicantState} is
499     * {@link android.net.wifi.SupplicantState#COMPLETED COMPLETED}.
500     */
501    public boolean isConnectionCompleted() {
502        return mWifiInfo.getSupplicantState() == SupplicantState.COMPLETED;
503    }
504
505    /**
506     * Report whether the Wi-Fi connection has successfully acquired an IP address.
507     * @return {@code true} if the Wi-Fi connection has been assigned an IP address.
508     */
509    public boolean hasIpAddress() {
510        return mHaveIpAddress;
511    }
512
513    /**
514     * Send the tracker a notification that a user-entered password key
515     * may be incorrect (i.e., caused authentication to fail).
516     */
517    void notifyPasswordKeyMayBeIncorrect() {
518        sendEmptyMessage(EVENT_PASSWORD_KEY_MAY_BE_INCORRECT);
519    }
520
521    /**
522     * Send the tracker a notification that a connection to the supplicant
523     * daemon has been established.
524     */
525    void notifySupplicantConnection() {
526        sendEmptyMessage(EVENT_SUPPLICANT_CONNECTION);
527    }
528
529    /**
530     * Send the tracker a notification that the state of the supplicant
531     * has changed.
532     * @param networkId the configured network on which the state change occurred
533     * @param newState the new {@code SupplicantState}
534     */
535    void notifyStateChange(int networkId, String BSSID, SupplicantState newState) {
536        Message msg = Message.obtain(
537            this, EVENT_SUPPLICANT_STATE_CHANGED,
538            new SupplicantStateChangeResult(networkId, BSSID, newState));
539        msg.sendToTarget();
540    }
541
542    /**
543     * Send the tracker a notification that the state of Wifi connectivity
544     * has changed.
545     * @param networkId the configured network on which the state change occurred
546     * @param newState the new network state
547     * @param BSSID when the new state is {@link DetailedState#CONNECTED
548     * NetworkInfo.DetailedState.CONNECTED},
549     * this is the MAC address of the access point. Otherwise, it
550     * is {@code null}.
551     */
552    void notifyStateChange(DetailedState newState, String BSSID, int networkId) {
553        Message msg = Message.obtain(
554            this, EVENT_NETWORK_STATE_CHANGED,
555            new NetworkStateChangeResult(newState, BSSID, networkId));
556        msg.sendToTarget();
557    }
558
559    /**
560     * Send the tracker a notification that a scan has completed, and results
561     * are available.
562     */
563    void notifyScanResultsAvailable() {
564        // reset the supplicant's handling of scan results to "normal" mode
565        setScanResultHandling(SUPPL_SCAN_HANDLING_NORMAL);
566        sendEmptyMessage(EVENT_SCAN_RESULTS_AVAILABLE);
567    }
568
569    /**
570     * Send the tracker a notification that we can no longer communicate with
571     * the supplicant daemon.
572     */
573    void notifySupplicantLost() {
574        sendEmptyMessage(EVENT_SUPPLICANT_DISCONNECT);
575    }
576
577    /**
578     * Send the tracker a notification that the Wi-Fi driver has been stopped.
579     */
580    void notifyDriverStopped() {
581        mRunState = RUN_STATE_STOPPED;
582
583        // Send a driver stopped message to our handler
584        Message.obtain(this, EVENT_DRIVER_STATE_CHANGED, DRIVER_STOPPED, 0).sendToTarget();
585    }
586
587    /**
588     * Send the tracker a notification that the Wi-Fi driver has been restarted after
589     * having been stopped.
590     */
591    void notifyDriverStarted() {
592        // Send a driver started message to our handler
593        Message.obtain(this, EVENT_DRIVER_STATE_CHANGED, DRIVER_STARTED, 0).sendToTarget();
594    }
595
596    /**
597     * Send the tracker a notification that the Wi-Fi driver has hung and needs restarting.
598     */
599    void notifyDriverHung() {
600        // Send a driver hanged message to our handler
601        Message.obtain(this, EVENT_DRIVER_STATE_CHANGED, DRIVER_HUNG, 0).sendToTarget();
602    }
603
604    /**
605     * Set the interval timer for polling connection information
606     * that is not delivered asynchronously.
607     */
608    private synchronized void checkPollTimer() {
609        if (mEnableRssiPolling &&
610                mWifiInfo.getSupplicantState() == SupplicantState.COMPLETED &&
611                !hasMessages(EVENT_POLL_INTERVAL)) {
612            sendEmptyMessageDelayed(EVENT_POLL_INTERVAL, POLL_STATUS_INTERVAL_MSECS);
613        }
614    }
615
616    /**
617     * TODO: mRunState is not synchronized in some places
618     * address this as part of re-architect.
619     *
620     * TODO: We are exposing an additional public synchronized call
621     * for a wakelock optimization in WifiService. Remove it
622     * when we handle the wakelock in ConnectivityService.
623     */
624    public synchronized boolean isDriverStopped() {
625        return mRunState == RUN_STATE_STOPPED || mRunState == RUN_STATE_STOPPING;
626    }
627
628    private void noteRunState() {
629        try {
630            if (mRunState == RUN_STATE_RUNNING) {
631                mBatteryStats.noteWifiRunning();
632            } else if (mRunState == RUN_STATE_STOPPED) {
633                mBatteryStats.noteWifiStopped();
634            }
635        } catch (RemoteException ignore) {
636        }
637    }
638
639    /**
640     * Set the run state to either "normal" or "scan-only".
641     * @param scanOnlyMode true if the new mode should be scan-only.
642     */
643    public synchronized void setScanOnlyMode(boolean scanOnlyMode) {
644        // do nothing unless scan-only mode is changing
645        if (mIsScanOnly != scanOnlyMode) {
646            int scanType = (scanOnlyMode ?
647                    SUPPL_SCAN_HANDLING_LIST_ONLY : SUPPL_SCAN_HANDLING_NORMAL);
648            if (LOCAL_LOGD) Log.v(TAG, "Scan-only mode changing to " + scanOnlyMode + " scanType=" + scanType);
649            if (setScanResultHandling(scanType)) {
650                mIsScanOnly = scanOnlyMode;
651                if (!isDriverStopped()) {
652                    if (scanOnlyMode) {
653                        disconnect();
654                    } else {
655                        reconnectCommand();
656                    }
657                }
658            }
659        }
660    }
661
662
663    private void checkIsBluetoothPlaying() {
664        boolean isBluetoothPlaying = false;
665        Set<BluetoothDevice> connected = mBluetoothA2dp.getConnectedSinks();
666
667        for (BluetoothDevice device : connected) {
668            if (mBluetoothA2dp.getSinkState(device) == BluetoothA2dp.STATE_PLAYING) {
669                isBluetoothPlaying = true;
670                break;
671            }
672        }
673        setBluetoothScanMode(isBluetoothPlaying);
674    }
675
676    public void enableRssiPolling(boolean enable) {
677        if (mEnableRssiPolling != enable) {
678            mEnableRssiPolling = enable;
679            checkPollTimer();
680        }
681    }
682
683    /**
684     * We release the wakelock in WifiService
685     * using a timer.
686     *
687     * TODO:
688     * Releasing wakelock using both timer and
689     * a call from ConnectivityService requires
690     * a rethink. We had problems where WifiService
691     * could keep a wakelock forever if we delete
692     * messages in the asynchronous call
693     * from ConnectivityService
694     */
695    @Override
696    public void releaseWakeLock() {
697    }
698
699    /**
700     * Tracks the WPA supplicant states to detect "loop" situations.
701     * @param newSupplicantState The new WPA supplicant state.
702     * @return {@code true} if the supplicant loop should be stopped
703     * and {@code false} if it should continue.
704     */
705    private boolean isSupplicantLooping(SupplicantState newSupplicantState) {
706        if (SupplicantState.ASSOCIATING.ordinal() <= newSupplicantState.ordinal()
707            && newSupplicantState.ordinal() < SupplicantState.COMPLETED.ordinal()) {
708            if (mSupplicantLoopState != newSupplicantState) {
709                if (newSupplicantState.ordinal() < mSupplicantLoopState.ordinal()) {
710                    ++mNumSupplicantLoopIterations;
711                }
712
713                mSupplicantLoopState = newSupplicantState;
714            }
715        } else if (newSupplicantState == SupplicantState.COMPLETED) {
716            resetSupplicantLoopState();
717        }
718
719        return mNumSupplicantLoopIterations >= MAX_SUPPLICANT_LOOP_ITERATIONS;
720    }
721
722    /**
723     * Resets the WPA supplicant loop state.
724     */
725    private void resetSupplicantLoopState() {
726        mNumSupplicantLoopIterations = 0;
727    }
728
729    @Override
730    public void handleMessage(Message msg) {
731        Intent intent;
732
733        switch (msg.what) {
734            case EVENT_SUPPLICANT_CONNECTION:
735                mRunState = RUN_STATE_RUNNING;
736                noteRunState();
737                checkUseStaticIp();
738                /* Reset notification state on new connection */
739                resetNotificationTimer();
740                /*
741                 * DHCP requests are blocking, so run them in a separate thread.
742                 */
743                HandlerThread dhcpThread = new HandlerThread("DHCP Handler Thread");
744                dhcpThread.start();
745                mDhcpTarget = new DhcpHandler(dhcpThread.getLooper(), this);
746                mIsScanModeActive = true;
747                mTornDownByConnMgr = false;
748                mLastBssid = null;
749                mLastSsid = null;
750                requestConnectionInfo();
751                SupplicantState supplState = mWifiInfo.getSupplicantState();
752                /**
753                 * The MAC address isn't going to change, so just request it
754                 * once here.
755                 */
756                String macaddr = getMacAddress();
757
758                if (macaddr != null) {
759                    mWifiInfo.setMacAddress(macaddr);
760                }
761                if (LOCAL_LOGD) Log.v(TAG, "Connection to supplicant established, state=" +
762                    supplState);
763                // Wi-Fi supplicant connection state changed:
764                // [31- 2] Reserved for future use
765                // [ 1- 0] Connected to supplicant (1), disconnected from supplicant (0) ,
766                //         or supplicant died (2)
767                EventLog.writeEvent(EVENTLOG_SUPPLICANT_CONNECTION_STATE_CHANGED, 1);
768                /*
769                 * The COMPLETED state change from the supplicant may have occurred
770                 * in between polling for supplicant availability, in which case
771                 * we didn't perform a DHCP request to get an IP address.
772                 */
773                if (supplState == SupplicantState.COMPLETED) {
774                    mLastBssid = mWifiInfo.getBSSID();
775                    mLastSsid = mWifiInfo.getSSID();
776                    configureInterface();
777                }
778                if (ActivityManagerNative.isSystemReady()) {
779                    intent = new Intent(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
780                    intent.putExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, true);
781                    mContext.sendBroadcast(intent);
782                }
783                if (supplState == SupplicantState.COMPLETED && mHaveIpAddress) {
784                    setDetailedState(DetailedState.CONNECTED);
785                } else {
786                    setDetailedState(WifiInfo.getDetailedStateOf(supplState));
787                }
788                /*
789                 * Filter out multicast packets. This saves battery power, since
790                 * the CPU doesn't have to spend time processing packets that
791                 * are going to end up being thrown away.
792                 */
793                mWM.initializeMulticastFiltering();
794
795                if (mBluetoothA2dp == null) {
796                    mBluetoothA2dp = new BluetoothA2dp(mContext);
797                }
798                checkIsBluetoothPlaying();
799
800                // initialize this after the supplicant is alive
801                setNumAllowedChannels();
802                break;
803
804            case EVENT_SUPPLICANT_DISCONNECT:
805                mRunState = RUN_STATE_STOPPED;
806                noteRunState();
807                boolean died = mWifiState.get() != WIFI_STATE_DISABLED &&
808                               mWifiState.get() != WIFI_STATE_DISABLING;
809                if (died) {
810                    if (LOCAL_LOGD) Log.v(TAG, "Supplicant died unexpectedly");
811                } else {
812                    if (LOCAL_LOGD) Log.v(TAG, "Connection to supplicant lost");
813                }
814                // Wi-Fi supplicant connection state changed:
815                // [31- 2] Reserved for future use
816                // [ 1- 0] Connected to supplicant (1), disconnected from supplicant (0) ,
817                //         or supplicant died (2)
818                EventLog.writeEvent(EVENTLOG_SUPPLICANT_CONNECTION_STATE_CHANGED, died ? 2 : 0);
819                closeSupplicantConnection();
820
821                if (died) {
822                    resetConnections(true);
823                }
824                // When supplicant dies, kill the DHCP thread
825                if (mDhcpTarget != null) {
826                    mDhcpTarget.getLooper().quit();
827                    mDhcpTarget = null;
828                }
829                mContext.removeStickyBroadcast(new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION));
830                if (ActivityManagerNative.isSystemReady()) {
831                    intent = new Intent(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
832                    intent.putExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, false);
833                    mContext.sendBroadcast(intent);
834                }
835                setDetailedState(DetailedState.DISCONNECTED);
836                setSupplicantState(SupplicantState.UNINITIALIZED);
837                mHaveIpAddress = false;
838                mObtainingIpAddress = false;
839                if (died) {
840                    mWM.setWifiEnabled(false);
841                }
842                break;
843
844            case EVENT_MAYBE_START_SCAN_POST_DISCONNECT:
845                // Only do this if we haven't gotten a new supplicant status since the timer
846                // started
847                if (mNumSupplicantStateChanges == msg.arg1) {
848                    scan(false); // do a passive scan
849                }
850                break;
851
852            case EVENT_SUPPLICANT_STATE_CHANGED:
853                mNumSupplicantStateChanges++;
854                SupplicantStateChangeResult supplicantStateResult =
855                    (SupplicantStateChangeResult) msg.obj;
856                SupplicantState newState = supplicantStateResult.state;
857                SupplicantState currentState = mWifiInfo.getSupplicantState();
858
859                // Wi-Fi supplicant state changed:
860                // [31- 6] Reserved for future use
861                // [ 5- 0] Supplicant state ordinal (as defined by SupplicantState)
862                int eventLogParam = (newState.ordinal() & 0x3f);
863                EventLog.writeEvent(EVENTLOG_SUPPLICANT_STATE_CHANGED, eventLogParam);
864
865                if (LOCAL_LOGD) Log.v(TAG, "Changing supplicant state: "
866                                      + currentState +
867                                      " ==> " + newState);
868
869                int networkId = supplicantStateResult.networkId;
870
871                /**
872                 * The SupplicantState BSSID value is valid in ASSOCIATING state only.
873                 * The NetworkState BSSID value comes upon a successful connection.
874                 */
875                if (supplicantStateResult.state == SupplicantState.ASSOCIATING) {
876                    mLastBssid = supplicantStateResult.BSSID;
877                }
878                /*
879                 * If we get disconnect or inactive we need to start our
880                 * watchdog timer to start a scan
881                 */
882                if (newState == SupplicantState.DISCONNECTED ||
883                        newState == SupplicantState.INACTIVE) {
884                    sendMessageDelayed(obtainMessage(EVENT_MAYBE_START_SCAN_POST_DISCONNECT,
885                            mNumSupplicantStateChanges, 0), KICKSTART_SCANNING_DELAY_MSECS);
886                }
887
888
889                /*
890                 * Did we get to DISCONNECTED state due to an
891                 * authentication (password) failure?
892                 */
893                boolean failedToAuthenticate = false;
894                if (newState == SupplicantState.DISCONNECTED) {
895                    failedToAuthenticate = mPasswordKeyMayBeIncorrect;
896                }
897                mPasswordKeyMayBeIncorrect = false;
898
899                /*
900                 * Keep track of the supplicant state and check if we should
901                 * disable the network
902                 */
903                boolean disabledNetwork = false;
904                if (isSupplicantLooping(newState)) {
905                    if (LOCAL_LOGD) {
906                        Log.v(TAG,
907                              "Stop WPA supplicant loop and disable network");
908                    }
909                    disabledNetwork = wifiManagerDisableNetwork(networkId);
910                }
911
912                if (disabledNetwork) {
913                    /*
914                     * Reset the loop state if we disabled the network
915                     */
916                    resetSupplicantLoopState();
917                } else if (newState != currentState ||
918                        (newState == SupplicantState.DISCONNECTED && isDriverStopped())) {
919                    setSupplicantState(newState);
920                    if (newState == SupplicantState.DORMANT) {
921                        DetailedState newDetailedState;
922                        Message reconnectMsg = obtainMessage(EVENT_DEFERRED_RECONNECT, mLastBssid);
923                        if (mIsScanOnly || mRunState == RUN_STATE_STOPPING) {
924                            newDetailedState = DetailedState.IDLE;
925                        } else {
926                            newDetailedState = DetailedState.FAILED;
927                        }
928                        handleDisconnectedState(newDetailedState, true);
929                        /**
930                         * If we were associated with a network (networkId != -1),
931                         * assume we reached this state because of a failed attempt
932                         * to acquire an IP address, and attempt another connection
933                         * and IP address acquisition in RECONNECT_DELAY_MSECS
934                         * milliseconds.
935                         */
936                        if (mRunState == RUN_STATE_RUNNING && !mIsScanOnly && networkId != -1) {
937                            sendMessageDelayed(reconnectMsg, RECONNECT_DELAY_MSECS);
938                        } else if (mRunState == RUN_STATE_STOPPING) {
939                            stopDriver();
940                        } else if (mRunState == RUN_STATE_STARTING && !mIsScanOnly) {
941                            reconnectCommand();
942                        }
943                    } else if (newState == SupplicantState.DISCONNECTED) {
944                        mHaveIpAddress = false;
945                        if (isDriverStopped() || mDisconnectExpected) {
946                            handleDisconnectedState(DetailedState.DISCONNECTED, true);
947                        } else {
948                            scheduleDisconnect();
949                        }
950                    } else if (newState != SupplicantState.COMPLETED && !mDisconnectPending) {
951                        /**
952                         * Ignore events that don't change the connectivity state,
953                         * such as WPA rekeying operations.
954                         */
955                        if (!(currentState == SupplicantState.COMPLETED &&
956                               (newState == SupplicantState.ASSOCIATING ||
957                                newState == SupplicantState.ASSOCIATED ||
958                                newState == SupplicantState.FOUR_WAY_HANDSHAKE ||
959                                newState == SupplicantState.GROUP_HANDSHAKE))) {
960                            setDetailedState(WifiInfo.getDetailedStateOf(newState));
961                        }
962                    }
963
964                    mDisconnectExpected = false;
965                    intent = new Intent(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
966                    intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
967                            | Intent.FLAG_RECEIVER_REPLACE_PENDING);
968                    intent.putExtra(WifiManager.EXTRA_NEW_STATE, (Parcelable)newState);
969                    if (failedToAuthenticate) {
970                        if (LOCAL_LOGD) Log.d(TAG, "Failed to authenticate, disabling network " + networkId);
971                        wifiManagerDisableNetwork(networkId);
972                        intent.putExtra(
973                            WifiManager.EXTRA_SUPPLICANT_ERROR,
974                            WifiManager.ERROR_AUTHENTICATING);
975                    }
976                    mContext.sendStickyBroadcast(intent);
977                }
978                break;
979
980            case EVENT_NETWORK_STATE_CHANGED:
981                /*
982                 * Each CONNECT or DISCONNECT generates a pair of events.
983                 * One is a supplicant state change event, and the other
984                 * is a network state change event. For connects, the
985                 * supplicant event always arrives first, followed by
986                 * the network state change event. Only the latter event
987                 * has the BSSID, which we are interested in capturing.
988                 * For disconnects, the order is the opposite -- the
989                 * network state change event comes first, followed by
990                 * the supplicant state change event.
991                 */
992                NetworkStateChangeResult result =
993                    (NetworkStateChangeResult) msg.obj;
994
995                // Wi-Fi network state changed:
996                // [31- 6] Reserved for future use
997                // [ 5- 0] Detailed state ordinal (as defined by NetworkInfo.DetailedState)
998                eventLogParam = (result.state.ordinal() & 0x3f);
999                EventLog.writeEvent(EVENTLOG_NETWORK_STATE_CHANGED, eventLogParam);
1000
1001                if (LOCAL_LOGD) Log.v(TAG, "New network state is " + result.state);
1002                /*
1003                 * If we're in scan-only mode, don't advance the state machine, and
1004                 * don't report the state change to clients.
1005                 */
1006                if (mIsScanOnly) {
1007                    if (LOCAL_LOGD) Log.v(TAG, "Dropping event in scan-only mode");
1008                    break;
1009                }
1010                if (result.state != DetailedState.SCANNING) {
1011                    /*
1012                     * Reset the scan count since there was a network state
1013                     * change. This could be from supplicant trying to associate
1014                     * with a network.
1015                     */
1016                    mNumScansSinceNetworkStateChange = 0;
1017                }
1018                /*
1019                 * If the supplicant sent us a CONNECTED event, we don't
1020                 * want to send out an indication of overall network
1021                 * connectivity until we have our IP address. If the
1022                 * supplicant sent us a DISCONNECTED event, we delay
1023                 * sending a notification in case a reconnection to
1024                 * the same access point occurs within a short time.
1025                 */
1026                if (result.state == DetailedState.DISCONNECTED) {
1027                    if (mWifiInfo.getSupplicantState() != SupplicantState.DORMANT) {
1028                        scheduleDisconnect();
1029                    }
1030                    break;
1031                }
1032                requestConnectionStatus(mWifiInfo);
1033                if (!(result.state == DetailedState.CONNECTED &&
1034                        (!mHaveIpAddress || mDisconnectPending))) {
1035                    setDetailedState(result.state);
1036                }
1037
1038                if (result.state == DetailedState.CONNECTED) {
1039                    /*
1040                     * Remove the 'available networks' notification when we
1041                     * successfully connect to a network.
1042                     */
1043                    setNotificationVisible(false, 0, false, 0);
1044                    boolean wasDisconnectPending = mDisconnectPending;
1045                    cancelDisconnect();
1046                    /*
1047                     * The connection is fully configured as far as link-level
1048                     * connectivity is concerned, but we may still need to obtain
1049                     * an IP address.
1050                     */
1051                    if (wasDisconnectPending) {
1052                        DetailedState saveState = getNetworkInfo().getDetailedState();
1053                        handleDisconnectedState(DetailedState.DISCONNECTED, false);
1054                        setDetailedStateInternal(saveState);
1055                    }
1056
1057                    configureInterface();
1058                    mLastBssid = result.BSSID;
1059                    mLastSsid = mWifiInfo.getSSID();
1060                    mLastNetworkId = result.networkId;
1061                    if (mHaveIpAddress) {
1062                        setDetailedState(DetailedState.CONNECTED);
1063                    } else {
1064                        setDetailedState(DetailedState.OBTAINING_IPADDR);
1065                    }
1066                }
1067                sendNetworkStateChangeBroadcast(mWifiInfo.getBSSID());
1068                break;
1069
1070            case EVENT_SCAN_RESULTS_AVAILABLE:
1071                if (ActivityManagerNative.isSystemReady()) {
1072                    mContext.sendBroadcast(new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
1073                }
1074                sendScanResultsAvailable();
1075                /**
1076                 * On receiving the first scan results after connecting to
1077                 * the supplicant, switch scan mode over to passive.
1078                 */
1079                setScanMode(false);
1080                break;
1081
1082            case EVENT_POLL_INTERVAL:
1083                if (mWifiInfo.getSupplicantState() != SupplicantState.UNINITIALIZED) {
1084                    requestPolledInfo(mWifiInfo, true);
1085                    checkPollTimer();
1086                }
1087                break;
1088
1089            case EVENT_DEFERRED_DISCONNECT:
1090                if (mWifiInfo.getSupplicantState() != SupplicantState.UNINITIALIZED) {
1091                    handleDisconnectedState(DetailedState.DISCONNECTED, true);
1092                }
1093                break;
1094
1095            case EVENT_DEFERRED_RECONNECT:
1096                /**
1097                 * mLastBssid can be null when there is a reconnect
1098                 * request on the first BSSID we connect to
1099                 */
1100                String BSSID = (msg.obj != null) ? msg.obj.toString() : null;
1101                /**
1102                 * If we've exceeded the maximum number of retries for reconnecting
1103                 * to a given network, disable the network
1104                 */
1105                if (mWifiInfo.getSupplicantState() != SupplicantState.UNINITIALIZED) {
1106                    if (++mReconnectCount > getMaxDhcpRetries()) {
1107                        if (LOCAL_LOGD) {
1108                            Log.d(TAG, "Failed reconnect count: " +
1109                                    mReconnectCount + " Disabling " + BSSID);
1110                        }
1111                        mWM.disableNetwork(mLastNetworkId);
1112                    }
1113                    reconnectCommand();
1114                }
1115                break;
1116
1117            case EVENT_INTERFACE_CONFIGURATION_SUCCEEDED:
1118                /**
1119                 * Since this event is sent from another thread, it might have been
1120                 * sent after we closed our connection to the supplicant in the course
1121                 * of disabling Wi-Fi. In that case, we should just ignore the event.
1122                 */
1123                if (mWifiInfo.getSupplicantState() == SupplicantState.UNINITIALIZED) {
1124                    break;
1125                }
1126                mReconnectCount = 0;
1127                mHaveIpAddress = true;
1128                mObtainingIpAddress = false;
1129                mWifiInfo.setIpAddress(mDhcpInfo.ipAddress);
1130                mLastSignalLevel = -1; // force update of signal strength
1131                if (mNetworkInfo.getDetailedState() != DetailedState.CONNECTED) {
1132                    setDetailedState(DetailedState.CONNECTED);
1133                    sendNetworkStateChangeBroadcast(mWifiInfo.getBSSID());
1134                } else {
1135                    mTarget.sendEmptyMessage(EVENT_CONFIGURATION_CHANGED);
1136                }
1137                if (LOCAL_LOGD) Log.v(TAG, "IP configuration: " + mDhcpInfo);
1138                // Wi-Fi interface configuration state changed:
1139                // [31- 1] Reserved for future use
1140                // [ 0- 0] Interface configuration succeeded (1) or failed (0)
1141                EventLog.writeEvent(EVENTLOG_INTERFACE_CONFIGURATION_STATE_CHANGED, 1);
1142
1143                // We've connected successfully, so allow the notification again in the future
1144                resetNotificationTimer();
1145                break;
1146
1147            case EVENT_INTERFACE_CONFIGURATION_FAILED:
1148                if (mWifiInfo.getSupplicantState() != SupplicantState.UNINITIALIZED) {
1149                    // Wi-Fi interface configuration state changed:
1150                    // [31- 1] Reserved for future use
1151                    // [ 0- 0] Interface configuration succeeded (1) or failed (0)
1152                    EventLog.writeEvent(EVENTLOG_INTERFACE_CONFIGURATION_STATE_CHANGED, 0);
1153                    mHaveIpAddress = false;
1154                    mWifiInfo.setIpAddress(0);
1155                    mObtainingIpAddress = false;
1156                    disconnect();
1157                }
1158                break;
1159
1160            case EVENT_DRIVER_STATE_CHANGED:
1161                // Wi-Fi driver state changed:
1162                // 0 STARTED
1163                // 1 STOPPED
1164                // 2 HUNG
1165                EventLog.writeEvent(EVENTLOG_DRIVER_STATE_CHANGED, msg.arg1);
1166
1167                switch (msg.arg1) {
1168                case DRIVER_STARTED:
1169                    /**
1170                     * Set the number of allowed radio channels according
1171                     * to the system setting, since it gets reset by the
1172                     * driver upon changing to the STARTED state.
1173                     */
1174                    setNumAllowedChannels();
1175                    synchronized (this) {
1176                        if (mRunState == RUN_STATE_STARTING) {
1177                            mRunState = RUN_STATE_RUNNING;
1178                            if (!mIsScanOnly) {
1179                                reconnectCommand();
1180                            } else {
1181                                // In some situations, supplicant needs to be kickstarted to
1182                                // start the background scanning
1183                                scan(true);
1184                            }
1185                        }
1186                    }
1187                    break;
1188                case DRIVER_HUNG:
1189                    Log.e(TAG, "Wifi Driver reports HUNG - reloading.");
1190                    /**
1191                     * restart the driver - toggle off and on
1192                     */
1193                    mWM.setWifiEnabled(false);
1194                    mWM.setWifiEnabled(true);
1195                    break;
1196                }
1197                noteRunState();
1198                break;
1199
1200            case EVENT_PASSWORD_KEY_MAY_BE_INCORRECT:
1201                mPasswordKeyMayBeIncorrect = true;
1202                break;
1203        }
1204    }
1205
1206    private boolean wifiManagerDisableNetwork(int networkId) {
1207        boolean disabledNetwork = false;
1208        if (0 <= networkId) {
1209            disabledNetwork = mWM.disableNetwork(networkId);
1210            if (LOCAL_LOGD) {
1211                if (disabledNetwork) {
1212                    Log.v(TAG, "Disabled network: " + networkId);
1213                }
1214            }
1215        }
1216        if (LOCAL_LOGD) {
1217            if (!disabledNetwork) {
1218                Log.e(TAG, "Failed to disable network:" +
1219                      " invalid network id: " + networkId);
1220            }
1221        }
1222        return disabledNetwork;
1223    }
1224
1225    private void configureInterface() {
1226        checkPollTimer();
1227        mLastSignalLevel = -1;
1228        if (!mUseStaticIp) {
1229            if (!mHaveIpAddress && !mObtainingIpAddress) {
1230                mObtainingIpAddress = true;
1231                mDhcpTarget.sendEmptyMessage(EVENT_DHCP_START);
1232            }
1233        } else {
1234            int event;
1235            if (NetworkUtils.configureInterface(mInterfaceName, mDhcpInfo)) {
1236                mHaveIpAddress = true;
1237                event = EVENT_INTERFACE_CONFIGURATION_SUCCEEDED;
1238                if (LOCAL_LOGD) Log.v(TAG, "Static IP configuration succeeded");
1239            } else {
1240                mHaveIpAddress = false;
1241                event = EVENT_INTERFACE_CONFIGURATION_FAILED;
1242                if (LOCAL_LOGD) Log.v(TAG, "Static IP configuration failed");
1243            }
1244            sendEmptyMessage(event);
1245        }
1246    }
1247
1248    /**
1249     * Reset our IP state and send out broadcasts following a disconnect.
1250     * @param newState the {@code DetailedState} to set. Should be either
1251     * {@code DISCONNECTED} or {@code FAILED}.
1252     * @param disableInterface indicates whether the interface should
1253     * be disabled
1254     */
1255    private void handleDisconnectedState(DetailedState newState, boolean disableInterface) {
1256        if (mDisconnectPending) {
1257            cancelDisconnect();
1258        }
1259        mDisconnectExpected = false;
1260        resetConnections(disableInterface);
1261        setDetailedState(newState);
1262        sendNetworkStateChangeBroadcast(mLastBssid);
1263        mWifiInfo.setBSSID(null);
1264        mLastBssid = null;
1265        mLastSsid = null;
1266        mDisconnectPending = false;
1267    }
1268
1269    /**
1270     * Resets the Wi-Fi Connections by clearing any state, resetting any sockets
1271     * using the interface, stopping DHCP, and disabling the interface.
1272     */
1273    public void resetConnections(boolean disableInterface) {
1274        if (LOCAL_LOGD) Log.d(TAG, "Reset connections and stopping DHCP");
1275        mHaveIpAddress = false;
1276        mObtainingIpAddress = false;
1277        mWifiInfo.setIpAddress(0);
1278
1279        /*
1280         * Reset connection depends on both the interface and the IP assigned,
1281         * so it should be done before any chance of the IP being lost.
1282         */
1283        NetworkUtils.resetConnections(mInterfaceName);
1284
1285        // Stop DHCP
1286        if (mDhcpTarget != null) {
1287            mDhcpTarget.setCancelCallback(true);
1288            mDhcpTarget.removeMessages(EVENT_DHCP_START);
1289        }
1290        if (!NetworkUtils.stopDhcp(mInterfaceName)) {
1291            Log.e(TAG, "Could not stop DHCP");
1292        }
1293
1294        /**
1295         * Interface is re-enabled in the supplicant
1296         * when moving out of ASSOCIATING state
1297         */
1298        if(disableInterface) {
1299            if (LOCAL_LOGD) Log.d(TAG, "Disabling interface");
1300            NetworkUtils.disableInterface(mInterfaceName);
1301        }
1302    }
1303
1304    /**
1305     * The supplicant is reporting that we are disconnected from the current
1306     * access point. Often, however, a disconnect will be followed very shortly
1307     * by a reconnect to the same access point. Therefore, we delay resetting
1308     * the connection's IP state for a bit.
1309     */
1310    private void scheduleDisconnect() {
1311        mDisconnectPending = true;
1312        if (!hasMessages(EVENT_DEFERRED_DISCONNECT)) {
1313            sendEmptyMessageDelayed(EVENT_DEFERRED_DISCONNECT, DISCONNECT_DELAY_MSECS);
1314        }
1315    }
1316
1317    private void cancelDisconnect() {
1318        mDisconnectPending = false;
1319        removeMessages(EVENT_DEFERRED_DISCONNECT);
1320    }
1321
1322    public DhcpInfo getDhcpInfo() {
1323        return mDhcpInfo;
1324    }
1325
1326    public synchronized List<ScanResult> getScanResultsList() {
1327        return mScanResults;
1328    }
1329
1330    public synchronized void setScanResultsList(List<ScanResult> scanList) {
1331        mScanResults = scanList;
1332    }
1333
1334    /**
1335     * Get status information for the current connection, if any.
1336     * @return a {@link WifiInfo} object containing information about the current connection
1337     */
1338    public WifiInfo requestConnectionInfo() {
1339        requestConnectionStatus(mWifiInfo);
1340        requestPolledInfo(mWifiInfo, false);
1341        return mWifiInfo;
1342    }
1343
1344    private void requestConnectionStatus(WifiInfo info) {
1345        String reply = status();
1346        if (reply == null) {
1347            return;
1348        }
1349        /*
1350         * Parse the reply from the supplicant to the status command, and update
1351         * local state accordingly. The reply is a series of lines of the form
1352         * "name=value".
1353         */
1354        String SSID = null;
1355        String BSSID = null;
1356        String suppState = null;
1357        int netId = -1;
1358        String[] lines = reply.split("\n");
1359        for (String line : lines) {
1360            String[] prop = line.split(" *= *");
1361            if (prop.length < 2)
1362                continue;
1363            String name = prop[0];
1364            String value = prop[1];
1365            if (name.equalsIgnoreCase("id"))
1366                netId = Integer.parseInt(value);
1367            else if (name.equalsIgnoreCase("ssid"))
1368                SSID = value;
1369            else if (name.equalsIgnoreCase("bssid"))
1370                BSSID = value;
1371            else if (name.equalsIgnoreCase("wpa_state"))
1372                suppState = value;
1373        }
1374        info.setNetworkId(netId);
1375        info.setSSID(SSID);
1376        info.setBSSID(BSSID);
1377        /*
1378         * We only set the supplicant state if the previous state was
1379         * UNINITIALIZED. This should only happen when we first connect to
1380         * the supplicant. Once we're connected, we should always receive
1381         * an event upon any state change, but in this case, we want to
1382         * make sure any listeners are made aware of the state change.
1383         */
1384        if (mWifiInfo.getSupplicantState() == SupplicantState.UNINITIALIZED && suppState != null)
1385            setSupplicantState(suppState);
1386    }
1387
1388    /**
1389     * Get the dynamic information that is not reported via events.
1390     * @param info the object into which the information should be captured.
1391     */
1392    private synchronized void requestPolledInfo(WifiInfo info, boolean polling)
1393    {
1394        int newRssi = (polling ? getRssiApprox() : getRssi());
1395        if (newRssi != -1 && -200 < newRssi && newRssi < 256) { // screen out invalid values
1396            /* some implementations avoid negative values by adding 256
1397             * so we need to adjust for that here.
1398             */
1399            if (newRssi > 0) newRssi -= 256;
1400            info.setRssi(newRssi);
1401            /*
1402             * Rather then sending the raw RSSI out every time it
1403             * changes, we precalculate the signal level that would
1404             * be displayed in the status bar, and only send the
1405             * broadcast if that much more coarse-grained number
1406             * changes. This cuts down greatly on the number of
1407             * broadcasts, at the cost of not informing others
1408             * interested in RSSI of all the changes in signal
1409             * level.
1410             */
1411            // TODO: The second arg to the call below needs to be a symbol somewhere, but
1412            // it's actually the size of an array of icons that's private
1413            // to StatusBar Policy.
1414            int newSignalLevel = WifiManager.calculateSignalLevel(newRssi, 4);
1415            if (newSignalLevel != mLastSignalLevel) {
1416                sendRssiChangeBroadcast(newRssi);
1417            }
1418            mLastSignalLevel = newSignalLevel;
1419        } else {
1420            info.setRssi(-200);
1421        }
1422        int newLinkSpeed = getLinkSpeed();
1423        if (newLinkSpeed != -1) {
1424            info.setLinkSpeed(newLinkSpeed);
1425        }
1426    }
1427
1428    private void sendRssiChangeBroadcast(final int newRssi) {
1429        if (ActivityManagerNative.isSystemReady()) {
1430            Intent intent = new Intent(WifiManager.RSSI_CHANGED_ACTION);
1431            intent.putExtra(WifiManager.EXTRA_NEW_RSSI, newRssi);
1432            mContext.sendBroadcast(intent);
1433        }
1434    }
1435
1436    private void sendNetworkStateChangeBroadcast(String bssid) {
1437        Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
1438        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
1439                | Intent.FLAG_RECEIVER_REPLACE_PENDING);
1440        intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, mNetworkInfo);
1441        if (bssid != null)
1442            intent.putExtra(WifiManager.EXTRA_BSSID, bssid);
1443        mContext.sendStickyBroadcast(intent);
1444    }
1445
1446    /**
1447     * Disable Wi-Fi connectivity by stopping the driver.
1448     */
1449    public boolean teardown() {
1450        if (!mTornDownByConnMgr) {
1451            if (disconnectAndStop()) {
1452                setTornDownByConnMgr(true);
1453                return true;
1454            } else {
1455                return false;
1456            }
1457        } else {
1458            return true;
1459        }
1460    }
1461
1462    /**
1463     * Reenable Wi-Fi connectivity by restarting the driver.
1464     */
1465    public boolean reconnect() {
1466        if (mTornDownByConnMgr) {
1467            if (restart()) {
1468                setTornDownByConnMgr(false);
1469                return true;
1470            } else {
1471                return false;
1472            }
1473        } else {
1474            return true;
1475        }
1476    }
1477
1478    /**
1479     * We want to stop the driver, but if we're connected to a network,
1480     * we first want to disconnect, so that the supplicant is always in
1481     * a known state (DISCONNECTED) when the driver is stopped.
1482     * @return {@code true} if the operation succeeds, which means that the
1483     * disconnect or stop command was initiated.
1484     */
1485    public synchronized boolean disconnectAndStop() {
1486        boolean ret = true;;
1487        if (mRunState != RUN_STATE_STOPPING && mRunState != RUN_STATE_STOPPED) {
1488            // Take down any open network notifications
1489            setNotificationVisible(false, 0, false, 0);
1490
1491            if (mWifiInfo.getSupplicantState() == SupplicantState.DORMANT) {
1492                ret = stopDriver();
1493            } else {
1494                ret = disconnect();
1495            }
1496            mRunState = RUN_STATE_STOPPING;
1497        }
1498        return ret;
1499    }
1500
1501    public synchronized boolean restart() {
1502        if (mRunState == RUN_STATE_STOPPED) {
1503            mRunState = RUN_STATE_STARTING;
1504            resetConnections(true);
1505            return startDriver();
1506        } else if (mRunState == RUN_STATE_STOPPING) {
1507            mRunState = RUN_STATE_STARTING;
1508        }
1509        return true;
1510    }
1511
1512    public int getWifiState() {
1513        return mWifiState.get();
1514    }
1515
1516    public void setWifiState(int wifiState) {
1517        mWifiState.set(wifiState);
1518    }
1519
1520   /**
1521     * The WifiNative interface functions are listed below.
1522     * The only native call that is not synchronized on
1523     * WifiStateTracker is waitForEvent() which waits on a
1524     * seperate monitor channel.
1525     *
1526     * All supplicant commands need the wifi to be in an
1527     * enabled state. This can be done by checking the
1528     * mWifiState to be WIFI_STATE_ENABLED.
1529     *
1530     * All commands that can cause commands to driver
1531     * initiated need the driver state to be started.
1532     * This is done by checking isDriverStopped() to
1533     * be false.
1534     */
1535
1536    /**
1537     * Load the driver and firmware
1538     *
1539     * @return {@code true} if the operation succeeds, {@code false} otherwise
1540     */
1541    public synchronized boolean loadDriver() {
1542        return WifiNative.loadDriver();
1543    }
1544
1545    /**
1546     * Unload the driver and firmware
1547     *
1548     * @return {@code true} if the operation succeeds, {@code false} otherwise
1549     */
1550    public synchronized boolean unloadDriver() {
1551        return WifiNative.unloadDriver();
1552    }
1553
1554    /**
1555     * Check the supplicant config and
1556     * start the supplicant daemon
1557     *
1558     * @return {@code true} if the operation succeeds, {@code false} otherwise
1559     */
1560    public synchronized boolean startSupplicant() {
1561        return WifiNative.startSupplicant();
1562    }
1563
1564    /**
1565     * Stop the supplicant daemon
1566     *
1567     * @return {@code true} if the operation succeeds, {@code false} otherwise
1568     */
1569    public synchronized boolean stopSupplicant() {
1570        return WifiNative.stopSupplicant();
1571    }
1572
1573    /**
1574     * Establishes two channels - control channel for commands
1575     * and monitor channel for notifying WifiMonitor
1576     *
1577     * @return {@code true} if the operation succeeds, {@code false} otherwise
1578     */
1579    public synchronized boolean connectToSupplicant() {
1580        return WifiNative.connectToSupplicant();
1581    }
1582
1583    /**
1584     * Close the control/monitor channels to supplicant
1585     */
1586    public synchronized void closeSupplicantConnection() {
1587        WifiNative.closeSupplicantConnection();
1588    }
1589
1590    /**
1591     * Check if the supplicant is alive
1592     *
1593     * @return {@code true} if the operation succeeds, {@code false} otherwise
1594     */
1595    public synchronized boolean ping() {
1596        if (mWifiState.get() != WIFI_STATE_ENABLED) {
1597            return false;
1598        }
1599        return WifiNative.pingCommand();
1600    }
1601
1602    /**
1603     * initiate an active or passive scan
1604     *
1605     * @param forceActive true if it is a active scan
1606     * @return {@code true} if the operation succeeds, {@code false} otherwise
1607     */
1608    public synchronized boolean scan(boolean forceActive) {
1609        if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
1610            return false;
1611        }
1612        return WifiNative.scanCommand(forceActive);
1613    }
1614
1615    /**
1616     * Specifies whether the supplicant or driver
1617     * take care of initiating scan and doing AP selection
1618     *
1619     * @param mode
1620     *    SUPPL_SCAN_HANDLING_NORMAL
1621     *    SUPPL_SCAN_HANDLING_LIST_ONLY
1622     * @return {@code true} if the operation succeeds, {@code false} otherwise
1623     */
1624    public synchronized boolean setScanResultHandling(int mode) {
1625        if (mWifiState.get() != WIFI_STATE_ENABLED) {
1626            return false;
1627        }
1628        return WifiNative.setScanResultHandlingCommand(mode);
1629    }
1630
1631    /**
1632     * Fetch the scan results from the supplicant
1633     *
1634     * @return example result string
1635     * 00:bb:cc:dd:cc:ee       2427    166     [WPA-EAP-TKIP][WPA2-EAP-CCMP]   Net1
1636     * 00:bb:cc:dd:cc:ff       2412    165     [WPA-EAP-TKIP][WPA2-EAP-CCMP]   Net2
1637     */
1638    public synchronized String scanResults() {
1639        if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
1640            return null;
1641        }
1642        return WifiNative.scanResultsCommand();
1643    }
1644
1645    /**
1646     * Set the scan mode - active or passive
1647     *
1648     * @return {@code true} if the operation succeeds, {@code false} otherwise
1649     */
1650    public synchronized boolean setScanMode(boolean isScanModeActive) {
1651        if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
1652            return false;
1653        }
1654        if (mIsScanModeActive != isScanModeActive) {
1655            return WifiNative.setScanModeCommand(mIsScanModeActive = isScanModeActive);
1656        }
1657        return true;
1658    }
1659
1660    /**
1661     * Disconnect from Access Point
1662     *
1663     * @return {@code true} if the operation succeeds, {@code false} otherwise
1664     */
1665    public synchronized boolean disconnect() {
1666        if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
1667            return false;
1668        }
1669        return WifiNative.disconnectCommand();
1670    }
1671
1672    /**
1673     * Initiate a reconnection to AP
1674     *
1675     * @return {@code true} if the operation succeeds, {@code false} otherwise
1676     */
1677    public synchronized boolean reconnectCommand() {
1678        if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
1679            return false;
1680        }
1681        return WifiNative.reconnectCommand();
1682    }
1683
1684    /**
1685     * Add a network
1686     *
1687     * @return network id of the new network
1688     */
1689    public synchronized int addNetwork() {
1690        if (mWifiState.get() != WIFI_STATE_ENABLED) {
1691            return -1;
1692        }
1693        return WifiNative.addNetworkCommand();
1694    }
1695
1696    /**
1697     * Delete a network
1698     *
1699     * @param networkId id of the network to be removed
1700     * @return {@code true} if the operation succeeds, {@code false} otherwise
1701     */
1702    public synchronized boolean removeNetwork(int networkId) {
1703        if (mWifiState.get() != WIFI_STATE_ENABLED) {
1704            return false;
1705        }
1706        return mDisconnectExpected = WifiNative.removeNetworkCommand(networkId);
1707    }
1708
1709    /**
1710     * Enable a network
1711     *
1712     * @param netId network id of the network
1713     * @param disableOthers true, if all other networks have to be disabled
1714     * @return {@code true} if the operation succeeds, {@code false} otherwise
1715     */
1716    public synchronized boolean enableNetwork(int netId, boolean disableOthers) {
1717        if (mWifiState.get() != WIFI_STATE_ENABLED) {
1718            return false;
1719        }
1720        return WifiNative.enableNetworkCommand(netId, disableOthers);
1721    }
1722
1723    /**
1724     * Disable a network
1725     *
1726     * @param netId network id of the network
1727     * @return {@code true} if the operation succeeds, {@code false} otherwise
1728     */
1729    public synchronized boolean disableNetwork(int netId) {
1730        if (mWifiState.get() != WIFI_STATE_ENABLED) {
1731            return false;
1732        }
1733        return WifiNative.disableNetworkCommand(netId);
1734    }
1735
1736    /**
1737     * Initiate a re-association in supplicant
1738     *
1739     * @return {@code true} if the operation succeeds, {@code false} otherwise
1740     */
1741    public synchronized boolean reassociate() {
1742        if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
1743            return false;
1744        }
1745        return WifiNative.reassociateCommand();
1746    }
1747
1748    /**
1749     * Blacklist a BSSID. This will avoid the AP if there are
1750     * alternate APs to connect
1751     *
1752     * @param bssid BSSID of the network
1753     * @return {@code true} if the operation succeeds, {@code false} otherwise
1754     */
1755    public synchronized boolean addToBlacklist(String bssid) {
1756        if (mWifiState.get() != WIFI_STATE_ENABLED) {
1757            return false;
1758        }
1759        return WifiNative.addToBlacklistCommand(bssid);
1760    }
1761
1762    /**
1763     * Clear the blacklist list
1764     *
1765     * @return {@code true} if the operation succeeds, {@code false} otherwise
1766     */
1767    public synchronized boolean clearBlacklist() {
1768        if (mWifiState.get() != WIFI_STATE_ENABLED) {
1769            return false;
1770        }
1771        return WifiNative.clearBlacklistCommand();
1772    }
1773
1774    /**
1775     * List all configured networks
1776     *
1777     * @return list of networks or null on failure
1778     */
1779    public synchronized String listNetworks() {
1780        if (mWifiState.get() != WIFI_STATE_ENABLED) {
1781            return null;
1782        }
1783        return WifiNative.listNetworksCommand();
1784    }
1785
1786    /**
1787     * Get network setting by name
1788     *
1789     * @param netId network id of the network
1790     * @param name network variable key
1791     * @return value corresponding to key
1792     */
1793    public synchronized String getNetworkVariable(int netId, String name) {
1794        if (mWifiState.get() != WIFI_STATE_ENABLED) {
1795            return null;
1796        }
1797        return WifiNative.getNetworkVariableCommand(netId, name);
1798    }
1799
1800    /**
1801     * Set network setting by name
1802     *
1803     * @param netId network id of the network
1804     * @param name network variable key
1805     * @param value network variable value
1806     * @return {@code true} if the operation succeeds, {@code false} otherwise
1807     */
1808    public synchronized boolean setNetworkVariable(int netId, String name, String value) {
1809        if (mWifiState.get() != WIFI_STATE_ENABLED) {
1810            return false;
1811        }
1812        return WifiNative.setNetworkVariableCommand(netId, name, value);
1813    }
1814
1815    /**
1816     * Get detailed status of the connection
1817     *
1818     * @return Example status result
1819     *  bssid=aa:bb:cc:dd:ee:ff
1820     *  ssid=TestNet
1821     *  id=3
1822     *  pairwise_cipher=NONE
1823     *  group_cipher=NONE
1824     *  key_mgmt=NONE
1825     *  wpa_state=COMPLETED
1826     *  ip_address=X.X.X.X
1827     */
1828    public synchronized String status() {
1829        if (mWifiState.get() != WIFI_STATE_ENABLED) {
1830            return null;
1831        }
1832        return WifiNative.statusCommand();
1833    }
1834
1835    /**
1836     * Get RSSI to currently connected network
1837     *
1838     * @return RSSI value, -1 on failure
1839     */
1840    public synchronized int getRssi() {
1841        if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
1842            return -1;
1843        }
1844        return WifiNative.getRssiApproxCommand();
1845    }
1846
1847    /**
1848     * Get approx RSSI to currently connected network
1849     *
1850     * @return RSSI value, -1 on failure
1851     */
1852    public synchronized int getRssiApprox() {
1853        if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
1854            return -1;
1855        }
1856        return WifiNative.getRssiApproxCommand();
1857    }
1858
1859    /**
1860     * Get link speed to currently connected network
1861     *
1862     * @return link speed, -1 on failure
1863     */
1864    public synchronized int getLinkSpeed() {
1865        if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
1866            return -1;
1867        }
1868        return WifiNative.getLinkSpeedCommand();
1869    }
1870
1871    /**
1872     * Get MAC address of radio
1873     *
1874     * @return MAC address, null on failure
1875     */
1876    public synchronized String getMacAddress() {
1877        if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
1878            return null;
1879        }
1880        return WifiNative.getMacAddressCommand();
1881    }
1882
1883    /**
1884     * Start driver
1885     *
1886     * @return {@code true} if the operation succeeds, {@code false} otherwise
1887     */
1888    public synchronized boolean startDriver() {
1889        if (mWifiState.get() != WIFI_STATE_ENABLED) {
1890            return false;
1891        }
1892        return WifiNative.startDriverCommand();
1893    }
1894
1895    /**
1896     * Stop driver
1897     *
1898     * @return {@code true} if the operation succeeds, {@code false} otherwise
1899     */
1900    public synchronized boolean stopDriver() {
1901        /* Driver stop should not happen only when supplicant event
1902         * DRIVER_STOPPED has already been handled */
1903        if (mWifiState.get() != WIFI_STATE_ENABLED || mRunState == RUN_STATE_STOPPED) {
1904            return false;
1905        }
1906        return WifiNative.stopDriverCommand();
1907    }
1908
1909    /**
1910     * Start packet filtering
1911     *
1912     * @return {@code true} if the operation succeeds, {@code false} otherwise
1913     */
1914    public synchronized boolean startPacketFiltering() {
1915        if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
1916            return false;
1917        }
1918        return WifiNative.startPacketFiltering();
1919    }
1920
1921    /**
1922     * Stop packet filtering
1923     *
1924     * @return {@code true} if the operation succeeds, {@code false} otherwise
1925     */
1926    public synchronized boolean stopPacketFiltering() {
1927        if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
1928            return false;
1929        }
1930        return WifiNative.stopPacketFiltering();
1931    }
1932
1933    /**
1934     * Set power mode
1935     * @param mode
1936     *     DRIVER_POWER_MODE_AUTO
1937     *     DRIVER_POWER_MODE_ACTIVE
1938     * @return {@code true} if the operation succeeds, {@code false} otherwise
1939     */
1940    public synchronized boolean setPowerMode(int mode) {
1941        if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
1942            return false;
1943        }
1944        return WifiNative.setPowerModeCommand(mode);
1945    }
1946
1947    /**
1948     * Set the number of allowed radio frequency channels from the system
1949     * setting value, if any.
1950     * @return {@code true} if the operation succeeds, {@code false} otherwise, e.g.,
1951     * the number of channels is invalid.
1952     */
1953    public synchronized boolean setNumAllowedChannels() {
1954        if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
1955            return false;
1956        }
1957        try {
1958            return setNumAllowedChannels(
1959                    Settings.Secure.getInt(mContext.getContentResolver(),
1960                    Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS));
1961        } catch (Settings.SettingNotFoundException e) {
1962            if (mNumAllowedChannels != 0) {
1963                WifiNative.setNumAllowedChannelsCommand(mNumAllowedChannels);
1964            }
1965            // otherwise, use the driver default
1966        }
1967        return true;
1968    }
1969
1970    /**
1971     * Set the number of radio frequency channels that are allowed to be used
1972     * in the current regulatory domain.
1973     * @param numChannels the number of allowed channels. Must be greater than 0
1974     * and less than or equal to 16.
1975     * @return {@code true} if the operation succeeds, {@code false} otherwise, e.g.,
1976     * {@code numChannels} is outside the valid range.
1977     */
1978    public synchronized boolean setNumAllowedChannels(int numChannels) {
1979        if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
1980            return false;
1981        }
1982        mNumAllowedChannels = numChannels;
1983        return WifiNative.setNumAllowedChannelsCommand(numChannels);
1984    }
1985
1986    /**
1987     * Get number of allowed channels
1988     *
1989     * @return channel count, -1 on failure
1990     */
1991    public synchronized int getNumAllowedChannels() {
1992        if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
1993            return -1;
1994        }
1995        return WifiNative.getNumAllowedChannelsCommand();
1996    }
1997
1998    /**
1999     * Set bluetooth coex mode:
2000     *
2001     * @param mode
2002     *  BLUETOOTH_COEXISTENCE_MODE_ENABLED
2003     *  BLUETOOTH_COEXISTENCE_MODE_DISABLED
2004     *  BLUETOOTH_COEXISTENCE_MODE_SENSE
2005     * @return {@code true} if the operation succeeds, {@code false} otherwise
2006     */
2007    public synchronized boolean setBluetoothCoexistenceMode(int mode) {
2008        if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
2009            return false;
2010        }
2011        return WifiNative.setBluetoothCoexistenceModeCommand(mode);
2012    }
2013
2014    /**
2015     * Enable or disable Bluetooth coexistence scan mode. When this mode is on,
2016     * some of the low-level scan parameters used by the driver are changed to
2017     * reduce interference with A2DP streaming.
2018     *
2019     * @param isBluetoothPlaying whether to enable or disable this mode
2020     */
2021    public synchronized void setBluetoothScanMode(boolean isBluetoothPlaying) {
2022        if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
2023            return;
2024        }
2025        WifiNative.setBluetoothCoexistenceScanModeCommand(isBluetoothPlaying);
2026    }
2027
2028    /**
2029     * Save configuration on supplicant
2030     *
2031     * @return {@code true} if the operation succeeds, {@code false} otherwise
2032     */
2033    public synchronized boolean saveConfig() {
2034        if (mWifiState.get() != WIFI_STATE_ENABLED) {
2035            return false;
2036        }
2037        return WifiNative.saveConfigCommand();
2038    }
2039
2040    /**
2041     * Reload the configuration from file
2042     *
2043     * @return {@code true} if the operation succeeds, {@code false} otherwise
2044     */
2045    public synchronized boolean reloadConfig() {
2046        if (mWifiState.get() != WIFI_STATE_ENABLED) {
2047            return false;
2048        }
2049        return WifiNative.reloadConfigCommand();
2050    }
2051
2052    public boolean setRadio(boolean turnOn) {
2053        return mWM.setWifiEnabled(turnOn);
2054    }
2055
2056    /**
2057     * {@inheritDoc}
2058     * There are currently no Wi-Fi-specific features supported.
2059     * @param feature the name of the feature
2060     * @return {@code -1} indicating failure, always
2061     */
2062    public int startUsingNetworkFeature(String feature, int callingPid, int callingUid) {
2063        return -1;
2064    }
2065
2066    /**
2067     * {@inheritDoc}
2068     * There are currently no Wi-Fi-specific features supported.
2069     * @param feature the name of the feature
2070     * @return {@code -1} indicating failure, always
2071     */
2072    public int stopUsingNetworkFeature(String feature, int callingPid, int callingUid) {
2073        return -1;
2074    }
2075
2076    @Override
2077    public void interpretScanResultsAvailable() {
2078
2079        // If we shouldn't place a notification on available networks, then
2080        // don't bother doing any of the following
2081        if (!mNotificationEnabled) return;
2082
2083        NetworkInfo networkInfo = getNetworkInfo();
2084
2085        State state = networkInfo.getState();
2086        if ((state == NetworkInfo.State.DISCONNECTED)
2087                || (state == NetworkInfo.State.UNKNOWN)) {
2088
2089            // Look for an open network
2090            List<ScanResult> scanResults = getScanResultsList();
2091            if (scanResults != null) {
2092                int numOpenNetworks = 0;
2093                for (int i = scanResults.size() - 1; i >= 0; i--) {
2094                    ScanResult scanResult = scanResults.get(i);
2095
2096                    if (TextUtils.isEmpty(scanResult.capabilities)) {
2097                        numOpenNetworks++;
2098                    }
2099                }
2100
2101                if (numOpenNetworks > 0) {
2102                    if (++mNumScansSinceNetworkStateChange >= NUM_SCANS_BEFORE_ACTUALLY_SCANNING) {
2103                        /*
2104                         * We've scanned continuously at least
2105                         * NUM_SCANS_BEFORE_NOTIFICATION times. The user
2106                         * probably does not have a remembered network in range,
2107                         * since otherwise supplicant would have tried to
2108                         * associate and thus resetting this counter.
2109                         */
2110                        setNotificationVisible(true, numOpenNetworks, false, 0);
2111                    }
2112                    return;
2113                }
2114            }
2115        }
2116
2117        // No open networks in range, remove the notification
2118        setNotificationVisible(false, 0, false, 0);
2119    }
2120
2121    /**
2122     * Display or don't display a notification that there are open Wi-Fi networks.
2123     * @param visible {@code true} if notification should be visible, {@code false} otherwise
2124     * @param numNetworks the number networks seen
2125     * @param force {@code true} to force notification to be shown/not-shown,
2126     * even if it is already shown/not-shown.
2127     * @param delay time in milliseconds after which the notification should be made
2128     * visible or invisible.
2129     */
2130    public void setNotificationVisible(boolean visible, int numNetworks, boolean force, int delay) {
2131
2132        // Since we use auto cancel on the notification, when the
2133        // mNetworksAvailableNotificationShown is true, the notification may
2134        // have actually been canceled.  However, when it is false we know
2135        // for sure that it is not being shown (it will not be shown any other
2136        // place than here)
2137
2138        // If it should be hidden and it is already hidden, then noop
2139        if (!visible && !mNotificationShown && !force) {
2140            return;
2141        }
2142
2143        Message message;
2144        if (visible) {
2145
2146            // Not enough time has passed to show the notification again
2147            if (System.currentTimeMillis() < mNotificationRepeatTime) {
2148                return;
2149            }
2150
2151            if (mNotification == null) {
2152                // Cache the Notification mainly so we can remove the
2153                // EVENT_NOTIFICATION_CHANGED message with this Notification from
2154                // the queue later
2155                mNotification = new Notification();
2156                mNotification.when = 0;
2157                mNotification.icon = ICON_NETWORKS_AVAILABLE;
2158                mNotification.flags = Notification.FLAG_AUTO_CANCEL;
2159                mNotification.contentIntent = PendingIntent.getActivity(mContext, 0,
2160                        new Intent(WifiManager.ACTION_PICK_WIFI_NETWORK), 0);
2161            }
2162
2163            CharSequence title = mContext.getResources().getQuantityText(
2164                    com.android.internal.R.plurals.wifi_available, numNetworks);
2165            CharSequence details = mContext.getResources().getQuantityText(
2166                    com.android.internal.R.plurals.wifi_available_detailed, numNetworks);
2167            mNotification.tickerText = title;
2168            mNotification.setLatestEventInfo(mContext, title, details, mNotification.contentIntent);
2169
2170            mNotificationRepeatTime = System.currentTimeMillis() + NOTIFICATION_REPEAT_DELAY_MS;
2171
2172            message = mTarget.obtainMessage(EVENT_NOTIFICATION_CHANGED, 1,
2173                    ICON_NETWORKS_AVAILABLE, mNotification);
2174
2175        } else {
2176
2177            // Remove any pending messages to show the notification
2178            mTarget.removeMessages(EVENT_NOTIFICATION_CHANGED, mNotification);
2179
2180            message = mTarget.obtainMessage(EVENT_NOTIFICATION_CHANGED, 0, ICON_NETWORKS_AVAILABLE);
2181        }
2182
2183        mTarget.sendMessageDelayed(message, delay);
2184
2185        mNotificationShown = visible;
2186    }
2187
2188    /**
2189     * Clears variables related to tracking whether a notification has been
2190     * shown recently.
2191     * <p>
2192     * After calling this method, the timer that prevents notifications from
2193     * being shown too often will be cleared.
2194     */
2195    private void resetNotificationTimer() {
2196        mNotificationRepeatTime = 0;
2197        mNumScansSinceNetworkStateChange = 0;
2198    }
2199
2200    @Override
2201    public String toString() {
2202        StringBuffer sb = new StringBuffer();
2203        sb.append("interface ").append(mInterfaceName);
2204        sb.append(" runState=");
2205        if (mRunState >= 1 && mRunState <= mRunStateNames.length) {
2206            sb.append(mRunStateNames[mRunState-1]);
2207        } else {
2208            sb.append(mRunState);
2209        }
2210        sb.append(LS).append(mWifiInfo).append(LS);
2211        sb.append(mDhcpInfo).append(LS);
2212        sb.append("haveIpAddress=").append(mHaveIpAddress).
2213                append(", obtainingIpAddress=").append(mObtainingIpAddress).
2214                append(", scanModeActive=").append(mIsScanModeActive).append(LS).
2215                append("lastSignalLevel=").append(mLastSignalLevel).
2216                append(", explicitlyDisabled=").append(mTornDownByConnMgr);
2217        return sb.toString();
2218    }
2219
2220    private class DhcpHandler extends Handler {
2221
2222        private Handler mTarget;
2223
2224        /**
2225         * Whether to skip the DHCP result callback to the target. For example,
2226         * this could be set if the network we were requesting an IP for has
2227         * since been disconnected.
2228         * <p>
2229         * Note: There is still a chance where the client's intended DHCP
2230         * request not being canceled. For example, we are request for IP on
2231         * A, and he queues request for IP on B, and then cancels the request on
2232         * B while we're still requesting from A.
2233         */
2234        private boolean mCancelCallback;
2235
2236        /**
2237         * Instance of the bluetooth headset helper. This needs to be created
2238         * early because there is a delay before it actually 'connects', as
2239         * noted by its javadoc. If we check before it is connected, it will be
2240         * in an error state and we will not disable coexistence.
2241         */
2242        private BluetoothHeadset mBluetoothHeadset;
2243
2244        public DhcpHandler(Looper looper, Handler target) {
2245            super(looper);
2246            mTarget = target;
2247
2248            mBluetoothHeadset = new BluetoothHeadset(mContext, null);
2249        }
2250
2251        public void handleMessage(Message msg) {
2252            int event;
2253
2254            switch (msg.what) {
2255                case EVENT_DHCP_START:
2256
2257                    boolean modifiedBluetoothCoexistenceMode = false;
2258                    if (shouldDisableCoexistenceMode()) {
2259                        /*
2260                         * There are problems setting the Wi-Fi driver's power
2261                         * mode to active when bluetooth coexistence mode is
2262                         * enabled or sense.
2263                         * <p>
2264                         * We set Wi-Fi to active mode when
2265                         * obtaining an IP address because we've found
2266                         * compatibility issues with some routers with low power
2267                         * mode.
2268                         * <p>
2269                         * In order for this active power mode to properly be set,
2270                         * we disable coexistence mode until we're done with
2271                         * obtaining an IP address.  One exception is if we
2272                         * are currently connected to a headset, since disabling
2273                         * coexistence would interrupt that connection.
2274                         */
2275                        modifiedBluetoothCoexistenceMode = true;
2276
2277                        // Disable the coexistence mode
2278                        setBluetoothCoexistenceMode(
2279                                WifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED);
2280                    }
2281
2282                    setPowerMode(DRIVER_POWER_MODE_ACTIVE);
2283
2284                    synchronized (this) {
2285                        // A new request is being made, so assume we will callback
2286                        mCancelCallback = false;
2287                    }
2288                    Log.d(TAG, "DhcpHandler: DHCP request started");
2289                    if (NetworkUtils.runDhcp(mInterfaceName, mDhcpInfo)) {
2290                        event = EVENT_INTERFACE_CONFIGURATION_SUCCEEDED;
2291                        if (LOCAL_LOGD) Log.v(TAG, "DhcpHandler: DHCP request succeeded");
2292                    } else {
2293                        event = EVENT_INTERFACE_CONFIGURATION_FAILED;
2294                        Log.i(TAG, "DhcpHandler: DHCP request failed: " +
2295                            NetworkUtils.getDhcpError());
2296                    }
2297
2298                    setPowerMode(DRIVER_POWER_MODE_AUTO);
2299
2300                    if (modifiedBluetoothCoexistenceMode) {
2301                        // Set the coexistence mode back to its default value
2302                        setBluetoothCoexistenceMode(
2303                                WifiNative.BLUETOOTH_COEXISTENCE_MODE_SENSE);
2304                    }
2305
2306                    synchronized (this) {
2307                        if (!mCancelCallback) {
2308                            mTarget.sendEmptyMessage(event);
2309                        }
2310                    }
2311                    break;
2312            }
2313        }
2314
2315        public synchronized void setCancelCallback(boolean cancelCallback) {
2316            mCancelCallback = cancelCallback;
2317        }
2318
2319        /**
2320         * Whether to disable coexistence mode while obtaining IP address. This
2321         * logic will return true only if the current bluetooth
2322         * headset/handsfree state is disconnected. This means if it is in an
2323         * error state, we will NOT disable coexistence mode to err on the side
2324         * of safety.
2325         *
2326         * @return Whether to disable coexistence mode.
2327         */
2328        private boolean shouldDisableCoexistenceMode() {
2329            int state = mBluetoothHeadset.getState(mBluetoothHeadset.getCurrentHeadset());
2330            return state == BluetoothHeadset.STATE_DISCONNECTED;
2331        }
2332    }
2333
2334    private void checkUseStaticIp() {
2335        mUseStaticIp = false;
2336        final ContentResolver cr = mContext.getContentResolver();
2337        try {
2338            if (Settings.System.getInt(cr, Settings.System.WIFI_USE_STATIC_IP) == 0) {
2339                return;
2340            }
2341        } catch (Settings.SettingNotFoundException e) {
2342            return;
2343        }
2344
2345        try {
2346            String addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_IP);
2347            if (addr != null) {
2348                mDhcpInfo.ipAddress = stringToIpAddr(addr);
2349            } else {
2350                return;
2351            }
2352            addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_GATEWAY);
2353            if (addr != null) {
2354                mDhcpInfo.gateway = stringToIpAddr(addr);
2355            } else {
2356                return;
2357            }
2358            addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_NETMASK);
2359            if (addr != null) {
2360                mDhcpInfo.netmask = stringToIpAddr(addr);
2361            } else {
2362                return;
2363            }
2364            addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_DNS1);
2365            if (addr != null) {
2366                mDhcpInfo.dns1 = stringToIpAddr(addr);
2367            } else {
2368                return;
2369            }
2370            addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_DNS2);
2371            if (addr != null) {
2372                mDhcpInfo.dns2 = stringToIpAddr(addr);
2373            } else {
2374                mDhcpInfo.dns2 = 0;
2375            }
2376        } catch (UnknownHostException e) {
2377            return;
2378        }
2379        mUseStaticIp = true;
2380    }
2381
2382    private static int stringToIpAddr(String addrString) throws UnknownHostException {
2383        try {
2384            String[] parts = addrString.split("\\.");
2385            if (parts.length != 4) {
2386                throw new UnknownHostException(addrString);
2387            }
2388
2389            int a = Integer.parseInt(parts[0])      ;
2390            int b = Integer.parseInt(parts[1]) <<  8;
2391            int c = Integer.parseInt(parts[2]) << 16;
2392            int d = Integer.parseInt(parts[3]) << 24;
2393
2394            return a | b | c | d;
2395        } catch (NumberFormatException ex) {
2396            throw new UnknownHostException(addrString);
2397        }
2398    }
2399
2400    private int getMaxDhcpRetries() {
2401        return Settings.Secure.getInt(mContext.getContentResolver(),
2402                                      Settings.Secure.WIFI_MAX_DHCP_RETRY_COUNT,
2403                                      DEFAULT_MAX_DHCP_RETRIES);
2404    }
2405
2406    private class SettingsObserver extends ContentObserver {
2407        public SettingsObserver(Handler handler) {
2408            super(handler);
2409            ContentResolver cr = mContext.getContentResolver();
2410            cr.registerContentObserver(Settings.System.getUriFor(
2411                Settings.System.WIFI_USE_STATIC_IP), false, this);
2412            cr.registerContentObserver(Settings.System.getUriFor(
2413                Settings.System.WIFI_STATIC_IP), false, this);
2414            cr.registerContentObserver(Settings.System.getUriFor(
2415                Settings.System.WIFI_STATIC_GATEWAY), false, this);
2416            cr.registerContentObserver(Settings.System.getUriFor(
2417                Settings.System.WIFI_STATIC_NETMASK), false, this);
2418            cr.registerContentObserver(Settings.System.getUriFor(
2419                Settings.System.WIFI_STATIC_DNS1), false, this);
2420            cr.registerContentObserver(Settings.System.getUriFor(
2421                Settings.System.WIFI_STATIC_DNS2), false, this);
2422        }
2423
2424        public void onChange(boolean selfChange) {
2425            super.onChange(selfChange);
2426
2427            boolean wasStaticIp = mUseStaticIp;
2428            int oIp, oGw, oMsk, oDns1, oDns2;
2429            oIp = oGw = oMsk = oDns1 = oDns2 = 0;
2430            if (wasStaticIp) {
2431                oIp = mDhcpInfo.ipAddress;
2432                oGw = mDhcpInfo.gateway;
2433                oMsk = mDhcpInfo.netmask;
2434                oDns1 = mDhcpInfo.dns1;
2435                oDns2 = mDhcpInfo.dns2;
2436            }
2437            checkUseStaticIp();
2438
2439            if (mWifiInfo.getSupplicantState() == SupplicantState.UNINITIALIZED) {
2440                return;
2441            }
2442
2443            boolean changed =
2444                (wasStaticIp != mUseStaticIp) ||
2445                    (wasStaticIp && (
2446                        oIp   != mDhcpInfo.ipAddress ||
2447                        oGw   != mDhcpInfo.gateway ||
2448                        oMsk  != mDhcpInfo.netmask ||
2449                        oDns1 != mDhcpInfo.dns1 ||
2450                        oDns2 != mDhcpInfo.dns2));
2451
2452            if (changed) {
2453                resetConnections(true);
2454                configureInterface();
2455                if (mUseStaticIp) {
2456                    mTarget.sendEmptyMessage(EVENT_CONFIGURATION_CHANGED);
2457                }
2458            }
2459        }
2460    }
2461
2462    private class NotificationEnabledSettingObserver extends ContentObserver {
2463
2464        public NotificationEnabledSettingObserver(Handler handler) {
2465            super(handler);
2466        }
2467
2468        public void register() {
2469            ContentResolver cr = mContext.getContentResolver();
2470            cr.registerContentObserver(Settings.Secure.getUriFor(
2471                Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON), true, this);
2472            mNotificationEnabled = getValue();
2473        }
2474
2475        @Override
2476        public void onChange(boolean selfChange) {
2477            super.onChange(selfChange);
2478
2479            mNotificationEnabled = getValue();
2480            if (!mNotificationEnabled) {
2481                // Remove any notification that may be showing
2482                setNotificationVisible(false, 0, true, 0);
2483            }
2484
2485            resetNotificationTimer();
2486        }
2487
2488        private boolean getValue() {
2489            return Settings.Secure.getInt(mContext.getContentResolver(),
2490                    Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 1) == 1;
2491        }
2492    }
2493}
2494