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