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