WifiManager.java revision d24b8183b93e781080b2c16c487e60d51c12da31
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 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     * @hide pending API council review
235     */
236    public static final int WIFI_MODE_FULL = 1;
237    /**
238     * In this mode, Wi-Fi will be kept active,
239     * but the only operation that will be supported is initiation of
240     * scans, and the subsequent reporting of scan results. No attempts
241     * will be made to automatically connect to remembered access points,
242     * nor will periodic scans be automatically performed looking for
243     * remembered access points. Scans must be explicitly requested by
244     * an application in this mode.
245     * @hide pending API council review
246     */
247    public static final int WIFI_MODE_SCAN_ONLY = 2;
248
249    /** Anything worse than or equal to this will show 0 bars. */
250    private static final int MIN_RSSI = -100;
251
252    /** Anything better than or equal to this will show the max bars. */
253    private static final int MAX_RSSI = -55;
254
255    IWifiManager mService;
256    Handler mHandler;
257
258    /**
259     * Create a new WifiManager instance.
260     * Applications will almost always want to use
261     * {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve
262     * the standard {@link android.content.Context#WIFI_SERVICE Context.WIFI_SERVICE}.
263     * @param service the Binder interface
264     * @param handler target for messages
265     * @hide - hide this because it takes in a parameter of type IWifiManager, which
266     * is a system private class.
267     */
268    public WifiManager(IWifiManager service, Handler handler) {
269        mService = service;
270        mHandler = handler;
271    }
272
273    /**
274     * Return a list of all the networks configured in the supplicant.
275     * Not all fields of WifiConfiguration are returned. Only the following
276     * fields are filled in:
277     * <ul>
278     * <li>networkId</li>
279     * <li>SSID</li>
280     * <li>BSSID</li>
281     * <li>priority</li>
282     * <li>allowedProtocols</li>
283     * <li>allowedKeyManagement</li>
284     * <li>allowedAuthAlgorithms</li>
285     * <li>allowedPairwiseCiphers</li>
286     * <li>allowedGroupCiphers</li>
287     * </ul>
288     * @return a list of network configurations in the form of a list
289     * of {@link WifiConfiguration} objects.
290     */
291    public List<WifiConfiguration> getConfiguredNetworks() {
292        try {
293            return mService.getConfiguredNetworks();
294        } catch (RemoteException e) {
295            return null;
296        }
297    }
298
299    /**
300     * Add a new network description to the set of configured networks.
301     * The {@code networkId} field of the supplied configuration object
302     * is ignored.
303     * <p/>
304     * The new network will be marked DISABLED by default. To enable it,
305     * called {@link #enableNetwork}.
306     *
307     * @param config the set of variables that describe the configuration,
308     *            contained in a {@link WifiConfiguration} object.
309     * @return the ID of the newly created network description. This is used in
310     *         other operations to specified the network to be acted upon.
311     *         Returns {@code -1} on failure.
312     */
313    public int addNetwork(WifiConfiguration config) {
314        if (config == null) {
315            return -1;
316        }
317        config.networkId = -1;
318        return addOrUpdateNetwork(config);
319    }
320
321    /**
322     * Update the network description of an existing configured network.
323     *
324     * @param config the set of variables that describe the configuration,
325     *            contained in a {@link WifiConfiguration} object. It may
326     *            be sparse, so that only the items that are being changed
327     *            are non-<code>null</code>. The {@code networkId} field
328     *            must be set to the ID of the existing network being updated.
329     * @return Returns the {@code networkId} of the supplied
330     *         {@code WifiConfiguration} on success.
331     *         <br/>
332     *         Returns {@code -1} on failure, including when the {@code networkId}
333     *         field of the {@code WifiConfiguration} does not refer to an
334     *         existing network.
335     */
336    public int updateNetwork(WifiConfiguration config) {
337        if (config == null || config.networkId < 0) {
338            return -1;
339        }
340        return addOrUpdateNetwork(config);
341    }
342
343    /**
344     * Internal method for doing the RPC that creates a new network description
345     * or updates an existing one.
346     *
347     * @param config The possibly sparse object containing the variables that
348     *         are to set or updated in the network description.
349     * @return the ID of the network on success, {@code -1} on failure.
350     */
351    private int addOrUpdateNetwork(WifiConfiguration config) {
352        try {
353            return mService.addOrUpdateNetwork(config);
354        } catch (RemoteException e) {
355            return -1;
356        }
357    }
358
359    /**
360     * Remove the specified network from the list of configured networks.
361     * This may result in the asynchronous delivery of state change
362     * events.
363     * @param netId the integer that identifies the network configuration
364     * to the supplicant
365     * @return {@code true} if the operation succeeded
366     */
367    public boolean removeNetwork(int netId) {
368        try {
369            return mService.removeNetwork(netId);
370        } catch (RemoteException e) {
371            return false;
372        }
373    }
374
375    /**
376     * Allow a previously configured network to be associated with. If
377     * <code>disableOthers</code> is true, then all other configured
378     * networks are disabled, and an attempt to connect to the selected
379     * network is initiated. This may result in the asynchronous delivery
380     * of state change events.
381     * @param netId the ID of the network in the list of configured networks
382     * @param disableOthers if true, disable all other networks. The way to
383     * select a particular network to connect to is specify {@code true}
384     * for this parameter.
385     * @return {@code true} if the operation succeeded
386     */
387    public boolean enableNetwork(int netId, boolean disableOthers) {
388        try {
389            return mService.enableNetwork(netId, disableOthers);
390        } catch (RemoteException e) {
391            return false;
392        }
393    }
394
395    /**
396     * Disable a configured network. The specified network will not be
397     * a candidate for associating. This may result in the asynchronous
398     * delivery of state change events.
399     * @param netId the ID of the network as returned by {@link #addNetwork}.
400     * @return {@code true} if the operation succeeded
401     */
402    public boolean disableNetwork(int netId) {
403        try {
404            return mService.disableNetwork(netId);
405        } catch (RemoteException e) {
406            return false;
407        }
408    }
409
410    /**
411     * Disassociate from the currently active access point. This may result
412     * in the asynchronous delivery of state change events.
413     * @return {@code true} if the operation succeeded
414     */
415    public boolean disconnect() {
416        try {
417            return mService.disconnect();
418        } catch (RemoteException e) {
419            return false;
420        }
421    }
422
423    /**
424     * Reconnect to the currently active access point, if we are currently
425     * disconnected. This may result in the asynchronous delivery of state
426     * change events.
427     * @return {@code true} if the operation succeeded
428     */
429    public boolean reconnect() {
430        try {
431            return mService.reconnect();
432        } catch (RemoteException e) {
433            return false;
434        }
435    }
436
437    /**
438     * Reconnect to the currently active access point, even if we are already
439     * connected. This may result in the asynchronous delivery of state
440     * change events.
441     * @return {@code true} if the operation succeeded
442     */
443    public boolean reassociate() {
444        try {
445            return mService.reassociate();
446        } catch (RemoteException e) {
447            return false;
448        }
449    }
450
451    /**
452     * Check that the supplicant daemon is responding to requests.
453     * @return {@code true} if we were able to communicate with the supplicant and
454     * it returned the expected response to the PING message.
455     */
456    public boolean pingSupplicant() {
457        if (mService == null)
458            return false;
459        try {
460            return mService.pingSupplicant();
461        } catch (RemoteException e) {
462            return false;
463        }
464    }
465
466    /**
467     * Request a scan for access points. Returns immediately. The availability
468     * of the results is made known later by means of an asynchronous event sent
469     * on completion of the scan.
470     * @return {@code true} if the operation succeeded, i.e., the scan was initiated
471     */
472    public boolean  startScan() {
473        try {
474            return mService.startScan();
475        } catch (RemoteException e) {
476            return false;
477        }
478    }
479
480    /**
481     * Return dynamic information about the current Wi-Fi connection, if any is active.
482     * @return the Wi-Fi information, contained in {@link WifiInfo}.
483     */
484    public WifiInfo getConnectionInfo() {
485        try {
486            return mService.getConnectionInfo();
487        } catch (RemoteException e) {
488            return null;
489        }
490    }
491
492    /**
493     * Return the results of the latest access point scan.
494     * @return the list of access points found in the most recent scan.
495     */
496    public List<ScanResult> getScanResults() {
497        try {
498            return mService.getScanResults();
499        } catch (RemoteException e) {
500            return null;
501        }
502    }
503
504    /**
505     * Tell the supplicant to persist the current list of configured networks.
506     * <p>
507     * Note: It is possible for this method to change the network IDs of
508     * existing networks. You should assume the network IDs can be different
509     * after calling this method.
510     *
511     * @return {@code true} if the operation succeeded
512     */
513    public boolean saveConfiguration() {
514        try {
515            return mService.saveConfiguration();
516        } catch (RemoteException e) {
517            return false;
518        }
519    }
520
521    /**
522     * Return the number of frequency channels that are allowed
523     * to be used in the current regulatory domain.
524     * @return the number of allowed channels, or {@code -1} if an error occurs
525     *
526     * @hide pending API council
527     */
528    public int getNumAllowedChannels() {
529        try {
530            return mService.getNumAllowedChannels();
531        } catch (RemoteException e) {
532            return -1;
533        }
534    }
535
536    /**
537     * Set the number of frequency channels that are allowed to be used
538     * in the current regulatory domain. This method should be used only
539     * if the correct number of channels cannot be determined automatically
540     * for some reason.
541     * @param numChannels the number of allowed channels. Must be greater than 0
542     * and less than or equal to 16.
543     * @return {@code true} if the operation succeeds, {@code false} otherwise, e.g.,
544     * {@code numChannels} is out of range.
545     *
546     * @hide pending API council
547     */
548    public boolean setNumAllowedChannels(int numChannels) {
549        try {
550            return mService.setNumAllowedChannels(numChannels);
551        } catch (RemoteException e) {
552            return false;
553        }
554    }
555
556    /**
557     * Return the list of valid values for the number of allowed radio channels
558     * for various regulatory domains.
559     * @return the list of channel counts, or {@code null} if the operation fails
560     *
561     * @hide pending API council review
562     */
563    public int[] getValidChannelCounts() {
564        try {
565            return mService.getValidChannelCounts();
566        } catch (RemoteException e) {
567            return null;
568        }
569   }
570
571    /**
572     * Return the DHCP-assigned addresses from the last successful DHCP request,
573     * if any.
574     * @return the DHCP information
575     */
576    public DhcpInfo getDhcpInfo() {
577        try {
578            return mService.getDhcpInfo();
579        } catch (RemoteException e) {
580            return null;
581        }
582    }
583
584
585    /**
586     * Enable or disable Wi-Fi.
587     * @param enabled {@code true} to enable, {@code false} to disable.
588     * @return {@code true} if the operation succeeds (or if the existing state
589     *         is the same as the requested state).
590     */
591    public boolean setWifiEnabled(boolean enabled) {
592        try {
593            return mService.setWifiEnabled(enabled);
594        } catch (RemoteException e) {
595            return false;
596        }
597    }
598
599    /**
600     * Gets the Wi-Fi enabled state.
601     * @return One of {@link #WIFI_STATE_DISABLED},
602     *         {@link #WIFI_STATE_DISABLING}, {@link #WIFI_STATE_ENABLED},
603     *         {@link #WIFI_STATE_ENABLING}, {@link #WIFI_STATE_UNKNOWN}
604     * @see #isWifiEnabled()
605     */
606    public int getWifiState() {
607        try {
608            return mService.getWifiEnabledState();
609        } catch (RemoteException e) {
610            return WIFI_STATE_UNKNOWN;
611        }
612    }
613
614    /**
615     * Return whether Wi-Fi is enabled or disabled.
616     * @return {@code true} if Wi-Fi is enabled
617     * @see #getWifiState()
618     */
619    public boolean isWifiEnabled() {
620        return getWifiState() == WIFI_STATE_ENABLED;
621    }
622
623    /**
624     * Calculates the level of the signal. This should be used any time a signal
625     * is being shown.
626     *
627     * @param rssi The power of the signal measured in RSSI.
628     * @param numLevels The number of levels to consider in the calculated
629     *            level.
630     * @return A level of the signal, given in the range of 0 to numLevels-1
631     *         (both inclusive).
632     */
633    public static int calculateSignalLevel(int rssi, int numLevels) {
634        if (rssi <= MIN_RSSI) {
635            return 0;
636        } else if (rssi >= MAX_RSSI) {
637            return numLevels - 1;
638        } else {
639            int partitionSize = (MAX_RSSI - MIN_RSSI) / (numLevels - 1);
640            return (rssi - MIN_RSSI) / partitionSize;
641        }
642    }
643
644    /**
645     * Compares two signal strengths.
646     *
647     * @param rssiA The power of the first signal measured in RSSI.
648     * @param rssiB The power of the second signal measured in RSSI.
649     * @return Returns <0 if the first signal is weaker than the second signal,
650     *         0 if the two signals have the same strength, and >0 if the first
651     *         signal is stronger than the second signal.
652     */
653    public static int compareSignalLevel(int rssiA, int rssiB) {
654        return rssiA - rssiB;
655    }
656
657    /**
658     * Allows an application to keep the Wi-Fi radio awake.
659     * Normally the Wi-Fi radio may turn off when the user has not used the device in a while.
660     * Acquiring a WifiLock will keep the radio on until the lock is released.  Multiple
661     * applications may hold WifiLocks, and the radio will only be allowed to turn off when no
662     * WifiLocks are held in any application.
663     *
664     * Before using a WifiLock, consider carefully if your application requires Wi-Fi access, or
665     * could function over a mobile network, if available.  A program that needs to download large
666     * files should hold a WifiLock to ensure that the download will complete, but a program whose
667     * network usage is occasional or low-bandwidth should not hold a WifiLock to avoid adversely
668     * affecting battery life.
669     *
670     * Note that WifiLocks cannot override the user-level "Wi-Fi Enabled" setting, nor Airplane
671     * Mode.  They simply keep the radio from turning off when Wi-Fi is already on but the device
672     * is idle.
673     */
674    public class WifiLock {
675        private String mTag;
676        private final IBinder mBinder;
677        private int mRefCount;
678        int mLockType;
679        private boolean mRefCounted;
680        private boolean mHeld;
681
682        private WifiLock(int lockType, String tag) {
683            mTag = tag;
684            mLockType = lockType;
685            mBinder = new Binder();
686            mRefCount = 0;
687            mRefCounted = true;
688            mHeld = false;
689        }
690
691        /**
692         * Locks the Wi-Fi radio on until {@link #release} is called.
693         *
694         * If this WifiLock is reference-counted, each call to {@code acquire} will increment the
695         * reference count, and the radio will remain locked as long as the reference count is
696         * above zero.
697         *
698         * If this WifiLock is not reference-counted, the first call to {@code acquire} will lock
699         * the radio, but subsequent calls will be ignored.  Only one call to {@link #release}
700         * will be required, regardless of the number of times that {@code acquire} is called.
701         */
702        public void acquire() {
703            synchronized (mBinder) {
704                if (mRefCounted ? (++mRefCount > 0) : (!mHeld)) {
705                    try {
706                        mService.acquireWifiLock(mBinder, mLockType, mTag);
707                    } catch (RemoteException ignore) {
708                    }
709                    mHeld = true;
710                }
711            }
712        }
713
714        /**
715         * Unlocks the Wi-Fi radio, allowing it to turn off when the device is idle.
716         *
717         * If this WifiLock is reference-counted, each call to {@code release} will decrement the
718         * reference count, and the radio will be unlocked only when the reference count reaches
719         * zero.  If the reference count goes below zero (that is, if {@code release} is called
720         * a greater number of times than {@link #acquire}), an exception is thrown.
721         *
722         * If this WifiLock is not reference-counted, the first call to {@code release} (after
723         * the radio was locked using {@link #acquire}) will unlock the radio, and subsequent
724         * calls will be ignored.
725         */
726        public void release() {
727            synchronized (mBinder) {
728                if (mRefCounted ? (--mRefCount == 0) : (mHeld)) {
729                    try {
730                        mService.releaseWifiLock(mBinder);
731                    } catch (RemoteException ignore) {
732                    }
733                    mHeld = false;
734                }
735                if (mRefCount < 0) {
736                    throw new RuntimeException("WifiLock under-locked " + mTag);
737                }
738            }
739        }
740
741        /**
742         * Controls whether this is a reference-counted or non-reference-counted WifiLock.
743         *
744         * Reference-counted WifiLocks keep track of the number of calls to {@link #acquire} and
745         * {@link #release}, and only allow the radio to sleep when every call to {@link #acquire}
746         * has been balanced with a call to {@link #release}.  Non-reference-counted WifiLocks
747         * lock the radio whenever {@link #acquire} is called and it is unlocked, and unlock the
748         * radio whenever {@link #release} is called and it is locked.
749         *
750         * @param refCounted true if this WifiLock should keep a reference count
751         */
752        public void setReferenceCounted(boolean refCounted) {
753            mRefCounted = refCounted;
754        }
755
756        /**
757         * Checks whether this WifiLock is currently held.
758         *
759         * @return true if this WifiLock is held, false otherwise
760         */
761        public boolean isHeld() {
762            synchronized (mBinder) {
763                return mHeld;
764            }
765        }
766
767        public String toString() {
768            String s1, s2, s3;
769            synchronized (mBinder) {
770                s1 = Integer.toHexString(System.identityHashCode(this));
771                s2 = mHeld ? "held; " : "";
772                if (mRefCounted) {
773                    s3 = "refcounted: refcount = " + mRefCount;
774                } else {
775                    s3 = "not refcounted";
776                }
777                return "WifiLock{ " + s1 + "; " + s2 + s3 + " }";
778            }
779        }
780
781        @Override
782        protected void finalize() throws Throwable {
783            super.finalize();
784            synchronized (mBinder) {
785                if (mHeld) {
786                    try {
787                        mService.releaseWifiLock(mBinder);
788                    } catch (RemoteException ignore) {
789                    }
790                }
791            }
792        }
793    }
794
795    /**
796     * Creates a new WifiLock.
797     *
798     * @param lockType the type of lock to create. See {@link #WIFI_MODE_FULL} and
799     * {@link #WIFI_MODE_SCAN_ONLY} for descriptions of the types of Wi-Fi locks.
800     * @param tag a tag for the WifiLock to identify it in debugging messages.  This string is
801     *            never shown to the user under normal conditions, but should be descriptive
802     *            enough to identify your application and the specific WifiLock within it, if it
803     *            holds multiple WifiLocks.
804     *
805     * @return a new, unacquired WifiLock with the given tag.
806     *
807     * @see WifiLock
808     *
809     * @hide pending API council review
810     */
811    public WifiLock createWifiLock(int lockType, String tag) {
812        return new WifiLock(lockType, tag);
813    }
814
815    /**
816     * Creates a new WifiLock.
817     *
818     * @param tag a tag for the WifiLock to identify it in debugging messages.  This string is
819     *            never shown to the user under normal conditions, but should be descriptive
820     *            enough to identify your application and the specific WifiLock within it, if it
821     *            holds multiple WifiLocks.
822     *
823     * @return a new, unacquired WifiLock with the given tag.
824     *
825     * @see WifiLock
826     */
827    public WifiLock createWifiLock(String tag) {
828        return new WifiLock(WIFI_MODE_FULL, tag);
829    }
830}
831