WifiManager.java revision 5347bd4cda2b6afc18f8acab48e52131f35ed13c
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.net.DhcpInfo;
22import android.os.Binder;
23import android.os.IBinder;
24import android.os.Handler;
25import android.os.RemoteException;
26
27import java.util.List;
28
29/**
30 * This class provides the primary API for managing all aspects of Wi-Fi
31 * connectivity. Get an instance of this class by calling
32 * {@link android.content.Context#getSystemService(String) Context.getSystemService(Context.WIFI_SERVICE)}.
33
34 * It deals with several categories of items:
35 * <ul>
36 * <li>The list of configured networks. The list can be viewed and updated,
37 * and attributes of individual entries can be modified.</li>
38 * <li>The currently active Wi-Fi network, if any. Connectivity can be
39 * established or torn down, and dynamic information about the state of
40 * the network can be queried.</li>
41 * <li>Results of access point scans, containing enough information to
42 * make decisions about what access point to connect to.</li>
43 * <li>It defines the names of various Intent actions that are broadcast
44 * upon any sort of change in Wi-Fi state.
45 * </ul>
46 * This is the API to use when performing Wi-Fi specific operations. To
47 * perform operations that pertain to network connectivity at an abstract
48 * level, use {@link android.net.ConnectivityManager}.
49 */
50public class WifiManager {
51
52    // Supplicant error codes:
53    /**
54     * The error code if there was a problem authenticating.
55     */
56    public static final int ERROR_AUTHENTICATING = 1;
57
58    /**
59     * Broadcast intent action indicating that Wi-Fi has been enabled, disabled,
60     * enabling, disabling, or unknown. One extra provides this state as an int.
61     * Another extra provides the previous state, if available.
62     *
63     * @see #EXTRA_WIFI_STATE
64     * @see #EXTRA_PREVIOUS_WIFI_STATE
65     */
66    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
67    public static final String WIFI_STATE_CHANGED_ACTION =
68        "android.net.wifi.WIFI_STATE_CHANGED";
69    /**
70     * The lookup key for an int that indicates whether Wi-Fi is enabled,
71     * disabled, enabling, disabling, or unknown.  Retrieve it with
72     * {@link android.content.Intent#getIntExtra(String,int)}.
73     *
74     * @see #WIFI_STATE_DISABLED
75     * @see #WIFI_STATE_DISABLING
76     * @see #WIFI_STATE_ENABLED
77     * @see #WIFI_STATE_ENABLING
78     * @see #WIFI_STATE_UNKNOWN
79     */
80    public static final String EXTRA_WIFI_STATE = "wifi_state";
81    /**
82     * The previous Wi-Fi state.
83     *
84     * @see #EXTRA_WIFI_STATE
85     */
86    public static final String EXTRA_PREVIOUS_WIFI_STATE = "previous_wifi_state";
87
88    /**
89     * Wi-Fi is currently being disabled. The state will change to {@link #WIFI_STATE_DISABLED} if
90     * it finishes successfully.
91     *
92     * @see #WIFI_STATE_CHANGED_ACTION
93     * @see #getWifiState()
94     */
95    public static final int WIFI_STATE_DISABLING = 0;
96    /**
97     * Wi-Fi is disabled.
98     *
99     * @see #WIFI_STATE_CHANGED_ACTION
100     * @see #getWifiState()
101     */
102    public static final int WIFI_STATE_DISABLED = 1;
103    /**
104     * Wi-Fi is currently being enabled. The state will change to {@link #WIFI_STATE_ENABLED} if
105     * it finishes successfully.
106     *
107     * @see #WIFI_STATE_CHANGED_ACTION
108     * @see #getWifiState()
109     */
110    public static final int WIFI_STATE_ENABLING = 2;
111    /**
112     * Wi-Fi is enabled.
113     *
114     * @see #WIFI_STATE_CHANGED_ACTION
115     * @see #getWifiState()
116     */
117    public static final int WIFI_STATE_ENABLED = 3;
118    /**
119     * Wi-Fi is in an unknown state. This state will occur when an error happens while enabling
120     * or disabling.
121     *
122     * @see #WIFI_STATE_CHANGED_ACTION
123     * @see #getWifiState()
124     */
125    public static final int WIFI_STATE_UNKNOWN = 4;
126
127    /**
128     * Broadcast intent action indicating that a connection to the supplicant has
129     * been established (and it is now possible
130     * to perform Wi-Fi operations) or the connection to the supplicant has been
131     * lost. One extra provides the connection state as a boolean, where {@code true}
132     * means CONNECTED.
133     * @see #EXTRA_SUPPLICANT_CONNECTED
134     */
135    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
136    public static final String SUPPLICANT_CONNECTION_CHANGE_ACTION =
137        "android.net.wifi.supplicant.CONNECTION_CHANGE";
138    /**
139     * The lookup key for a boolean that indicates whether a connection to
140     * the supplicant daemon has been gained or lost. {@code true} means
141     * a connection now exists.
142     * Retrieve it with {@link android.content.Intent#getBooleanExtra(String,boolean)}.
143     */
144    public static final String EXTRA_SUPPLICANT_CONNECTED = "connected";
145    /**
146     * Broadcast intent action indicating that the state of Wi-Fi connectivity
147     * has changed. One extra provides the new state
148     * in the form of a {@link android.net.NetworkInfo} object. If the new state is
149     * CONNECTED, a second extra may provide the BSSID of the access point,
150     * as a {@code String}.
151     * @see #EXTRA_NETWORK_INFO
152     * @see #EXTRA_BSSID
153     */
154    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
155    public static final String NETWORK_STATE_CHANGED_ACTION = "android.net.wifi.STATE_CHANGE";
156    /**
157     * The lookup key for a {@link android.net.NetworkInfo} object associated with the
158     * Wi-Fi network. Retrieve with
159     * {@link android.content.Intent#getParcelableExtra(String)}.
160     */
161    public static final String EXTRA_NETWORK_INFO = "networkInfo";
162    /**
163     * The lookup key for a String giving the BSSID of the access point to which
164     * we are connected. Only present when the new state is CONNECTED.
165     * Retrieve with
166     * {@link android.content.Intent#getStringExtra(String)}.
167     */
168    public static final String EXTRA_BSSID = "bssid";
169    /**
170     * Broadcast intent action indicating that the state of establishing a connection to
171     * an access point has changed.One extra provides the new
172     * {@link SupplicantState}. Note that the supplicant state is Wi-Fi specific, and
173     * is not generally the most useful thing to look at if you are just interested in
174     * the overall state of connectivity.
175     * @see #EXTRA_NEW_STATE
176     * @see #EXTRA_SUPPLICANT_ERROR
177     */
178    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
179    public static final String SUPPLICANT_STATE_CHANGED_ACTION =
180        "android.net.wifi.supplicant.STATE_CHANGE";
181    /**
182     * The lookup key for a {@link SupplicantState} describing the new state
183     * Retrieve with
184     * {@link android.content.Intent#getParcelableExtra(String)}.
185     */
186    public static final String EXTRA_NEW_STATE = "newState";
187
188    /**
189     * The lookup key for a {@link SupplicantState} describing the supplicant
190     * error code if any
191     * Retrieve with
192     * {@link android.content.Intent#getIntExtra(String, int)}.
193     * @see #ERROR_AUTHENTICATING
194     */
195    public static final String EXTRA_SUPPLICANT_ERROR = "supplicantError";
196
197    /**
198     * An access point scan has completed, and results are available from the supplicant.
199     * Call {@link #getScanResults()} to obtain the results.
200     */
201    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
202    public static final String SCAN_RESULTS_AVAILABLE_ACTION = "android.net.wifi.SCAN_RESULTS";
203    /**
204     * The RSSI (signal strength) has changed.
205     * @see #EXTRA_NEW_RSSI
206     */
207    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
208    public static final String RSSI_CHANGED_ACTION = "android.net.wifi.RSSI_CHANGED";
209    /**
210     * The lookup key for an {@code int} giving the new RSSI in dBm.
211     */
212    public static final String EXTRA_NEW_RSSI = "newRssi";
213
214    /**
215     * The network IDs of the configured networks could have changed.
216     */
217    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
218    public static final String NETWORK_IDS_CHANGED_ACTION = "android.net.wifi.NETWORK_IDS_CHANGED";
219
220    /**
221     * Activity Action: Pick a Wi-Fi network to connect to.
222     * <p>Input: Nothing.
223     * <p>Output: Nothing.
224     */
225    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
226    public static final String ACTION_PICK_WIFI_NETWORK = "android.net.wifi.PICK_WIFI_NETWORK";
227
228    /**
229     * In this Wi-Fi lock mode, Wi-Fi will be kept active,
230     * and will behave normally, i.e., it will attempt to automatically
231     * establish a connection to a remembered access point that is
232     * within range, and will do periodic scans if there are remembered
233     * access points but none are in range.
234     */
235    public static final int WIFI_MODE_FULL = 1;
236    /**
237     * In this Wi-Fi lock mode, Wi-Fi will be kept active,
238     * but the only operation that will be supported is initiation of
239     * scans, and the subsequent reporting of scan results. No attempts
240     * will be made to automatically connect to remembered access points,
241     * nor will periodic scans be automatically performed looking for
242     * remembered access points. Scans must be explicitly requested by
243     * an application in this mode.
244     */
245    public static final int WIFI_MODE_SCAN_ONLY = 2;
246
247    /** Anything worse than or equal to this will show 0 bars. */
248    private static final int MIN_RSSI = -100;
249
250    /** Anything better than or equal to this will show the max bars. */
251    private static final int MAX_RSSI = -55;
252
253    IWifiManager mService;
254    Handler mHandler;
255
256    /**
257     * Create a new WifiManager instance.
258     * Applications will almost always want to use
259     * {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve
260     * the standard {@link android.content.Context#WIFI_SERVICE Context.WIFI_SERVICE}.
261     * @param service the Binder interface
262     * @param handler target for messages
263     * @hide - hide this because it takes in a parameter of type IWifiManager, which
264     * is a system private class.
265     */
266    public WifiManager(IWifiManager service, Handler handler) {
267        mService = service;
268        mHandler = handler;
269    }
270
271    /**
272     * Return a list of all the networks configured in the supplicant.
273     * Not all fields of WifiConfiguration are returned. Only the following
274     * fields are filled in:
275     * <ul>
276     * <li>networkId</li>
277     * <li>SSID</li>
278     * <li>BSSID</li>
279     * <li>priority</li>
280     * <li>allowedProtocols</li>
281     * <li>allowedKeyManagement</li>
282     * <li>allowedAuthAlgorithms</li>
283     * <li>allowedPairwiseCiphers</li>
284     * <li>allowedGroupCiphers</li>
285     * </ul>
286     * @return a list of network configurations in the form of a list
287     * of {@link WifiConfiguration} objects.
288     */
289    public List<WifiConfiguration> getConfiguredNetworks() {
290        try {
291            return mService.getConfiguredNetworks();
292        } catch (RemoteException e) {
293            return null;
294        }
295    }
296
297    /**
298     * Add a new network description to the set of configured networks.
299     * The {@code networkId} field of the supplied configuration object
300     * is ignored.
301     * <p/>
302     * The new network will be marked DISABLED by default. To enable it,
303     * called {@link #enableNetwork}.
304     *
305     * @param config the set of variables that describe the configuration,
306     *            contained in a {@link WifiConfiguration} object.
307     * @return the ID of the newly created network description. This is used in
308     *         other operations to specified the network to be acted upon.
309     *         Returns {@code -1} on failure.
310     */
311    public int addNetwork(WifiConfiguration config) {
312        if (config == null) {
313            return -1;
314        }
315        config.networkId = -1;
316        return addOrUpdateNetwork(config);
317    }
318
319    /**
320     * Update the network description of an existing configured network.
321     *
322     * @param config the set of variables that describe the configuration,
323     *            contained in a {@link WifiConfiguration} object. It may
324     *            be sparse, so that only the items that are being changed
325     *            are non-<code>null</code>. The {@code networkId} field
326     *            must be set to the ID of the existing network being updated.
327     * @return Returns the {@code networkId} of the supplied
328     *         {@code WifiConfiguration} on success.
329     *         <br/>
330     *         Returns {@code -1} on failure, including when the {@code networkId}
331     *         field of the {@code WifiConfiguration} does not refer to an
332     *         existing network.
333     */
334    public int updateNetwork(WifiConfiguration config) {
335        if (config == null || config.networkId < 0) {
336            return -1;
337        }
338        return addOrUpdateNetwork(config);
339    }
340
341    /**
342     * Internal method for doing the RPC that creates a new network description
343     * or updates an existing one.
344     *
345     * @param config The possibly sparse object containing the variables that
346     *         are to set or updated in the network description.
347     * @return the ID of the network on success, {@code -1} on failure.
348     */
349    private int addOrUpdateNetwork(WifiConfiguration config) {
350        try {
351            return mService.addOrUpdateNetwork(config);
352        } catch (RemoteException e) {
353            return -1;
354        }
355    }
356
357    /**
358     * Remove the specified network from the list of configured networks.
359     * This may result in the asynchronous delivery of state change
360     * events.
361     * @param netId the integer that identifies the network configuration
362     * to the supplicant
363     * @return {@code true} if the operation succeeded
364     */
365    public boolean removeNetwork(int netId) {
366        try {
367            return mService.removeNetwork(netId);
368        } catch (RemoteException e) {
369            return false;
370        }
371    }
372
373    /**
374     * Allow a previously configured network to be associated with. If
375     * <code>disableOthers</code> is true, then all other configured
376     * networks are disabled, and an attempt to connect to the selected
377     * network is initiated. This may result in the asynchronous delivery
378     * of state change events.
379     * @param netId the ID of the network in the list of configured networks
380     * @param disableOthers if true, disable all other networks. The way to
381     * select a particular network to connect to is specify {@code true}
382     * for this parameter.
383     * @return {@code true} if the operation succeeded
384     */
385    public boolean enableNetwork(int netId, boolean disableOthers) {
386        try {
387            return mService.enableNetwork(netId, disableOthers);
388        } catch (RemoteException e) {
389            return false;
390        }
391    }
392
393    /**
394     * Disable a configured network. The specified network will not be
395     * a candidate for associating. This may result in the asynchronous
396     * delivery of state change events.
397     * @param netId the ID of the network as returned by {@link #addNetwork}.
398     * @return {@code true} if the operation succeeded
399     */
400    public boolean disableNetwork(int netId) {
401        try {
402            return mService.disableNetwork(netId);
403        } catch (RemoteException e) {
404            return false;
405        }
406    }
407
408    /**
409     * Disassociate from the currently active access point. This may result
410     * in the asynchronous delivery of state change events.
411     * @return {@code true} if the operation succeeded
412     */
413    public boolean disconnect() {
414        try {
415            return mService.disconnect();
416        } catch (RemoteException e) {
417            return false;
418        }
419    }
420
421    /**
422     * Reconnect to the currently active access point, if we are currently
423     * disconnected. This may result in the asynchronous delivery of state
424     * change events.
425     * @return {@code true} if the operation succeeded
426     */
427    public boolean reconnect() {
428        try {
429            return mService.reconnect();
430        } catch (RemoteException e) {
431            return false;
432        }
433    }
434
435    /**
436     * Reconnect to the currently active access point, even if we are already
437     * connected. This may result in the asynchronous delivery of state
438     * change events.
439     * @return {@code true} if the operation succeeded
440     */
441    public boolean reassociate() {
442        try {
443            return mService.reassociate();
444        } catch (RemoteException e) {
445            return false;
446        }
447    }
448
449    /**
450     * Check that the supplicant daemon is responding to requests.
451     * @return {@code true} if we were able to communicate with the supplicant and
452     * it returned the expected response to the PING message.
453     */
454    public boolean pingSupplicant() {
455        if (mService == null)
456            return false;
457        try {
458            return mService.pingSupplicant();
459        } catch (RemoteException e) {
460            return false;
461        }
462    }
463
464    /**
465     * Request a scan for access points. Returns immediately. The availability
466     * of the results is made known later by means of an asynchronous event sent
467     * on completion of the scan.
468     * @return {@code true} if the operation succeeded, i.e., the scan was initiated
469     */
470    public boolean  startScan() {
471        try {
472            return mService.startScan();
473        } catch (RemoteException e) {
474            return false;
475        }
476    }
477
478    /**
479     * Return dynamic information about the current Wi-Fi connection, if any is active.
480     * @return the Wi-Fi information, contained in {@link WifiInfo}.
481     */
482    public WifiInfo getConnectionInfo() {
483        try {
484            return mService.getConnectionInfo();
485        } catch (RemoteException e) {
486            return null;
487        }
488    }
489
490    /**
491     * Return the results of the latest access point scan.
492     * @return the list of access points found in the most recent scan.
493     */
494    public List<ScanResult> getScanResults() {
495        try {
496            return mService.getScanResults();
497        } catch (RemoteException e) {
498            return null;
499        }
500    }
501
502    /**
503     * Tell the supplicant to persist the current list of configured networks.
504     * <p>
505     * Note: It is possible for this method to change the network IDs of
506     * existing networks. You should assume the network IDs can be different
507     * after calling this method.
508     *
509     * @return {@code true} if the operation succeeded
510     */
511    public boolean saveConfiguration() {
512        try {
513            return mService.saveConfiguration();
514        } catch (RemoteException e) {
515            return false;
516        }
517    }
518
519    /**
520     * Return the number of frequency channels that are allowed
521     * to be used in the current regulatory domain.
522     * @return the number of allowed channels, or {@code -1} if an error occurs
523     *
524     * @hide pending API council
525     */
526    public int getNumAllowedChannels() {
527        try {
528            return mService.getNumAllowedChannels();
529        } catch (RemoteException e) {
530            return -1;
531        }
532    }
533
534    /**
535     * Set the number of frequency channels that are allowed to be used
536     * in the current regulatory domain. This method should be used only
537     * if the correct number of channels cannot be determined automatically
538     * for some reason.
539     * @param numChannels the number of allowed channels. Must be greater than 0
540     * and less than or equal to 16.
541     * @return {@code true} if the operation succeeds, {@code false} otherwise, e.g.,
542     * {@code numChannels} is out of range.
543     *
544     * @hide pending API council
545     */
546    public boolean setNumAllowedChannels(int numChannels) {
547        try {
548            return mService.setNumAllowedChannels(numChannels);
549        } catch (RemoteException e) {
550            return false;
551        }
552    }
553
554    /**
555     * Return the list of valid values for the number of allowed radio channels
556     * for various regulatory domains.
557     * @return the list of channel counts, or {@code null} if the operation fails
558     *
559     * @hide pending API council review
560     */
561    public int[] getValidChannelCounts() {
562        try {
563            return mService.getValidChannelCounts();
564        } catch (RemoteException e) {
565            return null;
566        }
567   }
568
569    /**
570     * Return the DHCP-assigned addresses from the last successful DHCP request,
571     * if any.
572     * @return the DHCP information
573     */
574    public DhcpInfo getDhcpInfo() {
575        try {
576            return mService.getDhcpInfo();
577        } catch (RemoteException e) {
578            return null;
579        }
580    }
581
582
583    /**
584     * Enable or disable Wi-Fi.
585     * @param enabled {@code true} to enable, {@code false} to disable.
586     * @return {@code true} if the operation succeeds (or if the existing state
587     *         is the same as the requested state).
588     */
589    public boolean setWifiEnabled(boolean enabled) {
590        try {
591            return mService.setWifiEnabled(enabled);
592        } catch (RemoteException e) {
593            return false;
594        }
595    }
596
597    /**
598     * Gets the Wi-Fi enabled state.
599     * @return One of {@link #WIFI_STATE_DISABLED},
600     *         {@link #WIFI_STATE_DISABLING}, {@link #WIFI_STATE_ENABLED},
601     *         {@link #WIFI_STATE_ENABLING}, {@link #WIFI_STATE_UNKNOWN}
602     * @see #isWifiEnabled()
603     */
604    public int getWifiState() {
605        try {
606            return mService.getWifiEnabledState();
607        } catch (RemoteException e) {
608            return WIFI_STATE_UNKNOWN;
609        }
610    }
611
612    /**
613     * Return whether Wi-Fi is enabled or disabled.
614     * @return {@code true} if Wi-Fi is enabled
615     * @see #getWifiState()
616     */
617    public boolean isWifiEnabled() {
618        return getWifiState() == WIFI_STATE_ENABLED;
619    }
620
621    /**
622     * Calculates the level of the signal. This should be used any time a signal
623     * is being shown.
624     *
625     * @param rssi The power of the signal measured in RSSI.
626     * @param numLevels The number of levels to consider in the calculated
627     *            level.
628     * @return A level of the signal, given in the range of 0 to numLevels-1
629     *         (both inclusive).
630     */
631    public static int calculateSignalLevel(int rssi, int numLevels) {
632        if (rssi <= MIN_RSSI) {
633            return 0;
634        } else if (rssi >= MAX_RSSI) {
635            return numLevels - 1;
636        } else {
637            int partitionSize = (MAX_RSSI - MIN_RSSI) / (numLevels - 1);
638            return (rssi - MIN_RSSI) / partitionSize;
639        }
640    }
641
642    /**
643     * Compares two signal strengths.
644     *
645     * @param rssiA The power of the first signal measured in RSSI.
646     * @param rssiB The power of the second signal measured in RSSI.
647     * @return Returns <0 if the first signal is weaker than the second signal,
648     *         0 if the two signals have the same strength, and >0 if the first
649     *         signal is stronger than the second signal.
650     */
651    public static int compareSignalLevel(int rssiA, int rssiB) {
652        return rssiA - rssiB;
653    }
654
655    /**
656     * Allows an application to keep the Wi-Fi radio awake.
657     * Normally the Wi-Fi radio may turn off when the user has not used the device in a while.
658     * Acquiring a WifiLock will keep the radio on until the lock is released.  Multiple
659     * applications may hold WifiLocks, and the radio will only be allowed to turn off when no
660     * WifiLocks are held in any application.
661     *
662     * Before using a WifiLock, consider carefully if your application requires Wi-Fi access, or
663     * could function over a mobile network, if available.  A program that needs to download large
664     * files should hold a WifiLock to ensure that the download will complete, but a program whose
665     * network usage is occasional or low-bandwidth should not hold a WifiLock to avoid adversely
666     * affecting battery life.
667     *
668     * Note that WifiLocks cannot override the user-level "Wi-Fi Enabled" setting, nor Airplane
669     * Mode.  They simply keep the radio from turning off when Wi-Fi is already on but the device
670     * is idle.
671     */
672    public class WifiLock {
673        private String mTag;
674        private final IBinder mBinder;
675        private int mRefCount;
676        int mLockType;
677        private boolean mRefCounted;
678        private boolean mHeld;
679
680        private WifiLock(int lockType, String tag) {
681            mTag = tag;
682            mLockType = lockType;
683            mBinder = new Binder();
684            mRefCount = 0;
685            mRefCounted = true;
686            mHeld = false;
687        }
688
689        /**
690         * Locks the Wi-Fi radio on until {@link #release} is called.
691         *
692         * If this WifiLock is reference-counted, each call to {@code acquire} will increment the
693         * reference count, and the radio will remain locked as long as the reference count is
694         * above zero.
695         *
696         * If this WifiLock is not reference-counted, the first call to {@code acquire} will lock
697         * the radio, but subsequent calls will be ignored.  Only one call to {@link #release}
698         * will be required, regardless of the number of times that {@code acquire} is called.
699         */
700        public void acquire() {
701            synchronized (mBinder) {
702                if (mRefCounted ? (++mRefCount > 0) : (!mHeld)) {
703                    try {
704                        mService.acquireWifiLock(mBinder, mLockType, mTag);
705                    } catch (RemoteException ignore) {
706                    }
707                    mHeld = true;
708                }
709            }
710        }
711
712        /**
713         * Unlocks the Wi-Fi radio, allowing it to turn off when the device is idle.
714         *
715         * If this WifiLock is reference-counted, each call to {@code release} will decrement the
716         * reference count, and the radio will be unlocked only when the reference count reaches
717         * zero.  If the reference count goes below zero (that is, if {@code release} is called
718         * a greater number of times than {@link #acquire}), an exception is thrown.
719         *
720         * If this WifiLock is not reference-counted, the first call to {@code release} (after
721         * the radio was locked using {@link #acquire}) will unlock the radio, and subsequent
722         * calls will be ignored.
723         */
724        public void release() {
725            synchronized (mBinder) {
726                if (mRefCounted ? (--mRefCount == 0) : (mHeld)) {
727                    try {
728                        mService.releaseWifiLock(mBinder);
729                    } catch (RemoteException ignore) {
730                    }
731                    mHeld = false;
732                }
733                if (mRefCount < 0) {
734                    throw new RuntimeException("WifiLock under-locked " + mTag);
735                }
736            }
737        }
738
739        /**
740         * Controls whether this is a reference-counted or non-reference-counted WifiLock.
741         *
742         * Reference-counted WifiLocks keep track of the number of calls to {@link #acquire} and
743         * {@link #release}, and only allow the radio to sleep when every call to {@link #acquire}
744         * has been balanced with a call to {@link #release}.  Non-reference-counted WifiLocks
745         * lock the radio whenever {@link #acquire} is called and it is unlocked, and unlock the
746         * radio whenever {@link #release} is called and it is locked.
747         *
748         * @param refCounted true if this WifiLock should keep a reference count
749         */
750        public void setReferenceCounted(boolean refCounted) {
751            mRefCounted = refCounted;
752        }
753
754        /**
755         * Checks whether this WifiLock is currently held.
756         *
757         * @return true if this WifiLock is held, false otherwise
758         */
759        public boolean isHeld() {
760            synchronized (mBinder) {
761                return mHeld;
762            }
763        }
764
765        public String toString() {
766            String s1, s2, s3;
767            synchronized (mBinder) {
768                s1 = Integer.toHexString(System.identityHashCode(this));
769                s2 = mHeld ? "held; " : "";
770                if (mRefCounted) {
771                    s3 = "refcounted: refcount = " + mRefCount;
772                } else {
773                    s3 = "not refcounted";
774                }
775                return "WifiLock{ " + s1 + "; " + s2 + s3 + " }";
776            }
777        }
778
779        @Override
780        protected void finalize() throws Throwable {
781            super.finalize();
782            synchronized (mBinder) {
783                if (mHeld) {
784                    try {
785                        mService.releaseWifiLock(mBinder);
786                    } catch (RemoteException ignore) {
787                    }
788                }
789            }
790        }
791    }
792
793    /**
794     * Creates a new WifiLock.
795     *
796     * @param lockType the type of lock to create. See {@link #WIFI_MODE_FULL} and
797     * {@link #WIFI_MODE_SCAN_ONLY} for descriptions of the types of Wi-Fi locks.
798     * @param tag a tag for the WifiLock to identify it in debugging messages.  This string is
799     *            never shown to the user under normal conditions, but should be descriptive
800     *            enough to identify your application and the specific WifiLock within it, if it
801     *            holds multiple WifiLocks.
802     *
803     * @return a new, unacquired WifiLock with the given tag.
804     *
805     * @see WifiLock
806     */
807    public WifiLock createWifiLock(int lockType, String tag) {
808        return new WifiLock(lockType, tag);
809    }
810
811    /**
812     * Creates a new WifiLock.
813     *
814     * @param tag a tag for the WifiLock to identify it in debugging messages.  This string is
815     *            never shown to the user under normal conditions, but should be descriptive
816     *            enough to identify your application and the specific WifiLock within it, if it
817     *            holds multiple WifiLocks.
818     *
819     * @return a new, unacquired WifiLock with the given tag.
820     *
821     * @see WifiLock
822     */
823    public WifiLock createWifiLock(String tag) {
824        return new WifiLock(WIFI_MODE_FULL, tag);
825    }
826
827    /**
828     * Check multicast filter status.
829     *
830     * @return true if multicast packets are allowed.
831     *
832     * @hide pending API council approval
833     */
834    public boolean isWifiMulticastEnabled() {
835        try {
836            return mService.isWifiMulticastEnabled();
837        } catch (RemoteException e) {
838            return false;
839        }
840    }
841
842    /**
843     * Turn on the reception of multicast packets.
844     * The default behavior is to disable multicast packets as they
845     * have a noticable negative effect on battery life.  An
846     * application can turn them on, but should not leave it on for longer
847     * than needed.  When the app quits (or crashes) its request will
848     * be reverted.
849     *
850     * @param tag a string associated with this request for debugging.
851     *
852     * @return true on success
853     *
854     * @see #disableWifiMulticast
855     *
856     * @hide pending API council approval
857     */
858    public boolean enableWifiMulticast(String tag) {
859        try {
860            mService.enableWifiMulticast(new Binder(), tag);
861            return true;
862        } catch (RemoteException e) {
863            return false;
864        }
865    }
866
867    /**
868     * Return to the default multicast-off setting.
869     * Note that if others had turned on Multicast reception, your
870     * call will not turn it back off - they must also turn off their
871     * request for multicast reception.
872     *
873     * @return true on success
874     *
875     * @see #enableWifiMulticast
876     *
877     * @hide pending API council approval
878     */
879    public boolean disableWifiMulticast() {
880        try {
881            mService.disableWifiMulticast();
882            return true;
883        } catch (RemoteException e) {
884            return false;
885        }
886    }
887}
888