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