WifiManager.java revision a5ec95cdb1a7d2024249277dff1f99d0046c9b56
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     * @return {@code true} if the operation succeeds, {@code false} otherwise, e.g.,
569     * {@code numChannels} is out of range.
570     *
571     * @hide pending API council
572     */
573    public boolean setNumAllowedChannels(int numChannels) {
574        try {
575            return mService.setNumAllowedChannels(numChannels);
576        } catch (RemoteException e) {
577            return false;
578        }
579    }
580
581    /**
582     * Return the list of valid values for the number of allowed radio channels
583     * for various regulatory domains.
584     * @return the list of channel counts, or {@code null} if the operation fails
585     *
586     * @hide pending API council review
587     */
588    public int[] getValidChannelCounts() {
589        try {
590            return mService.getValidChannelCounts();
591        } catch (RemoteException e) {
592            return null;
593        }
594   }
595
596    /**
597     * Return the DHCP-assigned addresses from the last successful DHCP request,
598     * if any.
599     * @return the DHCP information
600     */
601    public DhcpInfo getDhcpInfo() {
602        try {
603            return mService.getDhcpInfo();
604        } catch (RemoteException e) {
605            return null;
606        }
607    }
608
609
610    /**
611     * Enable or disable Wi-Fi.
612     * @param enabled {@code true} to enable, {@code false} to disable.
613     * @return {@code true} if the operation succeeds (or if the existing state
614     *         is the same as the requested state).
615     */
616    public boolean setWifiEnabled(boolean enabled) {
617        try {
618            return mService.setWifiEnabled(enabled);
619        } catch (RemoteException e) {
620            return false;
621        }
622    }
623
624    /**
625     * Gets the Wi-Fi enabled state.
626     * @return One of {@link #WIFI_STATE_DISABLED},
627     *         {@link #WIFI_STATE_DISABLING}, {@link #WIFI_STATE_ENABLED},
628     *         {@link #WIFI_STATE_ENABLING}, {@link #WIFI_STATE_UNKNOWN}
629     * @see #isWifiEnabled()
630     */
631    public int getWifiState() {
632        try {
633            return mService.getWifiEnabledState();
634        } catch (RemoteException e) {
635            return WIFI_STATE_UNKNOWN;
636        }
637    }
638
639    /**
640     * Return whether Wi-Fi is enabled or disabled.
641     * @return {@code true} if Wi-Fi is enabled
642     * @see #getWifiState()
643     */
644    public boolean isWifiEnabled() {
645        return getWifiState() == WIFI_STATE_ENABLED;
646    }
647
648    /**
649     * Calculates the level of the signal. This should be used any time a signal
650     * is being shown.
651     *
652     * @param rssi The power of the signal measured in RSSI.
653     * @param numLevels The number of levels to consider in the calculated
654     *            level.
655     * @return A level of the signal, given in the range of 0 to numLevels-1
656     *         (both inclusive).
657     */
658    public static int calculateSignalLevel(int rssi, int numLevels) {
659        if (rssi <= MIN_RSSI) {
660            return 0;
661        } else if (rssi >= MAX_RSSI) {
662            return numLevels - 1;
663        } else {
664            int partitionSize = (MAX_RSSI - MIN_RSSI) / (numLevels - 1);
665            return (rssi - MIN_RSSI) / partitionSize;
666        }
667    }
668
669    /**
670     * Compares two signal strengths.
671     *
672     * @param rssiA The power of the first signal measured in RSSI.
673     * @param rssiB The power of the second signal measured in RSSI.
674     * @return Returns <0 if the first signal is weaker than the second signal,
675     *         0 if the two signals have the same strength, and >0 if the first
676     *         signal is stronger than the second signal.
677     */
678    public static int compareSignalLevel(int rssiA, int rssiB) {
679        return rssiA - rssiB;
680    }
681
682    /**
683     * Allows an application to keep the Wi-Fi radio awake.
684     * Normally the Wi-Fi radio may turn off when the user has not used the device in a while.
685     * Acquiring a WifiLock will keep the radio on until the lock is released.  Multiple
686     * applications may hold WifiLocks, and the radio will only be allowed to turn off when no
687     * WifiLocks are held in any application.
688     *
689     * Before using a WifiLock, consider carefully if your application requires Wi-Fi access, or
690     * could function over a mobile network, if available.  A program that needs to download large
691     * files should hold a WifiLock to ensure that the download will complete, but a program whose
692     * network usage is occasional or low-bandwidth should not hold a WifiLock to avoid adversely
693     * affecting battery life.
694     *
695     * Note that WifiLocks cannot override the user-level "Wi-Fi Enabled" setting, nor Airplane
696     * Mode.  They simply keep the radio from turning off when Wi-Fi is already on but the device
697     * is idle.
698     */
699    public class WifiLock {
700        private String mTag;
701        private final IBinder mBinder;
702        private int mRefCount;
703        int mLockType;
704        private boolean mRefCounted;
705        private boolean mHeld;
706
707        private WifiLock(int lockType, String tag) {
708            mTag = tag;
709            mLockType = lockType;
710            mBinder = new Binder();
711            mRefCount = 0;
712            mRefCounted = true;
713            mHeld = false;
714        }
715
716        /**
717         * Locks the Wi-Fi radio on until {@link #release} is called.
718         *
719         * If this WifiLock is reference-counted, each call to {@code acquire} will increment the
720         * reference count, and the radio will remain locked as long as the reference count is
721         * above zero.
722         *
723         * If this WifiLock is not reference-counted, the first call to {@code acquire} will lock
724         * the radio, but subsequent calls will be ignored.  Only one call to {@link #release}
725         * will be required, regardless of the number of times that {@code acquire} is called.
726         */
727        public void acquire() {
728            synchronized (mBinder) {
729                if (mRefCounted ? (++mRefCount > 0) : (!mHeld)) {
730                    try {
731                        mService.acquireWifiLock(mBinder, mLockType, mTag);
732                        synchronized (WifiManager.this) {
733                            if (mActiveLockCount >= MAX_ACTIVE_LOCKS) {
734                                mService.releaseWifiLock(mBinder);
735                                throw new UnsupportedOperationException(
736                                            "Exceeded maximum number of wifi locks");
737                            }
738                            mActiveLockCount++;
739                        }
740                    } catch (RemoteException ignore) {
741                    }
742                    mHeld = true;
743                }
744            }
745        }
746
747        /**
748         * Unlocks the Wi-Fi radio, allowing it to turn off when the device is idle.
749         *
750         * If this WifiLock is reference-counted, each call to {@code release} will decrement the
751         * reference count, and the radio will be unlocked only when the reference count reaches
752         * zero.  If the reference count goes below zero (that is, if {@code release} is called
753         * a greater number of times than {@link #acquire}), an exception is thrown.
754         *
755         * If this WifiLock is not reference-counted, the first call to {@code release} (after
756         * the radio was locked using {@link #acquire}) will unlock the radio, and subsequent
757         * calls will be ignored.
758         */
759        public void release() {
760            synchronized (mBinder) {
761                if (mRefCounted ? (--mRefCount == 0) : (mHeld)) {
762                    try {
763                        mService.releaseWifiLock(mBinder);
764                        synchronized (WifiManager.this) {
765                            mActiveLockCount--;
766                        }
767                    } catch (RemoteException ignore) {
768                    }
769                    mHeld = false;
770                }
771                if (mRefCount < 0) {
772                    throw new RuntimeException("WifiLock under-locked " + mTag);
773                }
774            }
775        }
776
777        /**
778         * Controls whether this is a reference-counted or non-reference-counted WifiLock.
779         *
780         * Reference-counted WifiLocks keep track of the number of calls to {@link #acquire} and
781         * {@link #release}, and only allow the radio to sleep when every call to {@link #acquire}
782         * has been balanced with a call to {@link #release}.  Non-reference-counted WifiLocks
783         * lock the radio whenever {@link #acquire} is called and it is unlocked, and unlock the
784         * radio whenever {@link #release} is called and it is locked.
785         *
786         * @param refCounted true if this WifiLock should keep a reference count
787         */
788        public void setReferenceCounted(boolean refCounted) {
789            mRefCounted = refCounted;
790        }
791
792        /**
793         * Checks whether this WifiLock is currently held.
794         *
795         * @return true if this WifiLock is held, false otherwise
796         */
797        public boolean isHeld() {
798            synchronized (mBinder) {
799                return mHeld;
800            }
801        }
802
803        public String toString() {
804            String s1, s2, s3;
805            synchronized (mBinder) {
806                s1 = Integer.toHexString(System.identityHashCode(this));
807                s2 = mHeld ? "held; " : "";
808                if (mRefCounted) {
809                    s3 = "refcounted: refcount = " + mRefCount;
810                } else {
811                    s3 = "not refcounted";
812                }
813                return "WifiLock{ " + s1 + "; " + s2 + s3 + " }";
814            }
815        }
816
817        @Override
818        protected void finalize() throws Throwable {
819            super.finalize();
820            synchronized (mBinder) {
821                if (mHeld) {
822                    try {
823                        mService.releaseWifiLock(mBinder);
824                        synchronized (WifiManager.this) {
825                            mActiveLockCount--;
826                        }
827                    } catch (RemoteException ignore) {
828                    }
829                }
830            }
831        }
832    }
833
834    /**
835     * Creates a new WifiLock.
836     *
837     * @param lockType the type of lock to create. See {@link #WIFI_MODE_FULL} and
838     * {@link #WIFI_MODE_SCAN_ONLY} for descriptions of the types of Wi-Fi locks.
839     * @param tag a tag for the WifiLock to identify it in debugging messages.  This string is
840     *            never shown to the user under normal conditions, but should be descriptive
841     *            enough to identify your application and the specific WifiLock within it, if it
842     *            holds multiple WifiLocks.
843     *
844     * @return a new, unacquired WifiLock with the given tag.
845     *
846     * @see WifiLock
847     */
848    public WifiLock createWifiLock(int lockType, String tag) {
849        return new WifiLock(lockType, tag);
850    }
851
852    /**
853     * Creates a new WifiLock.
854     *
855     * @param tag a tag for the WifiLock to identify it in debugging messages.  This string is
856     *            never shown to the user under normal conditions, but should be descriptive
857     *            enough to identify your application and the specific WifiLock within it, if it
858     *            holds multiple WifiLocks.
859     *
860     * @return a new, unacquired WifiLock with the given tag.
861     *
862     * @see WifiLock
863     */
864    public WifiLock createWifiLock(String tag) {
865        return new WifiLock(WIFI_MODE_FULL, tag);
866    }
867
868
869    /**
870     * Create a new MulticastLock
871     *
872     * @param tag a tag for the MulticastLock to identify it in debugging
873     *            messages.
874     *
875     * @return a new, unacquired MulticastLock with the given tag.
876     *
877     * @see MulticastLock
878     */
879    public MulticastLock createMulticastLock(String tag) {
880        return new MulticastLock(tag);
881    }
882
883    /**
884     * Allows an application to receive Wifi Multicast packets.
885     * Normally the Wifi stack filters out packets not explicitly
886     * addressed to this device.  Acquring a MulticastLock will
887     * cause the stack to receive packets addressed to multicast
888     * addresses.  Processing these extra packets can cause a noticable
889     * battery drain and should be disabled when not needed
890     */
891    public class MulticastLock {
892        private String mTag;
893        private final IBinder mBinder;
894        private boolean mHeld;
895
896        private MulticastLock(String tag) {
897            mTag = tag;
898            mBinder = new Binder();
899            mHeld = false;
900        }
901
902        /**
903         * Locks Wifi Multicast on until {@link #release} is called.
904         *
905         * The first call to {@code acquire} will lock the Multicast on
906         * but subsequent calls will be ignored.  Only one call to
907         * {@link #release} will be required, regardless of the number of
908         * times that {@code acquire} is called.
909         *
910         * Note that other applications may also lock Wifi Multicast on.
911         * Only they can relinquish their lock.
912         *
913         * Also note that applications cannot leave Multicast locked on.
914         * When an app exits or crashes, any Multicast locks will be released.
915         */
916        public void acquire() {
917            synchronized (mBinder) {
918                if (!mHeld) {
919                    try {
920                        mService.acquireMulticastLock(mBinder, mTag);
921                        synchronized (WifiManager.this) {
922                            if (mActiveLockCount >= MAX_ACTIVE_LOCKS) {
923                                mService.releaseMulticastLock();
924                                throw new UnsupportedOperationException(
925                                        "Exceeded maximum number of wifi locks");
926                            }
927                            mActiveLockCount++;
928                        }
929                        mHeld = true;
930                    } catch (RemoteException ignore) {
931                    }
932                }
933            }
934        }
935
936        /**
937         * Unlocks Wifi Multicast, restoring the filter of packets
938         * not addressed specifically to this device and saving power.
939         *
940         * Note that if any other Wifi Multicast Locks are still outstanding
941         * this {@code release} call will not have an immediate effect.  Only
942         * when all applications have released all their Multicast Locks will
943         * the Multicast filter be turned back on.
944         *
945         * Also note that when an app exits or crashes all of its Multicast
946         * Locks will be automatically released.
947         */
948        public void release() {
949            synchronized (mBinder) {
950                if (mHeld) {
951                    try {
952                        mService.releaseMulticastLock();
953                        synchronized (WifiManager.this) {
954                            mActiveLockCount--;
955                        }
956                        mHeld = false;
957                    } catch (RemoteException ignore) {
958                    }
959                }
960            }
961        }
962
963        /**
964         * Checks whether this MulticastLock is currently held.
965         *
966         * @return true if this MulticastLock is held, false otherwise
967         */
968        public boolean isHeld() {
969            synchronized (mBinder) {
970                return mHeld;
971            }
972        }
973
974        public String toString() {
975            String s1, s2;
976            synchronized (mBinder) {
977                s1 = Integer.toHexString(System.identityHashCode(this));
978                s2 = mHeld ? "held; " : "";
979                return "MulticastLock{ " + s1 + "; " + s2 + " }";
980            }
981        }
982
983        @Override
984        protected void finalize() throws Throwable {
985            super.finalize();
986            release();
987        }
988    }
989
990    /**
991     * Check multicast filter status.
992     *
993     * @return true if multicast packets are allowed.
994     *
995     * @hide pending API council approval
996     */
997    public boolean isMulticastEnabled() {
998        try {
999            return mService.isMulticastEnabled();
1000        } catch (RemoteException e) {
1001            return false;
1002        }
1003    }
1004}
1005