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