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