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 android.annotation.SdkConstant;
20import android.annotation.SdkConstant.SdkConstantType;
21import android.content.Context;
22import android.net.DhcpInfo;
23import android.os.Binder;
24import android.os.IBinder;
25import android.os.Handler;
26import android.os.HandlerThread;
27import android.os.Looper;
28import android.os.Message;
29import android.os.RemoteException;
30import android.os.WorkSource;
31import android.os.Messenger;
32import android.util.Log;
33import android.util.SparseArray;
34
35import java.util.concurrent.CountDownLatch;
36
37import com.android.internal.util.AsyncChannel;
38import com.android.internal.util.Protocol;
39
40import java.util.List;
41
42/**
43 * This class provides the primary API for managing all aspects of Wi-Fi
44 * connectivity. Get an instance of this class by calling
45 * {@link android.content.Context#getSystemService(String) Context.getSystemService(Context.WIFI_SERVICE)}.
46
47 * It deals with several categories of items:
48 * <ul>
49 * <li>The list of configured networks. The list can be viewed and updated,
50 * and attributes of individual entries can be modified.</li>
51 * <li>The currently active Wi-Fi network, if any. Connectivity can be
52 * established or torn down, and dynamic information about the state of
53 * the network can be queried.</li>
54 * <li>Results of access point scans, containing enough information to
55 * make decisions about what access point to connect to.</li>
56 * <li>It defines the names of various Intent actions that are broadcast
57 * upon any sort of change in Wi-Fi state.
58 * </ul>
59 * This is the API to use when performing Wi-Fi specific operations. To
60 * perform operations that pertain to network connectivity at an abstract
61 * level, use {@link android.net.ConnectivityManager}.
62 */
63public class WifiManager {
64
65    private static final String TAG = "WifiManager";
66    // Supplicant error codes:
67    /**
68     * The error code if there was a problem authenticating.
69     */
70    public static final int ERROR_AUTHENTICATING = 1;
71
72    /**
73     * Broadcast intent action indicating that Wi-Fi has been enabled, disabled,
74     * enabling, disabling, or unknown. One extra provides this state as an int.
75     * Another extra provides the previous state, if available.
76     *
77     * @see #EXTRA_WIFI_STATE
78     * @see #EXTRA_PREVIOUS_WIFI_STATE
79     */
80    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
81    public static final String WIFI_STATE_CHANGED_ACTION =
82        "android.net.wifi.WIFI_STATE_CHANGED";
83    /**
84     * The lookup key for an int that indicates whether Wi-Fi is enabled,
85     * disabled, enabling, disabling, or unknown.  Retrieve it with
86     * {@link android.content.Intent#getIntExtra(String,int)}.
87     *
88     * @see #WIFI_STATE_DISABLED
89     * @see #WIFI_STATE_DISABLING
90     * @see #WIFI_STATE_ENABLED
91     * @see #WIFI_STATE_ENABLING
92     * @see #WIFI_STATE_UNKNOWN
93     */
94    public static final String EXTRA_WIFI_STATE = "wifi_state";
95    /**
96     * The previous Wi-Fi state.
97     *
98     * @see #EXTRA_WIFI_STATE
99     */
100    public static final String EXTRA_PREVIOUS_WIFI_STATE = "previous_wifi_state";
101
102    /**
103     * Wi-Fi is currently being disabled. The state will change to {@link #WIFI_STATE_DISABLED} if
104     * it finishes successfully.
105     *
106     * @see #WIFI_STATE_CHANGED_ACTION
107     * @see #getWifiState()
108     */
109    public static final int WIFI_STATE_DISABLING = 0;
110    /**
111     * Wi-Fi is disabled.
112     *
113     * @see #WIFI_STATE_CHANGED_ACTION
114     * @see #getWifiState()
115     */
116    public static final int WIFI_STATE_DISABLED = 1;
117    /**
118     * Wi-Fi is currently being enabled. The state will change to {@link #WIFI_STATE_ENABLED} if
119     * it finishes successfully.
120     *
121     * @see #WIFI_STATE_CHANGED_ACTION
122     * @see #getWifiState()
123     */
124    public static final int WIFI_STATE_ENABLING = 2;
125    /**
126     * Wi-Fi is enabled.
127     *
128     * @see #WIFI_STATE_CHANGED_ACTION
129     * @see #getWifiState()
130     */
131    public static final int WIFI_STATE_ENABLED = 3;
132    /**
133     * Wi-Fi is in an unknown state. This state will occur when an error happens while enabling
134     * or disabling.
135     *
136     * @see #WIFI_STATE_CHANGED_ACTION
137     * @see #getWifiState()
138     */
139    public static final int WIFI_STATE_UNKNOWN = 4;
140
141    /**
142     * Broadcast intent action indicating that Wi-Fi AP has been enabled, disabled,
143     * enabling, disabling, or failed.
144     *
145     * @hide
146     */
147    public static final String WIFI_AP_STATE_CHANGED_ACTION =
148        "android.net.wifi.WIFI_AP_STATE_CHANGED";
149
150    /**
151     * The lookup key for an int that indicates whether Wi-Fi AP is enabled,
152     * disabled, enabling, disabling, or failed.  Retrieve it with
153     * {@link android.content.Intent#getIntExtra(String,int)}.
154     *
155     * @see #WIFI_AP_STATE_DISABLED
156     * @see #WIFI_AP_STATE_DISABLING
157     * @see #WIFI_AP_STATE_ENABLED
158     * @see #WIFI_AP_STATE_ENABLING
159     * @see #WIFI_AP_STATE_FAILED
160     *
161     * @hide
162     */
163    public static final String EXTRA_WIFI_AP_STATE = "wifi_state";
164    /**
165     * The previous Wi-Fi state.
166     *
167     * @see #EXTRA_WIFI_AP_STATE
168     *
169     * @hide
170     */
171    public static final String EXTRA_PREVIOUS_WIFI_AP_STATE = "previous_wifi_state";
172    /**
173     * Wi-Fi AP is currently being disabled. The state will change to
174     * {@link #WIFI_AP_STATE_DISABLED} if it finishes successfully.
175     *
176     * @see #WIFI_AP_STATE_CHANGED_ACTION
177     * @see #getWifiApState()
178     *
179     * @hide
180     */
181    public static final int WIFI_AP_STATE_DISABLING = 10;
182    /**
183     * Wi-Fi AP is disabled.
184     *
185     * @see #WIFI_AP_STATE_CHANGED_ACTION
186     * @see #getWifiState()
187     *
188     * @hide
189     */
190    public static final int WIFI_AP_STATE_DISABLED = 11;
191    /**
192     * Wi-Fi AP is currently being enabled. The state will change to
193     * {@link #WIFI_AP_STATE_ENABLED} if it finishes successfully.
194     *
195     * @see #WIFI_AP_STATE_CHANGED_ACTION
196     * @see #getWifiApState()
197     *
198     * @hide
199     */
200    public static final int WIFI_AP_STATE_ENABLING = 12;
201    /**
202     * Wi-Fi AP is enabled.
203     *
204     * @see #WIFI_AP_STATE_CHANGED_ACTION
205     * @see #getWifiApState()
206     *
207     * @hide
208     */
209    public static final int WIFI_AP_STATE_ENABLED = 13;
210    /**
211     * Wi-Fi AP is in a failed state. This state will occur when an error occurs during
212     * enabling or disabling
213     *
214     * @see #WIFI_AP_STATE_CHANGED_ACTION
215     * @see #getWifiApState()
216     *
217     * @hide
218     */
219    public static final int WIFI_AP_STATE_FAILED = 14;
220
221    /**
222     * Broadcast intent action indicating that a connection to the supplicant has
223     * been established (and it is now possible
224     * to perform Wi-Fi operations) or the connection to the supplicant has been
225     * lost. One extra provides the connection state as a boolean, where {@code true}
226     * means CONNECTED.
227     * @see #EXTRA_SUPPLICANT_CONNECTED
228     */
229    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
230    public static final String SUPPLICANT_CONNECTION_CHANGE_ACTION =
231        "android.net.wifi.supplicant.CONNECTION_CHANGE";
232    /**
233     * The lookup key for a boolean that indicates whether a connection to
234     * the supplicant daemon has been gained or lost. {@code true} means
235     * a connection now exists.
236     * Retrieve it with {@link android.content.Intent#getBooleanExtra(String,boolean)}.
237     */
238    public static final String EXTRA_SUPPLICANT_CONNECTED = "connected";
239    /**
240     * Broadcast intent action indicating that the state of Wi-Fi connectivity
241     * has changed. One extra provides the new state
242     * in the form of a {@link android.net.NetworkInfo} object. If the new
243     * state is CONNECTED, additional extras may provide the BSSID and WifiInfo of
244     * the access point.
245     * as a {@code String}.
246     * @see #EXTRA_NETWORK_INFO
247     * @see #EXTRA_BSSID
248     * @see #EXTRA_WIFI_INFO
249     */
250    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
251    public static final String NETWORK_STATE_CHANGED_ACTION = "android.net.wifi.STATE_CHANGE";
252    /**
253     * The lookup key for a {@link android.net.NetworkInfo} object associated with the
254     * Wi-Fi network. Retrieve with
255     * {@link android.content.Intent#getParcelableExtra(String)}.
256     */
257    public static final String EXTRA_NETWORK_INFO = "networkInfo";
258    /**
259     * The lookup key for a String giving the BSSID of the access point to which
260     * we are connected. Only present when the new state is CONNECTED.
261     * Retrieve with
262     * {@link android.content.Intent#getStringExtra(String)}.
263     */
264    public static final String EXTRA_BSSID = "bssid";
265    /**
266     * The lookup key for a {@link android.net.wifi.WifiInfo} object giving the
267     * information about the access point to which we are connected. Only present
268     * when the new state is CONNECTED.  Retrieve with
269     * {@link android.content.Intent#getParcelableExtra(String)}.
270     */
271    public static final String EXTRA_WIFI_INFO = "wifiInfo";
272    /**
273     * Broadcast intent action indicating that the state of establishing a connection to
274     * an access point has changed.One extra provides the new
275     * {@link SupplicantState}. Note that the supplicant state is Wi-Fi specific, and
276     * is not generally the most useful thing to look at if you are just interested in
277     * the overall state of connectivity.
278     * @see #EXTRA_NEW_STATE
279     * @see #EXTRA_SUPPLICANT_ERROR
280     */
281    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
282    public static final String SUPPLICANT_STATE_CHANGED_ACTION =
283        "android.net.wifi.supplicant.STATE_CHANGE";
284    /**
285     * The lookup key for a {@link SupplicantState} describing the new state
286     * Retrieve with
287     * {@link android.content.Intent#getParcelableExtra(String)}.
288     */
289    public static final String EXTRA_NEW_STATE = "newState";
290
291    /**
292     * The lookup key for a {@link SupplicantState} describing the supplicant
293     * error code if any
294     * Retrieve with
295     * {@link android.content.Intent#getIntExtra(String, int)}.
296     * @see #ERROR_AUTHENTICATING
297     */
298    public static final String EXTRA_SUPPLICANT_ERROR = "supplicantError";
299
300    /**
301     * Broadcast intent action indicating that the configured networks changed.
302     * This can be as a result of adding/updating/deleting a network. If
303     * {@link #EXTRA_MULTIPLE_NETWORKS_CHANGED} is set to true the new configuration
304     * can be retreived with the {@link #EXTRA_WIFI_CONFIGURATION} extra. If multiple
305     * Wi-Fi configurations changed, {@link #EXTRA_WIFI_CONFIGURATION} will not be present.
306     * @hide
307     */
308    public static final String CONFIGURED_NETWORKS_CHANGED_ACTION =
309        "android.net.wifi.CONFIGURED_NETWORKS_CHANGE";
310    /**
311     * The lookup key for a (@link android.net.wifi.WifiConfiguration} object representing
312     * the changed Wi-Fi configuration when the {@link #CONFIGURED_NETWORKS_CHANGED_ACTION}
313     * broadcast is sent.
314     * @hide
315     */
316    public static final String EXTRA_WIFI_CONFIGURATION = "wifiConfiguration";
317    /**
318     * Multiple network configurations have changed.
319     * @see #CONFIGURED_NETWORKS_CHANGED_ACTION
320     *
321     * @hide
322     */
323    public static final String EXTRA_MULTIPLE_NETWORKS_CHANGED = "multipleChanges";
324    /**
325     * The lookup key for an integer indicating the reason a Wi-Fi network configuration
326     * has changed. Only present if {@link #EXTRA_MULTIPLE_NETWORKS_CHANGED} is {@code false}
327     * @see #CONFIGURED_NETWORKS_CHANGED_ACTION
328     * @hide
329     */
330    public static final String EXTRA_CHANGE_REASON = "changeReason";
331    /**
332     * The configuration is new and was added.
333     * @hide
334     */
335    public static final int CHANGE_REASON_ADDED = 0;
336    /**
337     * The configuration was removed and is no longer present in the system's list of
338     * configured networks.
339     * @hide
340     */
341    public static final int CHANGE_REASON_REMOVED = 1;
342    /**
343     * The configuration has changed as a result of explicit action or because the system
344     * took an automated action such as disabling a malfunctioning configuration.
345     * @hide
346     */
347    public static final int CHANGE_REASON_CONFIG_CHANGE = 2;
348    /**
349     * An access point scan has completed, and results are available from the supplicant.
350     * Call {@link #getScanResults()} to obtain the results.
351     */
352    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
353    public static final String SCAN_RESULTS_AVAILABLE_ACTION = "android.net.wifi.SCAN_RESULTS";
354    /**
355     * The RSSI (signal strength) has changed.
356     * @see #EXTRA_NEW_RSSI
357     */
358    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
359    public static final String RSSI_CHANGED_ACTION = "android.net.wifi.RSSI_CHANGED";
360    /**
361     * The lookup key for an {@code int} giving the new RSSI in dBm.
362     */
363    public static final String EXTRA_NEW_RSSI = "newRssi";
364
365    /**
366     * Broadcast intent action indicating that the link configuration
367     * changed on wifi.
368     * @hide
369     */
370    public static final String LINK_CONFIGURATION_CHANGED_ACTION =
371        "android.net.wifi.LINK_CONFIGURATION_CHANGED";
372
373    /**
374     * The lookup key for a {@link android.net.LinkProperties} object associated with the
375     * Wi-Fi network. Retrieve with
376     * {@link android.content.Intent#getParcelableExtra(String)}.
377     * @hide
378     */
379    public static final String EXTRA_LINK_PROPERTIES = "linkProperties";
380
381    /**
382     * The lookup key for a {@link android.net.LinkCapabilities} object associated with the
383     * Wi-Fi network. Retrieve with
384     * {@link android.content.Intent#getParcelableExtra(String)}.
385     * @hide
386     */
387    public static final String EXTRA_LINK_CAPABILITIES = "linkCapabilities";
388
389    /**
390     * The network IDs of the configured networks could have changed.
391     */
392    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
393    public static final String NETWORK_IDS_CHANGED_ACTION = "android.net.wifi.NETWORK_IDS_CHANGED";
394
395    /**
396     * Activity Action: Pick a Wi-Fi network to connect to.
397     * <p>Input: Nothing.
398     * <p>Output: Nothing.
399     */
400    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
401    public static final String ACTION_PICK_WIFI_NETWORK = "android.net.wifi.PICK_WIFI_NETWORK";
402
403    /**
404     * In this Wi-Fi lock mode, Wi-Fi will be kept active,
405     * and will behave normally, i.e., it will attempt to automatically
406     * establish a connection to a remembered access point that is
407     * within range, and will do periodic scans if there are remembered
408     * access points but none are in range.
409     */
410    public static final int WIFI_MODE_FULL = 1;
411    /**
412     * In this Wi-Fi lock mode, Wi-Fi will be kept active,
413     * but the only operation that will be supported is initiation of
414     * scans, and the subsequent reporting of scan results. No attempts
415     * will be made to automatically connect to remembered access points,
416     * nor will periodic scans be automatically performed looking for
417     * remembered access points. Scans must be explicitly requested by
418     * an application in this mode.
419     */
420    public static final int WIFI_MODE_SCAN_ONLY = 2;
421    /**
422     * In this Wi-Fi lock mode, Wi-Fi will be kept active as in mode
423     * {@link #WIFI_MODE_FULL} but it operates at high performance
424     * with minimum packet loss and low packet latency even when
425     * the device screen is off. This mode will consume more power
426     * and hence should be used only when there is a need for such
427     * an active connection.
428     * <p>
429     * An example use case is when a voice connection needs to be
430     * kept active even after the device screen goes off. Holding the
431     * regular {@link #WIFI_MODE_FULL} lock will keep the wifi
432     * connection active, but the connection can be lossy.
433     * Holding a {@link #WIFI_MODE_FULL_HIGH_PERF} lock for the
434     * duration of the voice call will improve the call quality.
435     * <p>
436     * When there is no support from the hardware, this lock mode
437     * will have the same behavior as {@link #WIFI_MODE_FULL}
438     */
439    public static final int WIFI_MODE_FULL_HIGH_PERF = 3;
440
441    /** Anything worse than or equal to this will show 0 bars. */
442    private static final int MIN_RSSI = -100;
443
444    /** Anything better than or equal to this will show the max bars. */
445    private static final int MAX_RSSI = -55;
446
447    /**
448     * Number of RSSI levels used in the framework to initiate
449     * {@link #RSSI_CHANGED_ACTION} broadcast
450     * @hide
451     */
452    public static final int RSSI_LEVELS = 5;
453
454    /**
455     * Auto settings in the driver. The driver could choose to operate on both
456     * 2.4 GHz and 5 GHz or make a dynamic decision on selecting the band.
457     * @hide
458     */
459    public static final int WIFI_FREQUENCY_BAND_AUTO = 0;
460
461    /**
462     * Operation on 5 GHz alone
463     * @hide
464     */
465    public static final int WIFI_FREQUENCY_BAND_5GHZ = 1;
466
467    /**
468     * Operation on 2.4 GHz alone
469     * @hide
470     */
471    public static final int WIFI_FREQUENCY_BAND_2GHZ = 2;
472
473    /** List of asyncronous notifications
474     * @hide
475     */
476    public static final int DATA_ACTIVITY_NOTIFICATION = 1;
477
478    //Lowest bit indicates data reception and the second lowest
479    //bit indicates data transmitted
480    /** @hide */
481    public static final int DATA_ACTIVITY_NONE         = 0x00;
482    /** @hide */
483    public static final int DATA_ACTIVITY_IN           = 0x01;
484    /** @hide */
485    public static final int DATA_ACTIVITY_OUT          = 0x02;
486    /** @hide */
487    public static final int DATA_ACTIVITY_INOUT        = 0x03;
488
489    /* Maximum number of active locks we allow.
490     * This limit was added to prevent apps from creating a ridiculous number
491     * of locks and crashing the system by overflowing the global ref table.
492     */
493    private static final int MAX_ACTIVE_LOCKS = 50;
494
495    /* Number of currently active WifiLocks and MulticastLocks */
496    private int mActiveLockCount;
497
498    private Context mContext;
499    IWifiManager mService;
500
501    private static final int INVALID_KEY = 0;
502    private int mListenerKey = 1;
503    private final SparseArray mListenerMap = new SparseArray();
504    private final Object mListenerMapLock = new Object();
505
506    private AsyncChannel mAsyncChannel = new AsyncChannel();
507    private ServiceHandler mHandler;
508    private Messenger mWifiServiceMessenger;
509    private final CountDownLatch mConnected = new CountDownLatch(1);
510
511    private static Object sThreadRefLock = new Object();
512    private static int sThreadRefCount;
513    private static HandlerThread sHandlerThread;
514
515    /**
516     * Create a new WifiManager instance.
517     * Applications will almost always want to use
518     * {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve
519     * the standard {@link android.content.Context#WIFI_SERVICE Context.WIFI_SERVICE}.
520     * @param context the application context
521     * @param service the Binder interface
522     * @hide - hide this because it takes in a parameter of type IWifiManager, which
523     * is a system private class.
524     */
525    public WifiManager(Context context, IWifiManager service) {
526        mContext = context;
527        mService = service;
528        init();
529    }
530
531    /**
532     * Return a list of all the networks configured in the supplicant.
533     * Not all fields of WifiConfiguration are returned. Only the following
534     * fields are filled in:
535     * <ul>
536     * <li>networkId</li>
537     * <li>SSID</li>
538     * <li>BSSID</li>
539     * <li>priority</li>
540     * <li>allowedProtocols</li>
541     * <li>allowedKeyManagement</li>
542     * <li>allowedAuthAlgorithms</li>
543     * <li>allowedPairwiseCiphers</li>
544     * <li>allowedGroupCiphers</li>
545     * </ul>
546     * @return a list of network configurations in the form of a list
547     * of {@link WifiConfiguration} objects. Upon failure to fetch or
548     * when when Wi-Fi is turned off, it can be null.
549     */
550    public List<WifiConfiguration> getConfiguredNetworks() {
551        try {
552            return mService.getConfiguredNetworks();
553        } catch (RemoteException e) {
554            return null;
555        }
556    }
557
558    /**
559     * Add a new network description to the set of configured networks.
560     * The {@code networkId} field of the supplied configuration object
561     * is ignored.
562     * <p/>
563     * The new network will be marked DISABLED by default. To enable it,
564     * called {@link #enableNetwork}.
565     *
566     * @param config the set of variables that describe the configuration,
567     *            contained in a {@link WifiConfiguration} object.
568     * @return the ID of the newly created network description. This is used in
569     *         other operations to specified the network to be acted upon.
570     *         Returns {@code -1} on failure.
571     */
572    public int addNetwork(WifiConfiguration config) {
573        if (config == null) {
574            return -1;
575        }
576        config.networkId = -1;
577        return addOrUpdateNetwork(config);
578    }
579
580    /**
581     * Update the network description of an existing configured network.
582     *
583     * @param config the set of variables that describe the configuration,
584     *            contained in a {@link WifiConfiguration} object. It may
585     *            be sparse, so that only the items that are being changed
586     *            are non-<code>null</code>. The {@code networkId} field
587     *            must be set to the ID of the existing network being updated.
588     * @return Returns the {@code networkId} of the supplied
589     *         {@code WifiConfiguration} on success.
590     *         <br/>
591     *         Returns {@code -1} on failure, including when the {@code networkId}
592     *         field of the {@code WifiConfiguration} does not refer to an
593     *         existing network.
594     */
595    public int updateNetwork(WifiConfiguration config) {
596        if (config == null || config.networkId < 0) {
597            return -1;
598        }
599        return addOrUpdateNetwork(config);
600    }
601
602    /**
603     * Internal method for doing the RPC that creates a new network description
604     * or updates an existing one.
605     *
606     * @param config The possibly sparse object containing the variables that
607     *         are to set or updated in the network description.
608     * @return the ID of the network on success, {@code -1} on failure.
609     */
610    private int addOrUpdateNetwork(WifiConfiguration config) {
611        try {
612            return mService.addOrUpdateNetwork(config);
613        } catch (RemoteException e) {
614            return -1;
615        }
616    }
617
618    /**
619     * Remove the specified network from the list of configured networks.
620     * This may result in the asynchronous delivery of state change
621     * events.
622     * @param netId the integer that identifies the network configuration
623     * to the supplicant
624     * @return {@code true} if the operation succeeded
625     */
626    public boolean removeNetwork(int netId) {
627        try {
628            return mService.removeNetwork(netId);
629        } catch (RemoteException e) {
630            return false;
631        }
632    }
633
634    /**
635     * Allow a previously configured network to be associated with. If
636     * <code>disableOthers</code> is true, then all other configured
637     * networks are disabled, and an attempt to connect to the selected
638     * network is initiated. This may result in the asynchronous delivery
639     * of state change events.
640     * @param netId the ID of the network in the list of configured networks
641     * @param disableOthers if true, disable all other networks. The way to
642     * select a particular network to connect to is specify {@code true}
643     * for this parameter.
644     * @return {@code true} if the operation succeeded
645     */
646    public boolean enableNetwork(int netId, boolean disableOthers) {
647        try {
648            return mService.enableNetwork(netId, disableOthers);
649        } catch (RemoteException e) {
650            return false;
651        }
652    }
653
654    /**
655     * Disable a configured network. The specified network will not be
656     * a candidate for associating. This may result in the asynchronous
657     * delivery of state change events.
658     * @param netId the ID of the network as returned by {@link #addNetwork}.
659     * @return {@code true} if the operation succeeded
660     */
661    public boolean disableNetwork(int netId) {
662        try {
663            return mService.disableNetwork(netId);
664        } catch (RemoteException e) {
665            return false;
666        }
667    }
668
669    /**
670     * Disassociate from the currently active access point. This may result
671     * in the asynchronous delivery of state change events.
672     * @return {@code true} if the operation succeeded
673     */
674    public boolean disconnect() {
675        try {
676            mService.disconnect();
677            return true;
678        } catch (RemoteException e) {
679            return false;
680        }
681    }
682
683    /**
684     * Reconnect to the currently active access point, if we are currently
685     * disconnected. This may result in the asynchronous delivery of state
686     * change events.
687     * @return {@code true} if the operation succeeded
688     */
689    public boolean reconnect() {
690        try {
691            mService.reconnect();
692            return true;
693        } catch (RemoteException e) {
694            return false;
695        }
696    }
697
698    /**
699     * Reconnect to the currently active access point, even if we are already
700     * connected. This may result in the asynchronous delivery of state
701     * change events.
702     * @return {@code true} if the operation succeeded
703     */
704    public boolean reassociate() {
705        try {
706            mService.reassociate();
707            return true;
708        } catch (RemoteException e) {
709            return false;
710        }
711    }
712
713    /**
714     * Check that the supplicant daemon is responding to requests.
715     * @return {@code true} if we were able to communicate with the supplicant and
716     * it returned the expected response to the PING message.
717     */
718    public boolean pingSupplicant() {
719        if (mService == null)
720            return false;
721        try {
722            return mService.pingSupplicant();
723        } catch (RemoteException e) {
724            return false;
725        }
726    }
727
728    /**
729     * Request a scan for access points. Returns immediately. The availability
730     * of the results is made known later by means of an asynchronous event sent
731     * on completion of the scan.
732     * @return {@code true} if the operation succeeded, i.e., the scan was initiated
733     */
734    public boolean startScan() {
735        try {
736            mService.startScan(false);
737            return true;
738        } catch (RemoteException e) {
739            return false;
740        }
741    }
742
743    /**
744     * Request a scan for access points. Returns immediately. The availability
745     * of the results is made known later by means of an asynchronous event sent
746     * on completion of the scan.
747     * This is a variant of startScan that forces an active scan, even if passive
748     * scans are the current default
749     * @return {@code true} if the operation succeeded, i.e., the scan was initiated
750     *
751     * @hide
752     */
753    public boolean startScanActive() {
754        try {
755            mService.startScan(true);
756            return true;
757        } catch (RemoteException e) {
758            return false;
759        }
760    }
761
762    /**
763     * Return dynamic information about the current Wi-Fi connection, if any is active.
764     * @return the Wi-Fi information, contained in {@link WifiInfo}.
765     */
766    public WifiInfo getConnectionInfo() {
767        try {
768            return mService.getConnectionInfo();
769        } catch (RemoteException e) {
770            return null;
771        }
772    }
773
774    /**
775     * Return the results of the latest access point scan.
776     * @return the list of access points found in the most recent scan.
777     */
778    public List<ScanResult> getScanResults() {
779        try {
780            return mService.getScanResults();
781        } catch (RemoteException e) {
782            return null;
783        }
784    }
785
786    /**
787     * Tell the supplicant to persist the current list of configured networks.
788     * <p>
789     * Note: It is possible for this method to change the network IDs of
790     * existing networks. You should assume the network IDs can be different
791     * after calling this method.
792     *
793     * @return {@code true} if the operation succeeded
794     */
795    public boolean saveConfiguration() {
796        try {
797            return mService.saveConfiguration();
798        } catch (RemoteException e) {
799            return false;
800        }
801    }
802
803    /**
804     * Set the country code.
805     * @param countryCode country code in ISO 3166 format.
806     * @param persist {@code true} if this needs to be remembered
807     *
808     * @hide
809     */
810    public void setCountryCode(String country, boolean persist) {
811        try {
812            mService.setCountryCode(country, persist);
813        } catch (RemoteException e) { }
814    }
815
816    /**
817     * Set the operational frequency band.
818     * @param band  One of
819     *     {@link #WIFI_FREQUENCY_BAND_AUTO},
820     *     {@link #WIFI_FREQUENCY_BAND_5GHZ},
821     *     {@link #WIFI_FREQUENCY_BAND_2GHZ},
822     * @param persist {@code true} if this needs to be remembered
823     * @hide
824     */
825    public void setFrequencyBand(int band, boolean persist) {
826        try {
827            mService.setFrequencyBand(band, persist);
828        } catch (RemoteException e) { }
829    }
830
831    /**
832     * Get the operational frequency band.
833     * @return One of
834     *     {@link #WIFI_FREQUENCY_BAND_AUTO},
835     *     {@link #WIFI_FREQUENCY_BAND_5GHZ},
836     *     {@link #WIFI_FREQUENCY_BAND_2GHZ} or
837     *     {@code -1} on failure.
838     * @hide
839     */
840    public int getFrequencyBand() {
841        try {
842            return mService.getFrequencyBand();
843        } catch (RemoteException e) {
844            return -1;
845        }
846    }
847
848    /**
849     * Check if the chipset supports dual frequency band (2.4 GHz and 5 GHz)
850     * @return {@code true} if supported, {@code false} otherwise.
851     * @hide
852     */
853    public boolean isDualBandSupported() {
854        try {
855            return mService.isDualBandSupported();
856        } catch (RemoteException e) {
857            return false;
858        }
859    }
860
861    /**
862     * Return the DHCP-assigned addresses from the last successful DHCP request,
863     * if any.
864     * @return the DHCP information
865     */
866    public DhcpInfo getDhcpInfo() {
867        try {
868            return mService.getDhcpInfo();
869        } catch (RemoteException e) {
870            return null;
871        }
872    }
873
874
875    /**
876     * Enable or disable Wi-Fi.
877     * @param enabled {@code true} to enable, {@code false} to disable.
878     * @return {@code true} if the operation succeeds (or if the existing state
879     *         is the same as the requested state).
880     */
881    public boolean setWifiEnabled(boolean enabled) {
882        try {
883            return mService.setWifiEnabled(enabled);
884        } catch (RemoteException e) {
885            return false;
886        }
887    }
888
889    /**
890     * Gets the Wi-Fi enabled state.
891     * @return One of {@link #WIFI_STATE_DISABLED},
892     *         {@link #WIFI_STATE_DISABLING}, {@link #WIFI_STATE_ENABLED},
893     *         {@link #WIFI_STATE_ENABLING}, {@link #WIFI_STATE_UNKNOWN}
894     * @see #isWifiEnabled()
895     */
896    public int getWifiState() {
897        try {
898            return mService.getWifiEnabledState();
899        } catch (RemoteException e) {
900            return WIFI_STATE_UNKNOWN;
901        }
902    }
903
904    /**
905     * Return whether Wi-Fi is enabled or disabled.
906     * @return {@code true} if Wi-Fi is enabled
907     * @see #getWifiState()
908     */
909    public boolean isWifiEnabled() {
910        return getWifiState() == WIFI_STATE_ENABLED;
911    }
912
913    /**
914     * Return TX packet counter, for CTS test of WiFi watchdog.
915     * @param listener is the interface to receive result
916     *
917     * @hide for CTS test only
918     */
919    public void getTxPacketCount(TxPacketCountListener listener) {
920        validateChannel();
921        mAsyncChannel.sendMessage(RSSI_PKTCNT_FETCH, 0, putListener(listener));
922    }
923
924    /**
925     * Calculates the level of the signal. This should be used any time a signal
926     * is being shown.
927     *
928     * @param rssi The power of the signal measured in RSSI.
929     * @param numLevels The number of levels to consider in the calculated
930     *            level.
931     * @return A level of the signal, given in the range of 0 to numLevels-1
932     *         (both inclusive).
933     */
934    public static int calculateSignalLevel(int rssi, int numLevels) {
935        if (rssi <= MIN_RSSI) {
936            return 0;
937        } else if (rssi >= MAX_RSSI) {
938            return numLevels - 1;
939        } else {
940            float inputRange = (MAX_RSSI - MIN_RSSI);
941            float outputRange = (numLevels - 1);
942            return (int)((float)(rssi - MIN_RSSI) * outputRange / inputRange);
943        }
944    }
945
946    /**
947     * Compares two signal strengths.
948     *
949     * @param rssiA The power of the first signal measured in RSSI.
950     * @param rssiB The power of the second signal measured in RSSI.
951     * @return Returns <0 if the first signal is weaker than the second signal,
952     *         0 if the two signals have the same strength, and >0 if the first
953     *         signal is stronger than the second signal.
954     */
955    public static int compareSignalLevel(int rssiA, int rssiB) {
956        return rssiA - rssiB;
957    }
958
959    /**
960     * Start AccessPoint mode with the specified
961     * configuration. If the radio is already running in
962     * AP mode, update the new configuration
963     * Note that starting in access point mode disables station
964     * mode operation
965     * @param wifiConfig SSID, security and channel details as
966     *        part of WifiConfiguration
967     * @return {@code true} if the operation succeeds, {@code false} otherwise
968     *
969     * @hide Dont open up yet
970     */
971    public boolean setWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled) {
972        try {
973            mService.setWifiApEnabled(wifiConfig, enabled);
974            return true;
975        } catch (RemoteException e) {
976            return false;
977        }
978    }
979
980    /**
981     * Gets the Wi-Fi enabled state.
982     * @return One of {@link #WIFI_AP_STATE_DISABLED},
983     *         {@link #WIFI_AP_STATE_DISABLING}, {@link #WIFI_AP_STATE_ENABLED},
984     *         {@link #WIFI_AP_STATE_ENABLING}, {@link #WIFI_AP_STATE_FAILED}
985     * @see #isWifiApEnabled()
986     *
987     * @hide Dont open yet
988     */
989    public int getWifiApState() {
990        try {
991            return mService.getWifiApEnabledState();
992        } catch (RemoteException e) {
993            return WIFI_AP_STATE_FAILED;
994        }
995    }
996
997    /**
998     * Return whether Wi-Fi AP is enabled or disabled.
999     * @return {@code true} if Wi-Fi AP is enabled
1000     * @see #getWifiApState()
1001     *
1002     * @hide Dont open yet
1003     */
1004    public boolean isWifiApEnabled() {
1005        return getWifiApState() == WIFI_AP_STATE_ENABLED;
1006    }
1007
1008    /**
1009     * Gets the Wi-Fi AP Configuration.
1010     * @return AP details in WifiConfiguration
1011     *
1012     * @hide Dont open yet
1013     */
1014    public WifiConfiguration getWifiApConfiguration() {
1015        try {
1016            return mService.getWifiApConfiguration();
1017        } catch (RemoteException e) {
1018            return null;
1019        }
1020    }
1021
1022    /**
1023     * Sets the Wi-Fi AP Configuration.
1024     * @return {@code true} if the operation succeeded, {@code false} otherwise
1025     *
1026     * @hide Dont open yet
1027     */
1028    public boolean setWifiApConfiguration(WifiConfiguration wifiConfig) {
1029        try {
1030            mService.setWifiApConfiguration(wifiConfig);
1031            return true;
1032        } catch (RemoteException e) {
1033            return false;
1034        }
1035    }
1036
1037   /**
1038     * Start the driver and connect to network.
1039     *
1040     * This function will over-ride WifiLock and device idle status. For example,
1041     * even if the device is idle or there is only a scan-only lock held,
1042     * a start wifi would mean that wifi connection is kept active until
1043     * a stopWifi() is sent.
1044     *
1045     * This API is used by WifiStateTracker
1046     *
1047     * @return {@code true} if the operation succeeds else {@code false}
1048     * @hide
1049     */
1050    public boolean startWifi() {
1051        try {
1052            mService.startWifi();
1053            return true;
1054        } catch (RemoteException e) {
1055            return false;
1056        }
1057    }
1058
1059    /**
1060     * Disconnect from a network (if any) and stop the driver.
1061     *
1062     * This function will over-ride WifiLock and device idle status. Wi-Fi
1063     * stays inactive until a startWifi() is issued.
1064     *
1065     * This API is used by WifiStateTracker
1066     *
1067     * @return {@code true} if the operation succeeds else {@code false}
1068     * @hide
1069     */
1070    public boolean stopWifi() {
1071        try {
1072            mService.stopWifi();
1073            return true;
1074        } catch (RemoteException e) {
1075            return false;
1076        }
1077    }
1078
1079    /**
1080     * Add a bssid to the supplicant blacklist
1081     *
1082     * This API is used by WifiWatchdogService
1083     *
1084     * @return {@code true} if the operation succeeds else {@code false}
1085     * @hide
1086     */
1087    public boolean addToBlacklist(String bssid) {
1088        try {
1089            mService.addToBlacklist(bssid);
1090            return true;
1091        } catch (RemoteException e) {
1092            return false;
1093        }
1094    }
1095
1096    /**
1097     * Clear the supplicant blacklist
1098     *
1099     * This API is used by WifiWatchdogService
1100     *
1101     * @return {@code true} if the operation succeeds else {@code false}
1102     * @hide
1103     */
1104    public boolean clearBlacklist() {
1105        try {
1106            mService.clearBlacklist();
1107            return true;
1108        } catch (RemoteException e) {
1109            return false;
1110        }
1111    }
1112
1113    /* TODO: deprecate synchronous API and open up the following API */
1114
1115    private static final int BASE = Protocol.BASE_WIFI_MANAGER;
1116
1117    /* Commands to WifiService */
1118    /** @hide */
1119    public static final int CONNECT_NETWORK                 = BASE + 1;
1120    /** @hide */
1121    public static final int CONNECT_NETWORK_FAILED          = BASE + 2;
1122    /** @hide */
1123    public static final int CONNECT_NETWORK_SUCCEEDED       = BASE + 3;
1124
1125    /** @hide */
1126    public static final int FORGET_NETWORK                  = BASE + 4;
1127    /** @hide */
1128    public static final int FORGET_NETWORK_FAILED           = BASE + 5;
1129    /** @hide */
1130    public static final int FORGET_NETWORK_SUCCEEDED        = BASE + 6;
1131
1132    /** @hide */
1133    public static final int SAVE_NETWORK                    = BASE + 7;
1134    /** @hide */
1135    public static final int SAVE_NETWORK_FAILED             = BASE + 8;
1136    /** @hide */
1137    public static final int SAVE_NETWORK_SUCCEEDED          = BASE + 9;
1138
1139    /** @hide */
1140    public static final int START_WPS                       = BASE + 10;
1141    /** @hide */
1142    public static final int START_WPS_SUCCEEDED             = BASE + 11;
1143    /** @hide */
1144    public static final int WPS_FAILED                      = BASE + 12;
1145    /** @hide */
1146    public static final int WPS_COMPLETED                   = BASE + 13;
1147
1148    /** @hide */
1149    public static final int CANCEL_WPS                      = BASE + 14;
1150    /** @hide */
1151    public static final int CANCEL_WPS_FAILED               = BASE + 15;
1152    /** @hide */
1153    public static final int CANCEL_WPS_SUCCEDED             = BASE + 16;
1154
1155    /** @hide */
1156    public static final int DISABLE_NETWORK                 = BASE + 17;
1157    /** @hide */
1158    public static final int DISABLE_NETWORK_FAILED          = BASE + 18;
1159    /** @hide */
1160    public static final int DISABLE_NETWORK_SUCCEEDED       = BASE + 19;
1161
1162    /** @hide */
1163    public static final int RSSI_PKTCNT_FETCH               = BASE + 20;
1164    /** @hide */
1165    public static final int RSSI_PKTCNT_FETCH_SUCCEEDED     = BASE + 21;
1166    /** @hide */
1167    public static final int RSSI_PKTCNT_FETCH_FAILED        = BASE + 22;
1168
1169    /* For system use only */
1170    /** @hide */
1171    public static final int ENABLE_TRAFFIC_STATS_POLL       = BASE + 31;
1172    /** @hide */
1173    public static final int TRAFFIC_STATS_POLL              = BASE + 32;
1174
1175
1176    /**
1177     * Passed with {@link ActionListener#onFailure}.
1178     * Indicates that the operation failed due to an internal error.
1179     * @hide
1180     */
1181    public static final int ERROR                       = 0;
1182
1183    /**
1184     * Passed with {@link ActionListener#onFailure}.
1185     * Indicates that the operation is already in progress
1186     * @hide
1187     */
1188    public static final int IN_PROGRESS                 = 1;
1189
1190    /**
1191     * Passed with {@link ActionListener#onFailure}.
1192     * Indicates that the operation failed because the framework is busy and
1193     * unable to service the request
1194     * @hide
1195     */
1196    public static final int BUSY                        = 2;
1197
1198    /* WPS specific errors */
1199    /** WPS overlap detected {@hide} */
1200    public static final int WPS_OVERLAP_ERROR           = 3;
1201    /** WEP on WPS is prohibited {@hide} */
1202    public static final int WPS_WEP_PROHIBITED          = 4;
1203    /** TKIP only prohibited {@hide} */
1204    public static final int WPS_TKIP_ONLY_PROHIBITED    = 5;
1205    /** Authentication failure on WPS {@hide} */
1206    public static final int WPS_AUTH_FAILURE            = 6;
1207    /** WPS timed out {@hide} */
1208    public static final int WPS_TIMED_OUT               = 7;
1209
1210    /** Interface for callback invocation on an application action {@hide} */
1211    public interface ActionListener {
1212        /** The operation succeeded */
1213        public void onSuccess();
1214        /**
1215         * The operation failed
1216         * @param reason The reason for failure could be one of
1217         * {@link #ERROR}, {@link #IN_PROGRESS} or {@link #BUSY}
1218         */
1219        public void onFailure(int reason);
1220    }
1221
1222    /** Interface for callback invocation on a start WPS action {@hide} */
1223    public interface WpsListener {
1224        /** WPS start succeeded */
1225        public void onStartSuccess(String pin);
1226
1227        /** WPS operation completed succesfully */
1228        public void onCompletion();
1229
1230        /**
1231         * WPS operation failed
1232         * @param reason The reason for failure could be one of
1233         * {@link #IN_PROGRESS}, {@link #WPS_OVERLAP_ERROR},{@link #ERROR} or {@link #BUSY}
1234         */
1235        public void onFailure(int reason);
1236    }
1237
1238    /** Interface for callback invocation on a TX packet count poll action {@hide} */
1239    public interface TxPacketCountListener {
1240        /**
1241         * The operation succeeded
1242         * @param count TX packet counter
1243         */
1244        public void onSuccess(int count);
1245        /**
1246         * The operation failed
1247         * @param reason The reason for failure could be one of
1248         * {@link #ERROR}, {@link #IN_PROGRESS} or {@link #BUSY}
1249         */
1250        public void onFailure(int reason);
1251    }
1252
1253    private class ServiceHandler extends Handler {
1254        ServiceHandler(Looper looper) {
1255            super(looper);
1256        }
1257
1258        @Override
1259        public void handleMessage(Message message) {
1260            Object listener = removeListener(message.arg2);
1261            switch (message.what) {
1262                case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
1263                    if (message.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
1264                        mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
1265                    } else {
1266                        Log.e(TAG, "Failed to set up channel connection");
1267                        // This will cause all further async API calls on the WifiManager
1268                        // to fail and throw an exception
1269                        mAsyncChannel = null;
1270                    }
1271                    mConnected.countDown();
1272                    break;
1273                case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED:
1274                    // Ignore
1275                    break;
1276                case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
1277                    Log.e(TAG, "Channel connection lost");
1278                    // This will cause all further async API calls on the WifiManager
1279                    // to fail and throw an exception
1280                    mAsyncChannel = null;
1281                    getLooper().quit();
1282                    break;
1283                    /* ActionListeners grouped together */
1284                case WifiManager.CONNECT_NETWORK_FAILED:
1285                case WifiManager.FORGET_NETWORK_FAILED:
1286                case WifiManager.SAVE_NETWORK_FAILED:
1287                case WifiManager.CANCEL_WPS_FAILED:
1288                case WifiManager.DISABLE_NETWORK_FAILED:
1289                    if (listener != null) {
1290                        ((ActionListener) listener).onFailure(message.arg1);
1291                    }
1292                    break;
1293                    /* ActionListeners grouped together */
1294                case WifiManager.CONNECT_NETWORK_SUCCEEDED:
1295                case WifiManager.FORGET_NETWORK_SUCCEEDED:
1296                case WifiManager.SAVE_NETWORK_SUCCEEDED:
1297                case WifiManager.CANCEL_WPS_SUCCEDED:
1298                case WifiManager.DISABLE_NETWORK_SUCCEEDED:
1299                    if (listener != null) {
1300                        ((ActionListener) listener).onSuccess();
1301                    }
1302                    break;
1303                case WifiManager.START_WPS_SUCCEEDED:
1304                    if (listener != null) {
1305                        WpsResult result = (WpsResult) message.obj;
1306                        ((WpsListener) listener).onStartSuccess(result.pin);
1307                        //Listener needs to stay until completion or failure
1308                        synchronized(mListenerMapLock) {
1309                            mListenerMap.put(message.arg2, listener);
1310                        }
1311                    }
1312                    break;
1313                case WifiManager.WPS_COMPLETED:
1314                    if (listener != null) {
1315                        ((WpsListener) listener).onCompletion();
1316                    }
1317                    break;
1318                case WifiManager.WPS_FAILED:
1319                    if (listener != null) {
1320                        ((WpsListener) listener).onFailure(message.arg1);
1321                    }
1322                    break;
1323                case WifiManager.RSSI_PKTCNT_FETCH_SUCCEEDED:
1324                    if (listener != null) {
1325                        RssiPacketCountInfo info = (RssiPacketCountInfo) message.obj;
1326                        if (info != null)
1327                            ((TxPacketCountListener) listener).onSuccess(info.txgood + info.txbad);
1328                        else
1329                            ((TxPacketCountListener) listener).onFailure(ERROR);
1330                    }
1331                    break;
1332                case WifiManager.RSSI_PKTCNT_FETCH_FAILED:
1333                    if (listener != null) {
1334                        ((TxPacketCountListener) listener).onFailure(message.arg1);
1335                    }
1336                    break;
1337                default:
1338                    //ignore
1339                    break;
1340            }
1341        }
1342    }
1343
1344    private int putListener(Object listener) {
1345        if (listener == null) return INVALID_KEY;
1346        int key;
1347        synchronized (mListenerMapLock) {
1348            do {
1349                key = mListenerKey++;
1350            } while (key == INVALID_KEY);
1351            mListenerMap.put(key, listener);
1352        }
1353        return key;
1354    }
1355
1356    private Object removeListener(int key) {
1357        if (key == INVALID_KEY) return null;
1358        synchronized (mListenerMapLock) {
1359            Object listener = mListenerMap.get(key);
1360            mListenerMap.remove(key);
1361            return listener;
1362        }
1363    }
1364
1365    private void init() {
1366        mWifiServiceMessenger = getWifiServiceMessenger();
1367        if (mWifiServiceMessenger == null) {
1368            mAsyncChannel = null;
1369            return;
1370        }
1371
1372        synchronized (sThreadRefLock) {
1373            if (++sThreadRefCount == 1) {
1374                sHandlerThread = new HandlerThread("WifiManager");
1375                sHandlerThread.start();
1376            }
1377        }
1378
1379        mHandler = new ServiceHandler(sHandlerThread.getLooper());
1380        mAsyncChannel.connect(mContext, mHandler, mWifiServiceMessenger);
1381        try {
1382            mConnected.await();
1383        } catch (InterruptedException e) {
1384            Log.e(TAG, "interrupted wait at init");
1385        }
1386    }
1387
1388    private void validateChannel() {
1389        if (mAsyncChannel == null) throw new IllegalStateException(
1390                "No permission to access and change wifi or a bad initialization");
1391    }
1392
1393    /**
1394     * Connect to a network with the given configuration. The network also
1395     * gets added to the supplicant configuration.
1396     *
1397     * For a new network, this function is used instead of a
1398     * sequence of addNetwork(), enableNetwork(), saveConfiguration() and
1399     * reconnect()
1400     *
1401     * @param config the set of variables that describe the configuration,
1402     *            contained in a {@link WifiConfiguration} object.
1403     * @param listener for callbacks on success or failure. Can be null.
1404     * @throws IllegalStateException if the WifiManager instance needs to be
1405     * initialized again
1406     *
1407     * @hide
1408     */
1409    public void connect(WifiConfiguration config, ActionListener listener) {
1410        if (config == null) throw new IllegalArgumentException("config cannot be null");
1411        validateChannel();
1412        // Use INVALID_NETWORK_ID for arg1 when passing a config object
1413        // arg1 is used to pass network id when the network already exists
1414        mAsyncChannel.sendMessage(CONNECT_NETWORK, WifiConfiguration.INVALID_NETWORK_ID,
1415                putListener(listener), config);
1416    }
1417
1418    /**
1419     * Connect to a network with the given networkId.
1420     *
1421     * This function is used instead of a enableNetwork(), saveConfiguration() and
1422     * reconnect()
1423     *
1424     * @param networkId the network id identifiying the network in the
1425     *                supplicant configuration list
1426     * @param listener for callbacks on success or failure. Can be null.
1427     * @throws IllegalStateException if the WifiManager instance needs to be
1428     * initialized again
1429     * @hide
1430     */
1431    public void connect(int networkId, ActionListener listener) {
1432        if (networkId < 0) throw new IllegalArgumentException("Network id cannot be negative");
1433        validateChannel();
1434        mAsyncChannel.sendMessage(CONNECT_NETWORK, networkId, putListener(listener));
1435    }
1436
1437    /**
1438     * Save the given network in the supplicant config. If the network already
1439     * exists, the configuration is updated. A new network is enabled
1440     * by default.
1441     *
1442     * For a new network, this function is used instead of a
1443     * sequence of addNetwork(), enableNetwork() and saveConfiguration().
1444     *
1445     * For an existing network, it accomplishes the task of updateNetwork()
1446     * and saveConfiguration()
1447     *
1448     * @param config the set of variables that describe the configuration,
1449     *            contained in a {@link WifiConfiguration} object.
1450     * @param listener for callbacks on success or failure. Can be null.
1451     * @throws IllegalStateException if the WifiManager instance needs to be
1452     * initialized again
1453     * @hide
1454     */
1455    public void save(WifiConfiguration config, ActionListener listener) {
1456        if (config == null) throw new IllegalArgumentException("config cannot be null");
1457        validateChannel();
1458        mAsyncChannel.sendMessage(SAVE_NETWORK, 0, putListener(listener), config);
1459    }
1460
1461    /**
1462     * Delete the network in the supplicant config.
1463     *
1464     * This function is used instead of a sequence of removeNetwork()
1465     * and saveConfiguration().
1466     *
1467     * @param config the set of variables that describe the configuration,
1468     *            contained in a {@link WifiConfiguration} object.
1469     * @param listener for callbacks on success or failure. Can be null.
1470     * @throws IllegalStateException if the WifiManager instance needs to be
1471     * initialized again
1472     * @hide
1473     */
1474    public void forget(int netId, ActionListener listener) {
1475        if (netId < 0) throw new IllegalArgumentException("Network id cannot be negative");
1476        validateChannel();
1477        mAsyncChannel.sendMessage(FORGET_NETWORK, netId, putListener(listener));
1478    }
1479
1480    /**
1481     * Disable network
1482     *
1483     * @param netId is the network Id
1484     * @param listener for callbacks on success or failure. Can be null.
1485     * @throws IllegalStateException if the WifiManager instance needs to be
1486     * initialized again
1487     * @hide
1488     */
1489    public void disable(int netId, ActionListener listener) {
1490        if (netId < 0) throw new IllegalArgumentException("Network id cannot be negative");
1491        validateChannel();
1492        mAsyncChannel.sendMessage(DISABLE_NETWORK, netId, putListener(listener));
1493    }
1494
1495    /**
1496     * Start Wi-fi Protected Setup
1497     *
1498     * @param config WPS configuration
1499     * @param listener for callbacks on success or failure. Can be null.
1500     * @throws IllegalStateException if the WifiManager instance needs to be
1501     * initialized again
1502     * @hide
1503     */
1504    public void startWps(WpsInfo config, WpsListener listener) {
1505        if (config == null) throw new IllegalArgumentException("config cannot be null");
1506        validateChannel();
1507        mAsyncChannel.sendMessage(START_WPS, 0, putListener(listener), config);
1508    }
1509
1510    /**
1511     * Cancel any ongoing Wi-fi Protected Setup
1512     *
1513     * @param listener for callbacks on success or failure. Can be null.
1514     * @throws IllegalStateException if the WifiManager instance needs to be
1515     * initialized again
1516     * @hide
1517     */
1518    public void cancelWps(ActionListener listener) {
1519        validateChannel();
1520        mAsyncChannel.sendMessage(CANCEL_WPS, 0, putListener(listener));
1521    }
1522
1523    /**
1524     * Get a reference to WifiService handler. This is used by a client to establish
1525     * an AsyncChannel communication with WifiService
1526     *
1527     * @return Messenger pointing to the WifiService handler
1528     * @hide
1529     */
1530    public Messenger getWifiServiceMessenger() {
1531        try {
1532            return mService.getWifiServiceMessenger();
1533        } catch (RemoteException e) {
1534            return null;
1535        } catch (SecurityException e) {
1536            return null;
1537        }
1538    }
1539
1540    /**
1541     * Get a reference to WifiStateMachine handler.
1542     * @return Messenger pointing to the WifiService handler
1543     * @hide
1544     */
1545    public Messenger getWifiStateMachineMessenger() {
1546        try {
1547            return mService.getWifiStateMachineMessenger();
1548        } catch (RemoteException e) {
1549            return null;
1550        }
1551    }
1552
1553    /**
1554     * Returns the file in which IP and proxy configuration data is stored
1555     * @hide
1556     */
1557    public String getConfigFile() {
1558        try {
1559            return mService.getConfigFile();
1560        } catch (RemoteException e) {
1561            return null;
1562        }
1563    }
1564
1565    /**
1566     * Allows an application to keep the Wi-Fi radio awake.
1567     * Normally the Wi-Fi radio may turn off when the user has not used the device in a while.
1568     * Acquiring a WifiLock will keep the radio on until the lock is released.  Multiple
1569     * applications may hold WifiLocks, and the radio will only be allowed to turn off when no
1570     * WifiLocks are held in any application.
1571     * <p>
1572     * Before using a WifiLock, consider carefully if your application requires Wi-Fi access, or
1573     * could function over a mobile network, if available.  A program that needs to download large
1574     * files should hold a WifiLock to ensure that the download will complete, but a program whose
1575     * network usage is occasional or low-bandwidth should not hold a WifiLock to avoid adversely
1576     * affecting battery life.
1577     * <p>
1578     * Note that WifiLocks cannot override the user-level "Wi-Fi Enabled" setting, nor Airplane
1579     * Mode.  They simply keep the radio from turning off when Wi-Fi is already on but the device
1580     * is idle.
1581     * <p>
1582     * Any application using a WifiLock must request the {@code android.permission.WAKE_LOCK}
1583     * permission in an {@code &lt;uses-permission&gt;} element of the application's manifest.
1584     */
1585    public class WifiLock {
1586        private String mTag;
1587        private final IBinder mBinder;
1588        private int mRefCount;
1589        int mLockType;
1590        private boolean mRefCounted;
1591        private boolean mHeld;
1592        private WorkSource mWorkSource;
1593
1594        private WifiLock(int lockType, String tag) {
1595            mTag = tag;
1596            mLockType = lockType;
1597            mBinder = new Binder();
1598            mRefCount = 0;
1599            mRefCounted = true;
1600            mHeld = false;
1601        }
1602
1603        /**
1604         * Locks the Wi-Fi radio on until {@link #release} is called.
1605         *
1606         * If this WifiLock is reference-counted, each call to {@code acquire} will increment the
1607         * reference count, and the radio will remain locked as long as the reference count is
1608         * above zero.
1609         *
1610         * If this WifiLock is not reference-counted, the first call to {@code acquire} will lock
1611         * the radio, but subsequent calls will be ignored.  Only one call to {@link #release}
1612         * will be required, regardless of the number of times that {@code acquire} is called.
1613         */
1614        public void acquire() {
1615            synchronized (mBinder) {
1616                if (mRefCounted ? (++mRefCount == 1) : (!mHeld)) {
1617                    try {
1618                        mService.acquireWifiLock(mBinder, mLockType, mTag, mWorkSource);
1619                        synchronized (WifiManager.this) {
1620                            if (mActiveLockCount >= MAX_ACTIVE_LOCKS) {
1621                                mService.releaseWifiLock(mBinder);
1622                                throw new UnsupportedOperationException(
1623                                            "Exceeded maximum number of wifi locks");
1624                            }
1625                            mActiveLockCount++;
1626                        }
1627                    } catch (RemoteException ignore) {
1628                    }
1629                    mHeld = true;
1630                }
1631            }
1632        }
1633
1634        /**
1635         * Unlocks the Wi-Fi radio, allowing it to turn off when the device is idle.
1636         *
1637         * If this WifiLock is reference-counted, each call to {@code release} will decrement the
1638         * reference count, and the radio will be unlocked only when the reference count reaches
1639         * zero.  If the reference count goes below zero (that is, if {@code release} is called
1640         * a greater number of times than {@link #acquire}), an exception is thrown.
1641         *
1642         * If this WifiLock is not reference-counted, the first call to {@code release} (after
1643         * the radio was locked using {@link #acquire}) will unlock the radio, and subsequent
1644         * calls will be ignored.
1645         */
1646        public void release() {
1647            synchronized (mBinder) {
1648                if (mRefCounted ? (--mRefCount == 0) : (mHeld)) {
1649                    try {
1650                        mService.releaseWifiLock(mBinder);
1651                        synchronized (WifiManager.this) {
1652                            mActiveLockCount--;
1653                        }
1654                    } catch (RemoteException ignore) {
1655                    }
1656                    mHeld = false;
1657                }
1658                if (mRefCount < 0) {
1659                    throw new RuntimeException("WifiLock under-locked " + mTag);
1660                }
1661            }
1662        }
1663
1664        /**
1665         * Controls whether this is a reference-counted or non-reference-counted WifiLock.
1666         *
1667         * Reference-counted WifiLocks keep track of the number of calls to {@link #acquire} and
1668         * {@link #release}, and only allow the radio to sleep when every call to {@link #acquire}
1669         * has been balanced with a call to {@link #release}.  Non-reference-counted WifiLocks
1670         * lock the radio whenever {@link #acquire} is called and it is unlocked, and unlock the
1671         * radio whenever {@link #release} is called and it is locked.
1672         *
1673         * @param refCounted true if this WifiLock should keep a reference count
1674         */
1675        public void setReferenceCounted(boolean refCounted) {
1676            mRefCounted = refCounted;
1677        }
1678
1679        /**
1680         * Checks whether this WifiLock is currently held.
1681         *
1682         * @return true if this WifiLock is held, false otherwise
1683         */
1684        public boolean isHeld() {
1685            synchronized (mBinder) {
1686                return mHeld;
1687            }
1688        }
1689
1690        public void setWorkSource(WorkSource ws) {
1691            synchronized (mBinder) {
1692                if (ws != null && ws.size() == 0) {
1693                    ws = null;
1694                }
1695                boolean changed = true;
1696                if (ws == null) {
1697                    mWorkSource = null;
1698                } else if (mWorkSource == null) {
1699                    changed = mWorkSource != null;
1700                    mWorkSource = new WorkSource(ws);
1701                } else {
1702                    changed = mWorkSource.diff(ws);
1703                    if (changed) {
1704                        mWorkSource.set(ws);
1705                    }
1706                }
1707                if (changed && mHeld) {
1708                    try {
1709                        mService.updateWifiLockWorkSource(mBinder, mWorkSource);
1710                    } catch (RemoteException e) {
1711                    }
1712                }
1713            }
1714        }
1715
1716        public String toString() {
1717            String s1, s2, s3;
1718            synchronized (mBinder) {
1719                s1 = Integer.toHexString(System.identityHashCode(this));
1720                s2 = mHeld ? "held; " : "";
1721                if (mRefCounted) {
1722                    s3 = "refcounted: refcount = " + mRefCount;
1723                } else {
1724                    s3 = "not refcounted";
1725                }
1726                return "WifiLock{ " + s1 + "; " + s2 + s3 + " }";
1727            }
1728        }
1729
1730        @Override
1731        protected void finalize() throws Throwable {
1732            super.finalize();
1733            synchronized (mBinder) {
1734                if (mHeld) {
1735                    try {
1736                        mService.releaseWifiLock(mBinder);
1737                        synchronized (WifiManager.this) {
1738                            mActiveLockCount--;
1739                        }
1740                    } catch (RemoteException ignore) {
1741                    }
1742                }
1743            }
1744        }
1745    }
1746
1747    /**
1748     * Creates a new WifiLock.
1749     *
1750     * @param lockType the type of lock to create. See {@link #WIFI_MODE_FULL},
1751     * {@link #WIFI_MODE_FULL_HIGH_PERF} and {@link #WIFI_MODE_SCAN_ONLY} for
1752     * descriptions of the types of Wi-Fi locks.
1753     * @param tag a tag for the WifiLock to identify it in debugging messages.  This string is
1754     *            never shown to the user under normal conditions, but should be descriptive
1755     *            enough to identify your application and the specific WifiLock within it, if it
1756     *            holds multiple WifiLocks.
1757     *
1758     * @return a new, unacquired WifiLock with the given tag.
1759     *
1760     * @see WifiLock
1761     */
1762    public WifiLock createWifiLock(int lockType, String tag) {
1763        return new WifiLock(lockType, tag);
1764    }
1765
1766    /**
1767     * Creates a new WifiLock.
1768     *
1769     * @param tag a tag for the WifiLock to identify it in debugging messages.  This string is
1770     *            never shown to the user under normal conditions, but should be descriptive
1771     *            enough to identify your application and the specific WifiLock within it, if it
1772     *            holds multiple WifiLocks.
1773     *
1774     * @return a new, unacquired WifiLock with the given tag.
1775     *
1776     * @see WifiLock
1777     */
1778    public WifiLock createWifiLock(String tag) {
1779        return new WifiLock(WIFI_MODE_FULL, tag);
1780    }
1781
1782
1783    /**
1784     * Create a new MulticastLock
1785     *
1786     * @param tag a tag for the MulticastLock to identify it in debugging
1787     *            messages.  This string is never shown to the user under
1788     *            normal conditions, but should be descriptive enough to
1789     *            identify your application and the specific MulticastLock
1790     *            within it, if it holds multiple MulticastLocks.
1791     *
1792     * @return a new, unacquired MulticastLock with the given tag.
1793     *
1794     * @see MulticastLock
1795     */
1796    public MulticastLock createMulticastLock(String tag) {
1797        return new MulticastLock(tag);
1798    }
1799
1800    /**
1801     * Allows an application to receive Wifi Multicast packets.
1802     * Normally the Wifi stack filters out packets not explicitly
1803     * addressed to this device.  Acquring a MulticastLock will
1804     * cause the stack to receive packets addressed to multicast
1805     * addresses.  Processing these extra packets can cause a noticable
1806     * battery drain and should be disabled when not needed.
1807     */
1808    public class MulticastLock {
1809        private String mTag;
1810        private final IBinder mBinder;
1811        private int mRefCount;
1812        private boolean mRefCounted;
1813        private boolean mHeld;
1814
1815        private MulticastLock(String tag) {
1816            mTag = tag;
1817            mBinder = new Binder();
1818            mRefCount = 0;
1819            mRefCounted = true;
1820            mHeld = false;
1821        }
1822
1823        /**
1824         * Locks Wifi Multicast on until {@link #release} is called.
1825         *
1826         * If this MulticastLock is reference-counted each call to
1827         * {@code acquire} will increment the reference count, and the
1828         * wifi interface will receive multicast packets as long as the
1829         * reference count is above zero.
1830         *
1831         * If this MulticastLock is not reference-counted, the first call to
1832         * {@code acquire} will turn on the multicast packets, but subsequent
1833         * calls will be ignored.  Only one call to {@link #release} will
1834         * be required, regardless of the number of times that {@code acquire}
1835         * is called.
1836         *
1837         * Note that other applications may also lock Wifi Multicast on.
1838         * Only they can relinquish their lock.
1839         *
1840         * Also note that applications cannot leave Multicast locked on.
1841         * When an app exits or crashes, any Multicast locks will be released.
1842         */
1843        public void acquire() {
1844            synchronized (mBinder) {
1845                if (mRefCounted ? (++mRefCount == 1) : (!mHeld)) {
1846                    try {
1847                        mService.acquireMulticastLock(mBinder, mTag);
1848                        synchronized (WifiManager.this) {
1849                            if (mActiveLockCount >= MAX_ACTIVE_LOCKS) {
1850                                mService.releaseMulticastLock();
1851                                throw new UnsupportedOperationException(
1852                                        "Exceeded maximum number of wifi locks");
1853                            }
1854                            mActiveLockCount++;
1855                        }
1856                    } catch (RemoteException ignore) {
1857                    }
1858                    mHeld = true;
1859                }
1860            }
1861        }
1862
1863        /**
1864         * Unlocks Wifi Multicast, restoring the filter of packets
1865         * not addressed specifically to this device and saving power.
1866         *
1867         * If this MulticastLock is reference-counted, each call to
1868         * {@code release} will decrement the reference count, and the
1869         * multicast packets will only stop being received when the reference
1870         * count reaches zero.  If the reference count goes below zero (that
1871         * is, if {@code release} is called a greater number of times than
1872         * {@link #acquire}), an exception is thrown.
1873         *
1874         * If this MulticastLock is not reference-counted, the first call to
1875         * {@code release} (after the radio was multicast locked using
1876         * {@link #acquire}) will unlock the multicast, and subsequent calls
1877         * will be ignored.
1878         *
1879         * Note that if any other Wifi Multicast Locks are still outstanding
1880         * this {@code release} call will not have an immediate effect.  Only
1881         * when all applications have released all their Multicast Locks will
1882         * the Multicast filter be turned back on.
1883         *
1884         * Also note that when an app exits or crashes all of its Multicast
1885         * Locks will be automatically released.
1886         */
1887        public void release() {
1888            synchronized (mBinder) {
1889                if (mRefCounted ? (--mRefCount == 0) : (mHeld)) {
1890                    try {
1891                        mService.releaseMulticastLock();
1892                        synchronized (WifiManager.this) {
1893                            mActiveLockCount--;
1894                        }
1895                    } catch (RemoteException ignore) {
1896                    }
1897                    mHeld = false;
1898                }
1899                if (mRefCount < 0) {
1900                    throw new RuntimeException("MulticastLock under-locked "
1901                            + mTag);
1902                }
1903            }
1904        }
1905
1906        /**
1907         * Controls whether this is a reference-counted or non-reference-
1908         * counted MulticastLock.
1909         *
1910         * Reference-counted MulticastLocks keep track of the number of calls
1911         * to {@link #acquire} and {@link #release}, and only stop the
1912         * reception of multicast packets when every call to {@link #acquire}
1913         * has been balanced with a call to {@link #release}.  Non-reference-
1914         * counted MulticastLocks allow the reception of multicast packets
1915         * whenever {@link #acquire} is called and stop accepting multicast
1916         * packets whenever {@link #release} is called.
1917         *
1918         * @param refCounted true if this MulticastLock should keep a reference
1919         * count
1920         */
1921        public void setReferenceCounted(boolean refCounted) {
1922            mRefCounted = refCounted;
1923        }
1924
1925        /**
1926         * Checks whether this MulticastLock is currently held.
1927         *
1928         * @return true if this MulticastLock is held, false otherwise
1929         */
1930        public boolean isHeld() {
1931            synchronized (mBinder) {
1932                return mHeld;
1933            }
1934        }
1935
1936        public String toString() {
1937            String s1, s2, s3;
1938            synchronized (mBinder) {
1939                s1 = Integer.toHexString(System.identityHashCode(this));
1940                s2 = mHeld ? "held; " : "";
1941                if (mRefCounted) {
1942                    s3 = "refcounted: refcount = " + mRefCount;
1943                } else {
1944                    s3 = "not refcounted";
1945                }
1946                return "MulticastLock{ " + s1 + "; " + s2 + s3 + " }";
1947            }
1948        }
1949
1950        @Override
1951        protected void finalize() throws Throwable {
1952            super.finalize();
1953            setReferenceCounted(false);
1954            release();
1955        }
1956    }
1957
1958    /**
1959     * Check multicast filter status.
1960     *
1961     * @return true if multicast packets are allowed.
1962     *
1963     * @hide pending API council approval
1964     */
1965    public boolean isMulticastEnabled() {
1966        try {
1967            return mService.isMulticastEnabled();
1968        } catch (RemoteException e) {
1969            return false;
1970        }
1971    }
1972
1973    /**
1974     * Initialize the multicast filtering to 'on'
1975     * @hide no intent to publish
1976     */
1977    public boolean initializeMulticastFiltering() {
1978        try {
1979            mService.initializeMulticastFiltering();
1980            return true;
1981        } catch (RemoteException e) {
1982             return false;
1983        }
1984    }
1985
1986    /** @hide */
1987    public void captivePortalCheckComplete() {
1988        try {
1989            mService.captivePortalCheckComplete();
1990        } catch (RemoteException e) {}
1991    }
1992
1993    protected void finalize() throws Throwable {
1994        try {
1995            synchronized (sThreadRefLock) {
1996                if (--sThreadRefCount == 0 && sHandlerThread != null) {
1997                    sHandlerThread.getLooper().quit();
1998                }
1999            }
2000        } finally {
2001            super.finalize();
2002        }
2003    }
2004}
2005