WifiManager.java revision 54b6cfa9a9e5b861a9930af873580d6dc20f773c
1094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea/*
2094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea * Copyright (C) 2008 The Android Open Source Project
3094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea *
4094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea * Licensed under the Apache License, Version 2.0 (the "License");
5094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea * you may not use this file except in compliance with the License.
6094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea * You may obtain a copy of the License at
7094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea *
8094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea *      http://www.apache.org/licenses/LICENSE-2.0
9094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea *
10094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea * Unless required by applicable law or agreed to in writing, software
11094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea * distributed under the License is distributed on an "AS IS" BASIS,
12094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea * See the License for the specific language governing permissions and
14094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea * limitations under the License.
15094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea */
16094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
17094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Maleapackage android.net.wifi;
18094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
19094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Maleaimport android.annotation.SdkConstant;
20094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Maleaimport android.annotation.SdkConstant.SdkConstantType;
21094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Maleaimport android.net.DhcpInfo;
22094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Maleaimport android.os.Binder;
23094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Maleaimport android.os.IBinder;
24094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Maleaimport android.os.Handler;
25094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Maleaimport android.os.RemoteException;
26094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
27094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Maleaimport com.android.internal.os.RuntimeInit;
28094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
29094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Maleaimport java.util.List;
30094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
31094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea/**
32094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea * This class provides the primary API for managing all aspects of Wi-Fi
33094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea * connectivity. Get an instance of this class by calling
34094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea * {@link android.content.Context#getSystemService(String) Context.getSystemService(Context.WIFI_SERVICE)}.
35094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea
36094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea * It deals with several categories of items:
37094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea * <ul>
38094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea * <li>The list of configured networks. The list can be viewed and updated,
39094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea * and attributes of individual entries can be modified.</li>
40094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea * <li>The currently active Wi-Fi network, if any. Connectivity can be
41094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea * established or torn down, and dynamic information about the state of
42094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea * the network can be queried.</li>
43094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea * <li>Results of access point scans, containing enough information to
44094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea * make decisions about what access point to connect to.</li>
45094881f513ab366f7ffd0b2c7778ab50281ca59eDaniel Malea * <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 DHCP-assigned addresses from the last successful DHCP request,
508     * if any.
509     * @return the DHCP information
510     */
511    public DhcpInfo getDhcpInfo() {
512        try {
513            return mService.getDhcpInfo();
514        } catch (RemoteException e) {
515            return null;
516        }
517    }
518
519
520    /**
521     * Enable or disable Wi-Fi.
522     * @param enabled {@code true} to enable, {@code false} to disable.
523     * @return {@code true} if the operation succeeds (or if the existing state
524     *         is the same as the requested state).
525     */
526    public boolean setWifiEnabled(boolean enabled) {
527        try {
528            return mService.setWifiEnabled(enabled);
529        } catch (RemoteException e) {
530            return false;
531        }
532    }
533
534    /**
535     * Gets the Wi-Fi enabled state.
536     * @return One of {@link #WIFI_STATE_DISABLED},
537     *         {@link #WIFI_STATE_DISABLING}, {@link #WIFI_STATE_ENABLED},
538     *         {@link #WIFI_STATE_ENABLING}, {@link #WIFI_STATE_UNKNOWN}
539     * @see #isWifiEnabled()
540     */
541    public int getWifiState() {
542        try {
543            return mService.getWifiState();
544        } catch (RemoteException e) {
545            return WIFI_STATE_UNKNOWN;
546        }
547    }
548
549    /**
550     * Return whether Wi-Fi is enabled or disabled.
551     * @return {@code true} if Wi-Fi is enabled
552     * @see #getWifiState()
553     */
554    public boolean isWifiEnabled() {
555        return getWifiState() == WIFI_STATE_ENABLED;
556    }
557
558    /**
559     * Calculates the level of the signal. This should be used any time a signal
560     * is being shown.
561     *
562     * @param rssi The power of the signal measured in RSSI.
563     * @param numLevels The number of levels to consider in the calculated
564     *            level.
565     * @return A level of the signal, given in the range of 0 to numLevels-1
566     *         (both inclusive).
567     */
568    public static int calculateSignalLevel(int rssi, int numLevels) {
569        if (rssi <= MIN_RSSI) {
570            return 0;
571        } else if (rssi >= MAX_RSSI) {
572            return numLevels - 1;
573        } else {
574            int partitionSize = (MAX_RSSI - MIN_RSSI) / (numLevels - 1);
575            return (rssi - MIN_RSSI) / partitionSize;
576        }
577    }
578
579    /**
580     * Compares two signal strengths.
581     *
582     * @param rssiA The power of the first signal measured in RSSI.
583     * @param rssiB The power of the second signal measured in RSSI.
584     * @return Returns <0 if the first signal is weaker than the second signal,
585     *         0 if the two signals have the same strength, and >0 if the first
586     *         signal is stronger than the second signal.
587     */
588    public static int compareSignalLevel(int rssiA, int rssiB) {
589        return rssiA - rssiB;
590    }
591
592    /**
593     * Allows an application to keep the Wi-Fi radio awake.
594     * Normally the Wi-Fi radio may turn off when the user has not used the device in a while.
595     * Acquiring a WifiLock will keep the radio on until the lock is released.  Multiple
596     * applications may hold WifiLocks, and the radio will only be allowed to turn off when no
597     * WifiLocks are held in any application.
598     *
599     * Before using a WifiLock, consider carefully if your application requires Wi-Fi access, or
600     * could function over a mobile network, if available.  A program that needs to download large
601     * files should hold a WifiLock to ensure that the download will complete, but a program whose
602     * network usage is occasional or low-bandwidth should not hold a WifiLock to avoid adversely
603     * affecting battery life.
604     *
605     * Note that WifiLocks cannot override the user-level "Wi-Fi Enabled" setting, nor Airplane
606     * Mode.  They simply keep the radio from turning off when Wi-Fi is already on but the device
607     * is idle.
608     */
609    public class WifiLock {
610        private String mTag;
611        private IBinder mBinder;
612        private int mRefCount;
613        private boolean mRefCounted;
614        private boolean mHeld;
615
616        private WifiLock(String tag) {
617            mTag = tag;
618            mBinder = new Binder();
619            mRefCount = 0;
620            mRefCounted = true;
621            mHeld = false;
622        }
623
624        /**
625         * Locks the Wi-Fi radio on until {@link #release} is called.
626         *
627         * If this WifiLock is reference-counted, each call to {@link #acquire} will increment the
628         * reference count, and the radio will remain locked as long as the reference count is
629         * above zero.
630         *
631         * If this WifiLock is not reference-counted, the first call to {@link #acquire} will lock
632         * the radio, but subsequent calls will be ignored.  Only one call to {@link #release}
633         * will be required, regardless of the number of times that {@link #acquire} is called.
634         */
635        public void acquire() {
636            synchronized (mBinder) {
637                if (mRefCounted ? (++mRefCount > 0) : (!mHeld)) {
638                    try {
639                        mService.acquireWifiLock(mBinder, mTag);
640                    } catch (RemoteException e) {
641                    }
642                    mHeld = true;
643                }
644            }
645        }
646
647        /**
648         * Unlocks the Wi-Fi radio, allowing it to turn off when the device is idle.
649         *
650         * If this WifiLock is reference-counted, each call to {@link #release} will decrement the
651         * reference count, and the radio will be unlocked only when the reference count reaches
652         * zero.  If the reference count goes below zero (that is, if {@link #release} is called
653         * a greater number of times than {@link #acquire}), an exception is thrown.
654         *
655         * If this WifiLock is not reference-counted, the first call to {@link #release} (after
656         * the radio was locked using {@link #acquire}) will unlock the radio, and subsequent
657         * calls will be ignored.
658         */
659        public void release() {
660            synchronized (mBinder) {
661                if (mRefCounted ? (--mRefCount == 0) : (mHeld)) {
662                    try {
663                        mService.releaseWifiLock(mBinder);
664                    } catch (RemoteException e) {
665                    }
666                    mHeld = false;
667                }
668                if (mRefCount < 0) {
669                    throw new RuntimeException("WifiLock under-locked " + mTag);
670                }
671            }
672        }
673
674        /**
675         * Controls whether this is a reference-counted or non-reference-counted WifiLock.
676         *
677         * Reference-counted WifiLocks keep track of the number of calls to {@link #acquire} and
678         * {@link #release}, and only allow the radio to sleep when every call to {@link #acquire}
679         * has been balanced with a call to {@link #release}.  Non-reference-counted WifiLocks
680         * lock the radio whenever {@link #acquire} is called and it is unlocked, and unlock the
681         * radio whenever {@link #release} is called and it is locked.
682         *
683         * @param refCounted true if this WifiLock should keep a reference count
684         */
685        public void setReferenceCounted(boolean refCounted) {
686            mRefCounted = refCounted;
687        }
688
689        /**
690         * Checks whether this WifiLock is currently held.
691         *
692         * @return true if this WifiLock is held, false otherwise
693         */
694        public boolean isHeld() {
695            synchronized (mBinder) {
696                return mHeld;
697            }
698        }
699
700        public String toString() {
701            String s1, s2, s3;
702            synchronized (mBinder) {
703                s1 = Integer.toHexString(System.identityHashCode(this));
704                s2 = mHeld ? "held; " : "";
705                if (mRefCounted) {
706                    s3 = "refcounted: refcount = " + mRefCount;
707                } else {
708                    s3 = "not refcounted";
709                }
710                return "WifiLock{ " + s1 + "; " + s2 + s3 + " }";
711            }
712        }
713
714        @Override
715        protected void finalize() throws Throwable {
716            synchronized (mBinder) {
717                if (mHeld) {
718                    try {
719                        mService.releaseWifiLock(mBinder);
720                    } catch (RemoteException e) {
721                    }
722                    RuntimeInit.crash("WifiLock", new Exception(
723                            "WifiLock finalized while still held: " + mTag));
724                }
725            }
726        }
727    }
728
729    /**
730     * Creates a new WifiLock.
731     *
732     * @param tag a tag for the WifiLock to identify it in debugging messages.  This string is
733     *            never shown to the user under normal conditions, but should be descriptive
734     *            enough to identify your application and the specific WifiLock within it, if it
735     *            holds multiple WifiLocks.
736     *
737     * @return a new, unacquired WifiLock with the given tag.
738     *
739     * @see WifiLock
740     */
741    public WifiLock createWifiLock(String tag) {
742        return new WifiLock(tag);
743    }
744
745}
746