WifiManager.java revision 227bec49157bc496f7c9e8e8f63c12728a448922
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;
26import android.os.WorkSource;
27import android.os.Messenger;
28
29import java.util.List;
30
31/**
32 * This class provides the primary API for managing all aspects of Wi-Fi
33 * connectivity. Get an instance of this class by calling
34 * {@link android.content.Context#getSystemService(String) Context.getSystemService(Context.WIFI_SERVICE)}.
35
36 * It deals with several categories of items:
37 * <ul>
38 * <li>The list of configured networks. The list can be viewed and updated,
39 * and attributes of individual entries can be modified.</li>
40 * <li>The currently active Wi-Fi network, if any. Connectivity can be
41 * established or torn down, and dynamic information about the state of
42 * the network can be queried.</li>
43 * <li>Results of access point scans, containing enough information to
44 * make decisions about what access point to connect to.</li>
45 * <li>It defines the names of various Intent actions that are broadcast
46 * upon any sort of change in Wi-Fi state.
47 * </ul>
48 * This is the API to use when performing Wi-Fi specific operations. To
49 * perform operations that pertain to network connectivity at an abstract
50 * level, use {@link android.net.ConnectivityManager}.
51 */
52public class WifiManager {
53
54    // Supplicant error codes:
55    /**
56     * The error code if there was a problem authenticating.
57     */
58    public static final int ERROR_AUTHENTICATING = 1;
59
60    /**
61     * Broadcast intent action indicating that Wi-Fi has been enabled, disabled,
62     * enabling, disabling, or unknown. One extra provides this state as an int.
63     * Another extra provides the previous state, if available.
64     *
65     * @see #EXTRA_WIFI_STATE
66     * @see #EXTRA_PREVIOUS_WIFI_STATE
67     */
68    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
69    public static final String WIFI_STATE_CHANGED_ACTION =
70        "android.net.wifi.WIFI_STATE_CHANGED";
71    /**
72     * The lookup key for an int that indicates whether Wi-Fi is enabled,
73     * disabled, enabling, disabling, or unknown.  Retrieve it with
74     * {@link android.content.Intent#getIntExtra(String,int)}.
75     *
76     * @see #WIFI_STATE_DISABLED
77     * @see #WIFI_STATE_DISABLING
78     * @see #WIFI_STATE_ENABLED
79     * @see #WIFI_STATE_ENABLING
80     * @see #WIFI_STATE_UNKNOWN
81     */
82    public static final String EXTRA_WIFI_STATE = "wifi_state";
83    /**
84     * The previous Wi-Fi state.
85     *
86     * @see #EXTRA_WIFI_STATE
87     */
88    public static final String EXTRA_PREVIOUS_WIFI_STATE = "previous_wifi_state";
89
90    /**
91     * Wi-Fi is currently being disabled. The state will change to {@link #WIFI_STATE_DISABLED} if
92     * it finishes successfully.
93     *
94     * @see #WIFI_STATE_CHANGED_ACTION
95     * @see #getWifiState()
96     */
97    public static final int WIFI_STATE_DISABLING = 0;
98    /**
99     * Wi-Fi is disabled.
100     *
101     * @see #WIFI_STATE_CHANGED_ACTION
102     * @see #getWifiState()
103     */
104    public static final int WIFI_STATE_DISABLED = 1;
105    /**
106     * Wi-Fi is currently being enabled. The state will change to {@link #WIFI_STATE_ENABLED} if
107     * it finishes successfully.
108     *
109     * @see #WIFI_STATE_CHANGED_ACTION
110     * @see #getWifiState()
111     */
112    public static final int WIFI_STATE_ENABLING = 2;
113    /**
114     * Wi-Fi is enabled.
115     *
116     * @see #WIFI_STATE_CHANGED_ACTION
117     * @see #getWifiState()
118     */
119    public static final int WIFI_STATE_ENABLED = 3;
120    /**
121     * Wi-Fi is in an unknown state. This state will occur when an error happens while enabling
122     * or disabling.
123     *
124     * @see #WIFI_STATE_CHANGED_ACTION
125     * @see #getWifiState()
126     */
127    public static final int WIFI_STATE_UNKNOWN = 4;
128
129    /**
130     * Broadcast intent action indicating that Wi-Fi AP has been enabled, disabled,
131     * enabling, disabling, or failed.
132     *
133     * @hide
134     */
135    public static final String WIFI_AP_STATE_CHANGED_ACTION =
136        "android.net.wifi.WIFI_AP_STATE_CHANGED";
137
138    /**
139     * The lookup key for an int that indicates whether Wi-Fi AP is enabled,
140     * disabled, enabling, disabling, or failed.  Retrieve it with
141     * {@link android.content.Intent#getIntExtra(String,int)}.
142     *
143     * @see #WIFI_AP_STATE_DISABLED
144     * @see #WIFI_AP_STATE_DISABLING
145     * @see #WIFI_AP_STATE_ENABLED
146     * @see #WIFI_AP_STATE_ENABLING
147     * @see #WIFI_AP_STATE_FAILED
148     *
149     * @hide
150     */
151    public static final String EXTRA_WIFI_AP_STATE = "wifi_state";
152    /**
153     * The previous Wi-Fi state.
154     *
155     * @see #EXTRA_WIFI_AP_STATE
156     *
157     * @hide
158     */
159    public static final String EXTRA_PREVIOUS_WIFI_AP_STATE = "previous_wifi_state";
160    /**
161     * Wi-Fi AP is currently being disabled. The state will change to
162     * {@link #WIFI_AP_STATE_DISABLED} if it finishes successfully.
163     *
164     * @see #WIFI_AP_STATE_CHANGED_ACTION
165     * @see #getWifiApState()
166     *
167     * @hide
168     */
169    public static final int WIFI_AP_STATE_DISABLING = 10;
170    /**
171     * Wi-Fi AP is disabled.
172     *
173     * @see #WIFI_AP_STATE_CHANGED_ACTION
174     * @see #getWifiState()
175     *
176     * @hide
177     */
178    public static final int WIFI_AP_STATE_DISABLED = 11;
179    /**
180     * Wi-Fi AP is currently being enabled. The state will change to
181     * {@link #WIFI_AP_STATE_ENABLED} if it finishes successfully.
182     *
183     * @see #WIFI_AP_STATE_CHANGED_ACTION
184     * @see #getWifiApState()
185     *
186     * @hide
187     */
188    public static final int WIFI_AP_STATE_ENABLING = 12;
189    /**
190     * Wi-Fi AP is enabled.
191     *
192     * @see #WIFI_AP_STATE_CHANGED_ACTION
193     * @see #getWifiApState()
194     *
195     * @hide
196     */
197    public static final int WIFI_AP_STATE_ENABLED = 13;
198    /**
199     * Wi-Fi AP is in a failed state. This state will occur when an error occurs during
200     * enabling or disabling
201     *
202     * @see #WIFI_AP_STATE_CHANGED_ACTION
203     * @see #getWifiApState()
204     *
205     * @hide
206     */
207    public static final int WIFI_AP_STATE_FAILED = 14;
208
209    /**
210     * Broadcast intent action indicating that a connection to the supplicant has
211     * been established (and it is now possible
212     * to perform Wi-Fi operations) or the connection to the supplicant has been
213     * lost. One extra provides the connection state as a boolean, where {@code true}
214     * means CONNECTED.
215     * @see #EXTRA_SUPPLICANT_CONNECTED
216     */
217    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
218    public static final String SUPPLICANT_CONNECTION_CHANGE_ACTION =
219        "android.net.wifi.supplicant.CONNECTION_CHANGE";
220    /**
221     * The lookup key for a boolean that indicates whether a connection to
222     * the supplicant daemon has been gained or lost. {@code true} means
223     * a connection now exists.
224     * Retrieve it with {@link android.content.Intent#getBooleanExtra(String,boolean)}.
225     */
226    public static final String EXTRA_SUPPLICANT_CONNECTED = "connected";
227    /**
228     * Broadcast intent action indicating that the state of Wi-Fi connectivity
229     * has changed. One extra provides the new state
230     * in the form of a {@link android.net.NetworkInfo} object. If the new state is
231     * CONNECTED, a second extra may provide the BSSID of the access point,
232     * as a {@code String}.
233     * @see #EXTRA_NETWORK_INFO
234     * @see #EXTRA_BSSID
235     */
236    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
237    public static final String NETWORK_STATE_CHANGED_ACTION = "android.net.wifi.STATE_CHANGE";
238    /**
239     * The lookup key for a {@link android.net.NetworkInfo} object associated with the
240     * Wi-Fi network. Retrieve with
241     * {@link android.content.Intent#getParcelableExtra(String)}.
242     */
243    public static final String EXTRA_NETWORK_INFO = "networkInfo";
244    /**
245     * The lookup key for a String giving the BSSID of the access point to which
246     * we are connected. Only present when the new state is CONNECTED.
247     * Retrieve with
248     * {@link android.content.Intent#getStringExtra(String)}.
249     */
250    public static final String EXTRA_BSSID = "bssid";
251    /**
252     * Broadcast intent action indicating that the state of establishing a connection to
253     * an access point has changed.One extra provides the new
254     * {@link SupplicantState}. Note that the supplicant state is Wi-Fi specific, and
255     * is not generally the most useful thing to look at if you are just interested in
256     * the overall state of connectivity.
257     * @see #EXTRA_NEW_STATE
258     * @see #EXTRA_SUPPLICANT_ERROR
259     */
260    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
261    public static final String SUPPLICANT_STATE_CHANGED_ACTION =
262        "android.net.wifi.supplicant.STATE_CHANGE";
263    /**
264     * The lookup key for a {@link SupplicantState} describing the new state
265     * Retrieve with
266     * {@link android.content.Intent#getParcelableExtra(String)}.
267     */
268    public static final String EXTRA_NEW_STATE = "newState";
269
270    /**
271     * The lookup key for a {@link SupplicantState} describing the supplicant
272     * error code if any
273     * Retrieve with
274     * {@link android.content.Intent#getIntExtra(String, int)}.
275     * @see #ERROR_AUTHENTICATING
276     */
277    public static final String EXTRA_SUPPLICANT_ERROR = "supplicantError";
278
279    /**
280     * Broadcast intent action for reporting errors
281     * @hide
282     */
283    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
284    public static final String ERROR_ACTION = "android.net.wifi.ERROR";
285    /**
286     * The type of error being reported
287     * @hide
288     */
289    public static final String EXTRA_ERROR_CODE = "errorCode";
290
291    /**
292     * Valid error codes
293     * @hide
294     */
295    public static final int WPS_OVERLAP_ERROR = 1;
296
297    /**
298     * Broadcast intent action indicating that the configured networks changed.
299     * This can be as a result of adding/updating/deleting a network
300     * @hide
301     */
302    public static final String CONFIGURED_NETWORKS_CHANGED_ACTION =
303        "android.net.wifi.CONFIGURED_NETWORKS_CHANGE";
304    /**
305     * An access point scan has completed, and results are available from the supplicant.
306     * Call {@link #getScanResults()} to obtain the results.
307     */
308    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
309    public static final String SCAN_RESULTS_AVAILABLE_ACTION = "android.net.wifi.SCAN_RESULTS";
310    /**
311     * The RSSI (signal strength) has changed.
312     * @see #EXTRA_NEW_RSSI
313     */
314    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
315    public static final String RSSI_CHANGED_ACTION = "android.net.wifi.RSSI_CHANGED";
316    /**
317     * The lookup key for an {@code int} giving the new RSSI in dBm.
318     */
319    public static final String EXTRA_NEW_RSSI = "newRssi";
320
321    /**
322     * Broadcast intent action indicating that the link configuration
323     * changed on wifi.
324     * @hide
325     */
326    public static final String LINK_CONFIGURATION_CHANGED_ACTION =
327        "android.net.wifi.LINK_CONFIGURATION_CHANGED";
328
329    /**
330     * The lookup key for a {@link android.net.LinkProperties} object associated with the
331     * Wi-Fi network. Retrieve with
332     * {@link android.content.Intent#getParcelableExtra(String)}.
333     * @hide
334     */
335    public static final String EXTRA_LINK_PROPERTIES = "linkProperties";
336
337    /**
338     * The lookup key for a {@link android.net.LinkCapabilities} object associated with the
339     * Wi-Fi network. Retrieve with
340     * {@link android.content.Intent#getParcelableExtra(String)}.
341     * @hide
342     */
343    public static final String EXTRA_LINK_CAPABILITIES = "linkCapabilities";
344
345    /**
346     * The network IDs of the configured networks could have changed.
347     */
348    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
349    public static final String NETWORK_IDS_CHANGED_ACTION = "android.net.wifi.NETWORK_IDS_CHANGED";
350
351    /**
352     * Activity Action: Pick a Wi-Fi network to connect to.
353     * <p>Input: Nothing.
354     * <p>Output: Nothing.
355     */
356    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
357    public static final String ACTION_PICK_WIFI_NETWORK = "android.net.wifi.PICK_WIFI_NETWORK";
358
359    /**
360     * In this Wi-Fi lock mode, Wi-Fi will be kept active,
361     * and will behave normally, i.e., it will attempt to automatically
362     * establish a connection to a remembered access point that is
363     * within range, and will do periodic scans if there are remembered
364     * access points but none are in range.
365     */
366    public static final int WIFI_MODE_FULL = 1;
367    /**
368     * In this Wi-Fi lock mode, Wi-Fi will be kept active,
369     * but the only operation that will be supported is initiation of
370     * scans, and the subsequent reporting of scan results. No attempts
371     * will be made to automatically connect to remembered access points,
372     * nor will periodic scans be automatically performed looking for
373     * remembered access points. Scans must be explicitly requested by
374     * an application in this mode.
375     */
376    public static final int WIFI_MODE_SCAN_ONLY = 2;
377    /**
378     * In this Wi-Fi lock mode, Wi-Fi will be kept active as in mode
379     * {@link #WIFI_MODE_FULL} but it operates at high performance
380     * with minimum packet loss and low packet latency even when
381     * the device screen is off. This mode will consume more power
382     * and hence should be used only when there is a need for such
383     * an active connection.
384     * <p>
385     * An example use case is when a voice connection needs to be
386     * kept active even after the device screen goes off. Holding the
387     * regular {@link #WIFI_MODE_FULL} lock will keep the wifi
388     * connection active, but the connection can be lossy.
389     * Holding a {@link #WIFI_MODE_FULL_HIGH_PERF} lock for the
390     * duration of the voice call will improve the call quality.
391     * <p>
392     * When there is no support from the hardware, this lock mode
393     * will have the same behavior as {@link #WIFI_MODE_FULL}
394     */
395    public static final int WIFI_MODE_FULL_HIGH_PERF = 3;
396
397    /** Anything worse than or equal to this will show 0 bars. */
398    private static final int MIN_RSSI = -100;
399
400    /** Anything better than or equal to this will show the max bars. */
401    private static final int MAX_RSSI = -55;
402
403    /**
404     * Auto settings in the driver. The driver could choose to operate on both
405     * 2.4 GHz and 5 GHz or make a dynamic decision on selecting the band.
406     * @hide
407     */
408    public static final int WIFI_FREQUENCY_BAND_AUTO = 0;
409
410    /**
411     * Operation on 5 GHz alone
412     * @hide
413     */
414    public static final int WIFI_FREQUENCY_BAND_5GHZ = 1;
415
416    /**
417     * Operation on 2.4 GHz alone
418     * @hide
419     */
420    public static final int WIFI_FREQUENCY_BAND_2GHZ = 2;
421
422    /** List of asyncronous notifications
423     * @hide
424     */
425    public static final int DATA_ACTIVITY_NOTIFICATION = 1;
426
427    //Lowest bit indicates data reception and the second lowest
428    //bit indicates data transmitted
429    /** @hide */
430    public static final int DATA_ACTIVITY_NONE         = 0x00;
431    /** @hide */
432    public static final int DATA_ACTIVITY_IN           = 0x01;
433    /** @hide */
434    public static final int DATA_ACTIVITY_OUT          = 0x02;
435    /** @hide */
436    public static final int DATA_ACTIVITY_INOUT        = 0x03;
437
438    IWifiManager mService;
439    Handler mHandler;
440
441    /* Maximum number of active locks we allow.
442     * This limit was added to prevent apps from creating a ridiculous number
443     * of locks and crashing the system by overflowing the global ref table.
444     */
445    private static final int MAX_ACTIVE_LOCKS = 50;
446
447    /* Number of currently active WifiLocks and MulticastLocks */
448    private int mActiveLockCount;
449
450    /**
451     * Create a new WifiManager instance.
452     * Applications will almost always want to use
453     * {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve
454     * the standard {@link android.content.Context#WIFI_SERVICE Context.WIFI_SERVICE}.
455     * @param service the Binder interface
456     * @param handler target for messages
457     * @hide - hide this because it takes in a parameter of type IWifiManager, which
458     * is a system private class.
459     */
460    public WifiManager(IWifiManager service, Handler handler) {
461        mService = service;
462        mHandler = handler;
463    }
464
465    /**
466     * Return a list of all the networks configured in the supplicant.
467     * Not all fields of WifiConfiguration are returned. Only the following
468     * fields are filled in:
469     * <ul>
470     * <li>networkId</li>
471     * <li>SSID</li>
472     * <li>BSSID</li>
473     * <li>priority</li>
474     * <li>allowedProtocols</li>
475     * <li>allowedKeyManagement</li>
476     * <li>allowedAuthAlgorithms</li>
477     * <li>allowedPairwiseCiphers</li>
478     * <li>allowedGroupCiphers</li>
479     * </ul>
480     * @return a list of network configurations in the form of a list
481     * of {@link WifiConfiguration} objects.
482     */
483    public List<WifiConfiguration> getConfiguredNetworks() {
484        try {
485            return mService.getConfiguredNetworks();
486        } catch (RemoteException e) {
487            return null;
488        }
489    }
490
491    /**
492     * Add a new network description to the set of configured networks.
493     * The {@code networkId} field of the supplied configuration object
494     * is ignored.
495     * <p/>
496     * The new network will be marked DISABLED by default. To enable it,
497     * called {@link #enableNetwork}.
498     *
499     * @param config the set of variables that describe the configuration,
500     *            contained in a {@link WifiConfiguration} object.
501     * @return the ID of the newly created network description. This is used in
502     *         other operations to specified the network to be acted upon.
503     *         Returns {@code -1} on failure.
504     */
505    public int addNetwork(WifiConfiguration config) {
506        if (config == null) {
507            return -1;
508        }
509        config.networkId = -1;
510        return addOrUpdateNetwork(config);
511    }
512
513    /**
514     * Update the network description of an existing configured network.
515     *
516     * @param config the set of variables that describe the configuration,
517     *            contained in a {@link WifiConfiguration} object. It may
518     *            be sparse, so that only the items that are being changed
519     *            are non-<code>null</code>. The {@code networkId} field
520     *            must be set to the ID of the existing network being updated.
521     * @return Returns the {@code networkId} of the supplied
522     *         {@code WifiConfiguration} on success.
523     *         <br/>
524     *         Returns {@code -1} on failure, including when the {@code networkId}
525     *         field of the {@code WifiConfiguration} does not refer to an
526     *         existing network.
527     */
528    public int updateNetwork(WifiConfiguration config) {
529        if (config == null || config.networkId < 0) {
530            return -1;
531        }
532        return addOrUpdateNetwork(config);
533    }
534
535    /**
536     * Internal method for doing the RPC that creates a new network description
537     * or updates an existing one.
538     *
539     * @param config The possibly sparse object containing the variables that
540     *         are to set or updated in the network description.
541     * @return the ID of the network on success, {@code -1} on failure.
542     */
543    private int addOrUpdateNetwork(WifiConfiguration config) {
544        try {
545            return mService.addOrUpdateNetwork(config);
546        } catch (RemoteException e) {
547            return -1;
548        }
549    }
550
551    /**
552     * Remove the specified network from the list of configured networks.
553     * This may result in the asynchronous delivery of state change
554     * events.
555     * @param netId the integer that identifies the network configuration
556     * to the supplicant
557     * @return {@code true} if the operation succeeded
558     */
559    public boolean removeNetwork(int netId) {
560        try {
561            return mService.removeNetwork(netId);
562        } catch (RemoteException e) {
563            return false;
564        }
565    }
566
567    /**
568     * Allow a previously configured network to be associated with. If
569     * <code>disableOthers</code> is true, then all other configured
570     * networks are disabled, and an attempt to connect to the selected
571     * network is initiated. This may result in the asynchronous delivery
572     * of state change events.
573     * @param netId the ID of the network in the list of configured networks
574     * @param disableOthers if true, disable all other networks. The way to
575     * select a particular network to connect to is specify {@code true}
576     * for this parameter.
577     * @return {@code true} if the operation succeeded
578     */
579    public boolean enableNetwork(int netId, boolean disableOthers) {
580        try {
581            return mService.enableNetwork(netId, disableOthers);
582        } catch (RemoteException e) {
583            return false;
584        }
585    }
586
587    /**
588     * Disable a configured network. The specified network will not be
589     * a candidate for associating. This may result in the asynchronous
590     * delivery of state change events.
591     * @param netId the ID of the network as returned by {@link #addNetwork}.
592     * @return {@code true} if the operation succeeded
593     */
594    public boolean disableNetwork(int netId) {
595        try {
596            return mService.disableNetwork(netId);
597        } catch (RemoteException e) {
598            return false;
599        }
600    }
601
602    /**
603     * Disassociate from the currently active access point. This may result
604     * in the asynchronous delivery of state change events.
605     * @return {@code true} if the operation succeeded
606     */
607    public boolean disconnect() {
608        try {
609            mService.disconnect();
610            return true;
611        } catch (RemoteException e) {
612            return false;
613        }
614    }
615
616    /**
617     * Reconnect to the currently active access point, if we are currently
618     * disconnected. This may result in the asynchronous delivery of state
619     * change events.
620     * @return {@code true} if the operation succeeded
621     */
622    public boolean reconnect() {
623        try {
624            mService.reconnect();
625            return true;
626        } catch (RemoteException e) {
627            return false;
628        }
629    }
630
631    /**
632     * Reconnect to the currently active access point, even if we are already
633     * connected. This may result in the asynchronous delivery of state
634     * change events.
635     * @return {@code true} if the operation succeeded
636     */
637    public boolean reassociate() {
638        try {
639            mService.reassociate();
640            return true;
641        } catch (RemoteException e) {
642            return false;
643        }
644    }
645
646    /**
647     * Check that the supplicant daemon is responding to requests.
648     * @return {@code true} if we were able to communicate with the supplicant and
649     * it returned the expected response to the PING message.
650     */
651    public boolean pingSupplicant() {
652        if (mService == null)
653            return false;
654        try {
655            return mService.pingSupplicant();
656        } catch (RemoteException e) {
657            return false;
658        }
659    }
660
661    /**
662     * Request a scan for access points. Returns immediately. The availability
663     * of the results is made known later by means of an asynchronous event sent
664     * on completion of the scan.
665     * @return {@code true} if the operation succeeded, i.e., the scan was initiated
666     */
667    public boolean startScan() {
668        try {
669            mService.startScan(false);
670            return true;
671        } catch (RemoteException e) {
672            return false;
673        }
674    }
675
676    /**
677     * Request a scan for access points. Returns immediately. The availability
678     * of the results is made known later by means of an asynchronous event sent
679     * on completion of the scan.
680     * This is a variant of startScan that forces an active scan, even if passive
681     * scans are the current default
682     * @return {@code true} if the operation succeeded, i.e., the scan was initiated
683     *
684     * @hide
685     */
686    public boolean startScanActive() {
687        try {
688            mService.startScan(true);
689            return true;
690        } catch (RemoteException e) {
691            return false;
692        }
693    }
694
695    /**
696     * Return dynamic information about the current Wi-Fi connection, if any is active.
697     * @return the Wi-Fi information, contained in {@link WifiInfo}.
698     */
699    public WifiInfo getConnectionInfo() {
700        try {
701            return mService.getConnectionInfo();
702        } catch (RemoteException e) {
703            return null;
704        }
705    }
706
707    /**
708     * Return the results of the latest access point scan.
709     * @return the list of access points found in the most recent scan.
710     */
711    public List<ScanResult> getScanResults() {
712        try {
713            return mService.getScanResults();
714        } catch (RemoteException e) {
715            return null;
716        }
717    }
718
719    /**
720     * Tell the supplicant to persist the current list of configured networks.
721     * <p>
722     * Note: It is possible for this method to change the network IDs of
723     * existing networks. You should assume the network IDs can be different
724     * after calling this method.
725     *
726     * @return {@code true} if the operation succeeded
727     */
728    public boolean saveConfiguration() {
729        try {
730            return mService.saveConfiguration();
731        } catch (RemoteException e) {
732            return false;
733        }
734    }
735
736    /**
737     * Set the country code.
738     * @param countryCode country code in ISO 3166 format.
739     * @param persist {@code true} if this needs to be remembered
740     *
741     * @hide
742     */
743    public void setCountryCode(String country, boolean persist) {
744        try {
745            mService.setCountryCode(country, persist);
746        } catch (RemoteException e) { }
747    }
748
749    /**
750     * Set the operational frequency band.
751     * @param band  One of
752     *     {@link #WIFI_FREQUENCY_BAND_AUTO},
753     *     {@link #WIFI_FREQUENCY_BAND_5GHZ},
754     *     {@link #WIFI_FREQUENCY_BAND_2GHZ},
755     * @param persist {@code true} if this needs to be remembered
756     * @hide
757     */
758    public void setFrequencyBand(int band, boolean persist) {
759        try {
760            mService.setFrequencyBand(band, persist);
761        } catch (RemoteException e) { }
762    }
763
764    /**
765     * Get the operational frequency band.
766     * @return One of
767     *     {@link #WIFI_FREQUENCY_BAND_AUTO},
768     *     {@link #WIFI_FREQUENCY_BAND_5GHZ},
769     *     {@link #WIFI_FREQUENCY_BAND_2GHZ} or
770     *     {@code -1} on failure.
771     * @hide
772     */
773    public int getFrequencyBand() {
774        try {
775            return mService.getFrequencyBand();
776        } catch (RemoteException e) {
777            return -1;
778        }
779    }
780
781    /**
782     * Check if the chipset supports dual frequency band (2.4 GHz and 5 GHz)
783     * @return {@code true} if supported, {@code false} otherwise.
784     * @hide
785     */
786    public boolean isDualBandSupported() {
787        try {
788            return mService.isDualBandSupported();
789        } catch (RemoteException e) {
790            return false;
791        }
792    }
793
794    /**
795     * Return the DHCP-assigned addresses from the last successful DHCP request,
796     * if any.
797     * @return the DHCP information
798     */
799    public DhcpInfo getDhcpInfo() {
800        try {
801            return mService.getDhcpInfo();
802        } catch (RemoteException e) {
803            return null;
804        }
805    }
806
807
808    /**
809     * Enable or disable Wi-Fi.
810     * @param enabled {@code true} to enable, {@code false} to disable.
811     * @return {@code true} if the operation succeeds (or if the existing state
812     *         is the same as the requested state).
813     */
814    public boolean setWifiEnabled(boolean enabled) {
815        try {
816            return mService.setWifiEnabled(enabled);
817        } catch (RemoteException e) {
818            return false;
819        }
820    }
821
822    /**
823     * Gets the Wi-Fi enabled state.
824     * @return One of {@link #WIFI_STATE_DISABLED},
825     *         {@link #WIFI_STATE_DISABLING}, {@link #WIFI_STATE_ENABLED},
826     *         {@link #WIFI_STATE_ENABLING}, {@link #WIFI_STATE_UNKNOWN}
827     * @see #isWifiEnabled()
828     */
829    public int getWifiState() {
830        try {
831            return mService.getWifiEnabledState();
832        } catch (RemoteException e) {
833            return WIFI_STATE_UNKNOWN;
834        }
835    }
836
837    /**
838     * Return whether Wi-Fi is enabled or disabled.
839     * @return {@code true} if Wi-Fi is enabled
840     * @see #getWifiState()
841     */
842    public boolean isWifiEnabled() {
843        return getWifiState() == WIFI_STATE_ENABLED;
844    }
845
846    /**
847     * Calculates the level of the signal. This should be used any time a signal
848     * is being shown.
849     *
850     * @param rssi The power of the signal measured in RSSI.
851     * @param numLevels The number of levels to consider in the calculated
852     *            level.
853     * @return A level of the signal, given in the range of 0 to numLevels-1
854     *         (both inclusive).
855     */
856    public static int calculateSignalLevel(int rssi, int numLevels) {
857        if (rssi <= MIN_RSSI) {
858            return 0;
859        } else if (rssi >= MAX_RSSI) {
860            return numLevels - 1;
861        } else {
862            float inputRange = (MAX_RSSI - MIN_RSSI);
863            float outputRange = (numLevels - 1);
864            return (int)((float)(rssi - MIN_RSSI) * outputRange / inputRange);
865        }
866    }
867
868    /**
869     * Compares two signal strengths.
870     *
871     * @param rssiA The power of the first signal measured in RSSI.
872     * @param rssiB The power of the second signal measured in RSSI.
873     * @return Returns <0 if the first signal is weaker than the second signal,
874     *         0 if the two signals have the same strength, and >0 if the first
875     *         signal is stronger than the second signal.
876     */
877    public static int compareSignalLevel(int rssiA, int rssiB) {
878        return rssiA - rssiB;
879    }
880
881    /**
882     * Start AccessPoint mode with the specified
883     * configuration. If the radio is already running in
884     * AP mode, update the new configuration
885     * Note that starting in access point mode disables station
886     * mode operation
887     * @param wifiConfig SSID, security and channel details as
888     *        part of WifiConfiguration
889     * @return {@code true} if the operation succeeds, {@code false} otherwise
890     *
891     * @hide Dont open up yet
892     */
893    public boolean setWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled) {
894        try {
895            return mService.setWifiApEnabled(wifiConfig, enabled);
896        } catch (RemoteException e) {
897            return false;
898        }
899    }
900
901    /**
902     * Gets the Wi-Fi enabled state.
903     * @return One of {@link #WIFI_AP_STATE_DISABLED},
904     *         {@link #WIFI_AP_STATE_DISABLING}, {@link #WIFI_AP_STATE_ENABLED},
905     *         {@link #WIFI_AP_STATE_ENABLING}, {@link #WIFI_AP_STATE_FAILED}
906     * @see #isWifiApEnabled()
907     *
908     * @hide Dont open yet
909     */
910    public int getWifiApState() {
911        try {
912            return mService.getWifiApEnabledState();
913        } catch (RemoteException e) {
914            return WIFI_AP_STATE_FAILED;
915        }
916    }
917
918    /**
919     * Return whether Wi-Fi AP is enabled or disabled.
920     * @return {@code true} if Wi-Fi AP is enabled
921     * @see #getWifiApState()
922     *
923     * @hide Dont open yet
924     */
925    public boolean isWifiApEnabled() {
926        return getWifiApState() == WIFI_AP_STATE_ENABLED;
927    }
928
929    /**
930     * Gets the Wi-Fi AP Configuration.
931     * @return AP details in WifiConfiguration
932     *
933     * @hide Dont open yet
934     */
935    public WifiConfiguration getWifiApConfiguration() {
936        try {
937            return mService.getWifiApConfiguration();
938        } catch (RemoteException e) {
939            return null;
940        }
941    }
942
943    /**
944     * Sets the Wi-Fi AP Configuration.
945     * @return {@code true} if the operation succeeded, {@code false} otherwise
946     *
947     * @hide Dont open yet
948     */
949    public boolean setWifiApConfiguration(WifiConfiguration wifiConfig) {
950        try {
951            mService.setWifiApConfiguration(wifiConfig);
952            return true;
953        } catch (RemoteException e) {
954            return false;
955        }
956    }
957
958   /**
959     * Start the driver and connect to network.
960     *
961     * This function will over-ride WifiLock and device idle status. For example,
962     * even if the device is idle or there is only a scan-only lock held,
963     * a start wifi would mean that wifi connection is kept active until
964     * a stopWifi() is sent.
965     *
966     * This API is used by WifiStateTracker
967     *
968     * @return {@code true} if the operation succeeds else {@code false}
969     * @hide
970     */
971    public boolean startWifi() {
972        try {
973            mService.startWifi();
974            return true;
975        } catch (RemoteException e) {
976            return false;
977        }
978    }
979
980    /**
981     * Disconnect from a network (if any) and stop the driver.
982     *
983     * This function will over-ride WifiLock and device idle status. Wi-Fi
984     * stays inactive until a startWifi() is issued.
985     *
986     * This API is used by WifiStateTracker
987     *
988     * @return {@code true} if the operation succeeds else {@code false}
989     * @hide
990     */
991    public boolean stopWifi() {
992        try {
993            mService.stopWifi();
994            return true;
995        } catch (RemoteException e) {
996            return false;
997        }
998    }
999
1000    /**
1001     * Add a bssid to the supplicant blacklist
1002     *
1003     * This API is used by WifiWatchdogService
1004     *
1005     * @return {@code true} if the operation succeeds else {@code false}
1006     * @hide
1007     */
1008    public boolean addToBlacklist(String bssid) {
1009        try {
1010            mService.addToBlacklist(bssid);
1011            return true;
1012        } catch (RemoteException e) {
1013            return false;
1014        }
1015    }
1016
1017    /**
1018     * Clear the supplicant blacklist
1019     *
1020     * This API is used by WifiWatchdogService
1021     *
1022     * @return {@code true} if the operation succeeds else {@code false}
1023     * @hide
1024     */
1025    public boolean clearBlacklist() {
1026        try {
1027            mService.clearBlacklist();
1028            return true;
1029        } catch (RemoteException e) {
1030            return false;
1031        }
1032    }
1033
1034    /* TODO: deprecate synchronous API and open up the following API */
1035    /**
1036     * Connect to a network with the given configuration. The network also
1037     * gets added to the supplicant configuration.
1038     *
1039     * For a new network, this function is used instead of a
1040     * sequence of addNetwork(), enableNetwork(), saveConfiguration() and
1041     * reconnect()
1042     *
1043     * @param config the set of variables that describe the configuration,
1044     *            contained in a {@link WifiConfiguration} object.
1045     * @hide
1046     */
1047    public void connectNetwork(WifiConfiguration config) {
1048        if (config == null) {
1049            return;
1050        }
1051        try {
1052            mService.connectNetworkWithConfig(config);
1053        } catch (RemoteException e) { }
1054    }
1055
1056    /**
1057     * Connect to a network with the given networkId.
1058     *
1059     * This function is used instead of a enableNetwork(), saveConfiguration() and
1060     * reconnect()
1061     *
1062     * @param networkId the network id identifiying the network in the
1063     *                supplicant configuration list
1064     * @hide
1065     */
1066    public void connectNetwork(int networkId) {
1067        if (networkId < 0) {
1068            return;
1069        }
1070        try {
1071            mService.connectNetworkWithId(networkId);
1072        } catch (RemoteException e) { }
1073    }
1074
1075    /**
1076     * Save the given network in the supplicant config. If the network already
1077     * exists, the configuration is updated. A new network is enabled
1078     * by default.
1079     *
1080     * For a new network, this function is used instead of a
1081     * sequence of addNetwork(), enableNetwork() and saveConfiguration().
1082     *
1083     * For an existing network, it accomplishes the task of updateNetwork()
1084     * and saveConfiguration()
1085     *
1086     * @param config the set of variables that describe the configuration,
1087     *            contained in a {@link WifiConfiguration} object.
1088     * @hide
1089     */
1090    public void saveNetwork(WifiConfiguration config) {
1091        if (config == null) {
1092            return;
1093        }
1094        try {
1095            mService.saveNetwork(config);
1096        } catch (RemoteException e) { }
1097    }
1098
1099    /**
1100     * Delete the network in the supplicant config.
1101     *
1102     * This function is used instead of a sequence of removeNetwork()
1103     * and saveConfiguration().
1104     *
1105     * @param config the set of variables that describe the configuration,
1106     *            contained in a {@link WifiConfiguration} object.
1107     * @hide
1108     */
1109    public void forgetNetwork(int netId) {
1110        if (netId < 0) {
1111            return;
1112        }
1113        try {
1114            mService.forgetNetwork(netId);
1115        } catch (RemoteException e) { }
1116    }
1117
1118    /**
1119     * Start Wi-fi Protected Setup
1120     *
1121     * @param config WPS configuration
1122     * @return WpsResult containing pin and status
1123     * @hide
1124     * TODO: with use of AsyncChannel, return value should go away
1125     */
1126    public WpsResult startWps(WpsConfiguration config) {
1127        try {
1128            return mService.startWps(config);
1129        } catch (RemoteException e) {
1130            return new WpsResult(WpsResult.Status.FAILURE);
1131        }
1132    }
1133
1134    /**
1135     * Get a reference to WifiService handler. This is used by a client to establish
1136     * an AsyncChannel communication with WifiService
1137     *
1138     * @return Messenger pointing to the WifiService handler
1139     * @hide
1140     */
1141    public Messenger getMessenger() {
1142        try {
1143            return mService.getMessenger();
1144        } catch (RemoteException e) {
1145            return null;
1146        }
1147    }
1148
1149    /**
1150     * Allows an application to keep the Wi-Fi radio awake.
1151     * Normally the Wi-Fi radio may turn off when the user has not used the device in a while.
1152     * Acquiring a WifiLock will keep the radio on until the lock is released.  Multiple
1153     * applications may hold WifiLocks, and the radio will only be allowed to turn off when no
1154     * WifiLocks are held in any application.
1155     *
1156     * Before using a WifiLock, consider carefully if your application requires Wi-Fi access, or
1157     * could function over a mobile network, if available.  A program that needs to download large
1158     * files should hold a WifiLock to ensure that the download will complete, but a program whose
1159     * network usage is occasional or low-bandwidth should not hold a WifiLock to avoid adversely
1160     * affecting battery life.
1161     *
1162     * Note that WifiLocks cannot override the user-level "Wi-Fi Enabled" setting, nor Airplane
1163     * Mode.  They simply keep the radio from turning off when Wi-Fi is already on but the device
1164     * is idle.
1165     */
1166    public class WifiLock {
1167        private String mTag;
1168        private final IBinder mBinder;
1169        private int mRefCount;
1170        int mLockType;
1171        private boolean mRefCounted;
1172        private boolean mHeld;
1173        private WorkSource mWorkSource;
1174
1175        private WifiLock(int lockType, String tag) {
1176            mTag = tag;
1177            mLockType = lockType;
1178            mBinder = new Binder();
1179            mRefCount = 0;
1180            mRefCounted = true;
1181            mHeld = false;
1182        }
1183
1184        /**
1185         * Locks the Wi-Fi radio on until {@link #release} is called.
1186         *
1187         * If this WifiLock is reference-counted, each call to {@code acquire} will increment the
1188         * reference count, and the radio will remain locked as long as the reference count is
1189         * above zero.
1190         *
1191         * If this WifiLock is not reference-counted, the first call to {@code acquire} will lock
1192         * the radio, but subsequent calls will be ignored.  Only one call to {@link #release}
1193         * will be required, regardless of the number of times that {@code acquire} is called.
1194         */
1195        public void acquire() {
1196            synchronized (mBinder) {
1197                if (mRefCounted ? (++mRefCount > 0) : (!mHeld)) {
1198                    try {
1199                        mService.acquireWifiLock(mBinder, mLockType, mTag, mWorkSource);
1200                        synchronized (WifiManager.this) {
1201                            if (mActiveLockCount >= MAX_ACTIVE_LOCKS) {
1202                                mService.releaseWifiLock(mBinder);
1203                                throw new UnsupportedOperationException(
1204                                            "Exceeded maximum number of wifi locks");
1205                            }
1206                            mActiveLockCount++;
1207                        }
1208                    } catch (RemoteException ignore) {
1209                    }
1210                    mHeld = true;
1211                }
1212            }
1213        }
1214
1215        /**
1216         * Unlocks the Wi-Fi radio, allowing it to turn off when the device is idle.
1217         *
1218         * If this WifiLock is reference-counted, each call to {@code release} will decrement the
1219         * reference count, and the radio will be unlocked only when the reference count reaches
1220         * zero.  If the reference count goes below zero (that is, if {@code release} is called
1221         * a greater number of times than {@link #acquire}), an exception is thrown.
1222         *
1223         * If this WifiLock is not reference-counted, the first call to {@code release} (after
1224         * the radio was locked using {@link #acquire}) will unlock the radio, and subsequent
1225         * calls will be ignored.
1226         */
1227        public void release() {
1228            synchronized (mBinder) {
1229                if (mRefCounted ? (--mRefCount == 0) : (mHeld)) {
1230                    try {
1231                        mService.releaseWifiLock(mBinder);
1232                        synchronized (WifiManager.this) {
1233                            mActiveLockCount--;
1234                        }
1235                    } catch (RemoteException ignore) {
1236                    }
1237                    mHeld = false;
1238                }
1239                if (mRefCount < 0) {
1240                    throw new RuntimeException("WifiLock under-locked " + mTag);
1241                }
1242            }
1243        }
1244
1245        /**
1246         * Controls whether this is a reference-counted or non-reference-counted WifiLock.
1247         *
1248         * Reference-counted WifiLocks keep track of the number of calls to {@link #acquire} and
1249         * {@link #release}, and only allow the radio to sleep when every call to {@link #acquire}
1250         * has been balanced with a call to {@link #release}.  Non-reference-counted WifiLocks
1251         * lock the radio whenever {@link #acquire} is called and it is unlocked, and unlock the
1252         * radio whenever {@link #release} is called and it is locked.
1253         *
1254         * @param refCounted true if this WifiLock should keep a reference count
1255         */
1256        public void setReferenceCounted(boolean refCounted) {
1257            mRefCounted = refCounted;
1258        }
1259
1260        /**
1261         * Checks whether this WifiLock is currently held.
1262         *
1263         * @return true if this WifiLock is held, false otherwise
1264         */
1265        public boolean isHeld() {
1266            synchronized (mBinder) {
1267                return mHeld;
1268            }
1269        }
1270
1271        public void setWorkSource(WorkSource ws) {
1272            synchronized (mBinder) {
1273                if (ws != null && ws.size() == 0) {
1274                    ws = null;
1275                }
1276                boolean changed = true;
1277                if (ws == null) {
1278                    mWorkSource = null;
1279                } else if (mWorkSource == null) {
1280                    changed = mWorkSource != null;
1281                    mWorkSource = new WorkSource(ws);
1282                } else {
1283                    changed = mWorkSource.diff(ws);
1284                    if (changed) {
1285                        mWorkSource.set(ws);
1286                    }
1287                }
1288                if (changed && mHeld) {
1289                    try {
1290                        mService.updateWifiLockWorkSource(mBinder, mWorkSource);
1291                    } catch (RemoteException e) {
1292                    }
1293                }
1294            }
1295        }
1296
1297        public String toString() {
1298            String s1, s2, s3;
1299            synchronized (mBinder) {
1300                s1 = Integer.toHexString(System.identityHashCode(this));
1301                s2 = mHeld ? "held; " : "";
1302                if (mRefCounted) {
1303                    s3 = "refcounted: refcount = " + mRefCount;
1304                } else {
1305                    s3 = "not refcounted";
1306                }
1307                return "WifiLock{ " + s1 + "; " + s2 + s3 + " }";
1308            }
1309        }
1310
1311        @Override
1312        protected void finalize() throws Throwable {
1313            super.finalize();
1314            synchronized (mBinder) {
1315                if (mHeld) {
1316                    try {
1317                        mService.releaseWifiLock(mBinder);
1318                        synchronized (WifiManager.this) {
1319                            mActiveLockCount--;
1320                        }
1321                    } catch (RemoteException ignore) {
1322                    }
1323                }
1324            }
1325        }
1326    }
1327
1328    /**
1329     * Creates a new WifiLock.
1330     *
1331     * @param lockType the type of lock to create. See {@link #WIFI_MODE_FULL},
1332     * {@link #WIFI_MODE_FULL_HIGH_PERF} and {@link #WIFI_MODE_SCAN_ONLY} for
1333     * descriptions of the types of Wi-Fi locks.
1334     * @param tag a tag for the WifiLock to identify it in debugging messages.  This string is
1335     *            never shown to the user under normal conditions, but should be descriptive
1336     *            enough to identify your application and the specific WifiLock within it, if it
1337     *            holds multiple WifiLocks.
1338     *
1339     * @return a new, unacquired WifiLock with the given tag.
1340     *
1341     * @see WifiLock
1342     */
1343    public WifiLock createWifiLock(int lockType, String tag) {
1344        return new WifiLock(lockType, tag);
1345    }
1346
1347    /**
1348     * Creates a new WifiLock.
1349     *
1350     * @param tag a tag for the WifiLock to identify it in debugging messages.  This string is
1351     *            never shown to the user under normal conditions, but should be descriptive
1352     *            enough to identify your application and the specific WifiLock within it, if it
1353     *            holds multiple WifiLocks.
1354     *
1355     * @return a new, unacquired WifiLock with the given tag.
1356     *
1357     * @see WifiLock
1358     */
1359    public WifiLock createWifiLock(String tag) {
1360        return new WifiLock(WIFI_MODE_FULL, tag);
1361    }
1362
1363
1364    /**
1365     * Create a new MulticastLock
1366     *
1367     * @param tag a tag for the MulticastLock to identify it in debugging
1368     *            messages.  This string is never shown to the user under
1369     *            normal conditions, but should be descriptive enough to
1370     *            identify your application and the specific MulticastLock
1371     *            within it, if it holds multiple MulticastLocks.
1372     *
1373     * @return a new, unacquired MulticastLock with the given tag.
1374     *
1375     * @see MulticastLock
1376     */
1377    public MulticastLock createMulticastLock(String tag) {
1378        return new MulticastLock(tag);
1379    }
1380
1381    /**
1382     * Allows an application to receive Wifi Multicast packets.
1383     * Normally the Wifi stack filters out packets not explicitly
1384     * addressed to this device.  Acquring a MulticastLock will
1385     * cause the stack to receive packets addressed to multicast
1386     * addresses.  Processing these extra packets can cause a noticable
1387     * battery drain and should be disabled when not needed.
1388     */
1389    public class MulticastLock {
1390        private String mTag;
1391        private final IBinder mBinder;
1392        private int mRefCount;
1393        private boolean mRefCounted;
1394        private boolean mHeld;
1395
1396        private MulticastLock(String tag) {
1397            mTag = tag;
1398            mBinder = new Binder();
1399            mRefCount = 0;
1400            mRefCounted = true;
1401            mHeld = false;
1402        }
1403
1404        /**
1405         * Locks Wifi Multicast on until {@link #release} is called.
1406         *
1407         * If this MulticastLock is reference-counted each call to
1408         * {@code acquire} will increment the reference count, and the
1409         * wifi interface will receive multicast packets as long as the
1410         * reference count is above zero.
1411         *
1412         * If this MulticastLock is not reference-counted, the first call to
1413         * {@code acquire} will turn on the multicast packets, but subsequent
1414         * calls will be ignored.  Only one call to {@link #release} will
1415         * be required, regardless of the number of times that {@code acquire}
1416         * is called.
1417         *
1418         * Note that other applications may also lock Wifi Multicast on.
1419         * Only they can relinquish their lock.
1420         *
1421         * Also note that applications cannot leave Multicast locked on.
1422         * When an app exits or crashes, any Multicast locks will be released.
1423         */
1424        public void acquire() {
1425            synchronized (mBinder) {
1426                if (mRefCounted ? (++mRefCount > 0) : (!mHeld)) {
1427                    try {
1428                        mService.acquireMulticastLock(mBinder, mTag);
1429                        synchronized (WifiManager.this) {
1430                            if (mActiveLockCount >= MAX_ACTIVE_LOCKS) {
1431                                mService.releaseMulticastLock();
1432                                throw new UnsupportedOperationException(
1433                                        "Exceeded maximum number of wifi locks");
1434                            }
1435                            mActiveLockCount++;
1436                        }
1437                    } catch (RemoteException ignore) {
1438                    }
1439                    mHeld = true;
1440                }
1441            }
1442        }
1443
1444        /**
1445         * Unlocks Wifi Multicast, restoring the filter of packets
1446         * not addressed specifically to this device and saving power.
1447         *
1448         * If this MulticastLock is reference-counted, each call to
1449         * {@code release} will decrement the reference count, and the
1450         * multicast packets will only stop being received when the reference
1451         * count reaches zero.  If the reference count goes below zero (that
1452         * is, if {@code release} is called a greater number of times than
1453         * {@link #acquire}), an exception is thrown.
1454         *
1455         * If this MulticastLock is not reference-counted, the first call to
1456         * {@code release} (after the radio was multicast locked using
1457         * {@link #acquire}) will unlock the multicast, and subsequent calls
1458         * will be ignored.
1459         *
1460         * Note that if any other Wifi Multicast Locks are still outstanding
1461         * this {@code release} call will not have an immediate effect.  Only
1462         * when all applications have released all their Multicast Locks will
1463         * the Multicast filter be turned back on.
1464         *
1465         * Also note that when an app exits or crashes all of its Multicast
1466         * Locks will be automatically released.
1467         */
1468        public void release() {
1469            synchronized (mBinder) {
1470                if (mRefCounted ? (--mRefCount == 0) : (mHeld)) {
1471                    try {
1472                        mService.releaseMulticastLock();
1473                        synchronized (WifiManager.this) {
1474                            mActiveLockCount--;
1475                        }
1476                    } catch (RemoteException ignore) {
1477                    }
1478                    mHeld = false;
1479                }
1480                if (mRefCount < 0) {
1481                    throw new RuntimeException("MulticastLock under-locked "
1482                            + mTag);
1483                }
1484            }
1485        }
1486
1487        /**
1488         * Controls whether this is a reference-counted or non-reference-
1489         * counted MulticastLock.
1490         *
1491         * Reference-counted MulticastLocks keep track of the number of calls
1492         * to {@link #acquire} and {@link #release}, and only stop the
1493         * reception of multicast packets when every call to {@link #acquire}
1494         * has been balanced with a call to {@link #release}.  Non-reference-
1495         * counted MulticastLocks allow the reception of multicast packets
1496         * whenever {@link #acquire} is called and stop accepting multicast
1497         * packets whenever {@link #release} is called.
1498         *
1499         * @param refCounted true if this MulticastLock should keep a reference
1500         * count
1501         */
1502        public void setReferenceCounted(boolean refCounted) {
1503            mRefCounted = refCounted;
1504        }
1505
1506        /**
1507         * Checks whether this MulticastLock is currently held.
1508         *
1509         * @return true if this MulticastLock is held, false otherwise
1510         */
1511        public boolean isHeld() {
1512            synchronized (mBinder) {
1513                return mHeld;
1514            }
1515        }
1516
1517        public String toString() {
1518            String s1, s2, s3;
1519            synchronized (mBinder) {
1520                s1 = Integer.toHexString(System.identityHashCode(this));
1521                s2 = mHeld ? "held; " : "";
1522                if (mRefCounted) {
1523                    s3 = "refcounted: refcount = " + mRefCount;
1524                } else {
1525                    s3 = "not refcounted";
1526                }
1527                return "MulticastLock{ " + s1 + "; " + s2 + s3 + " }";
1528            }
1529        }
1530
1531        @Override
1532        protected void finalize() throws Throwable {
1533            super.finalize();
1534            setReferenceCounted(false);
1535            release();
1536        }
1537    }
1538
1539    /**
1540     * Check multicast filter status.
1541     *
1542     * @return true if multicast packets are allowed.
1543     *
1544     * @hide pending API council approval
1545     */
1546    public boolean isMulticastEnabled() {
1547        try {
1548            return mService.isMulticastEnabled();
1549        } catch (RemoteException e) {
1550            return false;
1551        }
1552    }
1553
1554    /**
1555     * Initialize the multicast filtering to 'on'
1556     * @hide no intent to publish
1557     */
1558    public boolean initializeMulticastFiltering() {
1559        try {
1560            mService.initializeMulticastFiltering();
1561            return true;
1562        } catch (RemoteException e) {
1563             return false;
1564        }
1565    }
1566}
1567