WifiManager.java revision 3550ac919c492f3fbaebbf868f4df0d1ea7ee784
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     * Disassociate from the currently active access point. This may result
619     * in the asynchronous delivery of state change events.
620     * @return {@code true} if the operation succeeded
621     */
622    public boolean disconnect() {
623        try {
624            mService.disconnect();
625            return true;
626        } catch (RemoteException e) {
627            return false;
628        }
629    }
630
631    /**
632     * Reconnect to the currently active access point, if we are currently
633     * disconnected. This may result in the asynchronous delivery of state
634     * change events.
635     * @return {@code true} if the operation succeeded
636     */
637    public boolean reconnect() {
638        try {
639            mService.reconnect();
640            return true;
641        } catch (RemoteException e) {
642            return false;
643        }
644    }
645
646    /**
647     * Reconnect to the currently active access point, even if we are already
648     * connected. This may result in the asynchronous delivery of state
649     * change events.
650     * @return {@code true} if the operation succeeded
651     */
652    public boolean reassociate() {
653        try {
654            mService.reassociate();
655            return true;
656        } catch (RemoteException e) {
657            return false;
658        }
659    }
660
661    /**
662     * Check that the supplicant daemon is responding to requests.
663     * @return {@code true} if we were able to communicate with the supplicant and
664     * it returned the expected response to the PING message.
665     */
666    public boolean pingSupplicant() {
667        if (mService == null)
668            return false;
669        try {
670            return mService.pingSupplicant();
671        } catch (RemoteException e) {
672            return false;
673        }
674    }
675
676    /**
677     * Request a scan for access points. Returns immediately. The availability
678     * of the results is made known later by means of an asynchronous event sent
679     * on completion of the scan.
680     * @return {@code true} if the operation succeeded, i.e., the scan was initiated
681     */
682    public boolean startScan() {
683        try {
684            mService.startScan(false);
685            return true;
686        } catch (RemoteException e) {
687            return false;
688        }
689    }
690
691    /**
692     * Request a scan for access points. Returns immediately. The availability
693     * of the results is made known later by means of an asynchronous event sent
694     * on completion of the scan.
695     * This is a variant of startScan that forces an active scan, even if passive
696     * scans are the current default
697     * @return {@code true} if the operation succeeded, i.e., the scan was initiated
698     *
699     * @hide
700     */
701    public boolean startScanActive() {
702        try {
703            mService.startScan(true);
704            return true;
705        } catch (RemoteException e) {
706            return false;
707        }
708    }
709
710    /**
711     * Return dynamic information about the current Wi-Fi connection, if any is active.
712     * @return the Wi-Fi information, contained in {@link WifiInfo}.
713     */
714    public WifiInfo getConnectionInfo() {
715        try {
716            return mService.getConnectionInfo();
717        } catch (RemoteException e) {
718            return null;
719        }
720    }
721
722    /**
723     * Return the results of the latest access point scan.
724     * @return the list of access points found in the most recent scan.
725     */
726    public List<ScanResult> getScanResults() {
727        try {
728            return mService.getScanResults();
729        } catch (RemoteException e) {
730            return null;
731        }
732    }
733
734    /**
735     * Tell the supplicant to persist the current list of configured networks.
736     * <p>
737     * Note: It is possible for this method to change the network IDs of
738     * existing networks. You should assume the network IDs can be different
739     * after calling this method.
740     *
741     * @return {@code true} if the operation succeeded
742     */
743    public boolean saveConfiguration() {
744        try {
745            return mService.saveConfiguration();
746        } catch (RemoteException e) {
747            return false;
748        }
749    }
750
751    /**
752     * Set the country code.
753     * @param countryCode country code in ISO 3166 format.
754     * @param persist {@code true} if this needs to be remembered
755     *
756     * @hide
757     */
758    public void setCountryCode(String country, boolean persist) {
759        try {
760            mService.setCountryCode(country, persist);
761        } catch (RemoteException e) { }
762    }
763
764    /**
765     * Set the operational frequency band.
766     * @param band  One of
767     *     {@link #WIFI_FREQUENCY_BAND_AUTO},
768     *     {@link #WIFI_FREQUENCY_BAND_5GHZ},
769     *     {@link #WIFI_FREQUENCY_BAND_2GHZ},
770     * @param persist {@code true} if this needs to be remembered
771     * @hide
772     */
773    public void setFrequencyBand(int band, boolean persist) {
774        try {
775            mService.setFrequencyBand(band, persist);
776        } catch (RemoteException e) { }
777    }
778
779    /**
780     * Get the operational frequency band.
781     * @return One of
782     *     {@link #WIFI_FREQUENCY_BAND_AUTO},
783     *     {@link #WIFI_FREQUENCY_BAND_5GHZ},
784     *     {@link #WIFI_FREQUENCY_BAND_2GHZ} or
785     *     {@code -1} on failure.
786     * @hide
787     */
788    public int getFrequencyBand() {
789        try {
790            return mService.getFrequencyBand();
791        } catch (RemoteException e) {
792            return -1;
793        }
794    }
795
796    /**
797     * Check if the chipset supports dual frequency band (2.4 GHz and 5 GHz)
798     * @return {@code true} if supported, {@code false} otherwise.
799     * @hide
800     */
801    public boolean isDualBandSupported() {
802        try {
803            return mService.isDualBandSupported();
804        } catch (RemoteException e) {
805            return false;
806        }
807    }
808
809    /**
810     * Return the DHCP-assigned addresses from the last successful DHCP request,
811     * if any.
812     * @return the DHCP information
813     */
814    public DhcpInfo getDhcpInfo() {
815        try {
816            return mService.getDhcpInfo();
817        } catch (RemoteException e) {
818            return null;
819        }
820    }
821
822
823    /**
824     * Enable or disable Wi-Fi.
825     * @param enabled {@code true} to enable, {@code false} to disable.
826     * @return {@code true} if the operation succeeds (or if the existing state
827     *         is the same as the requested state).
828     */
829    public boolean setWifiEnabled(boolean enabled) {
830        try {
831            return mService.setWifiEnabled(enabled);
832        } catch (RemoteException e) {
833            return false;
834        }
835    }
836
837    /**
838     * Gets the Wi-Fi enabled state.
839     * @return One of {@link #WIFI_STATE_DISABLED},
840     *         {@link #WIFI_STATE_DISABLING}, {@link #WIFI_STATE_ENABLED},
841     *         {@link #WIFI_STATE_ENABLING}, {@link #WIFI_STATE_UNKNOWN}
842     * @see #isWifiEnabled()
843     */
844    public int getWifiState() {
845        try {
846            return mService.getWifiEnabledState();
847        } catch (RemoteException e) {
848            return WIFI_STATE_UNKNOWN;
849        }
850    }
851
852    /**
853     * Return whether Wi-Fi is enabled or disabled.
854     * @return {@code true} if Wi-Fi is enabled
855     * @see #getWifiState()
856     */
857    public boolean isWifiEnabled() {
858        return getWifiState() == WIFI_STATE_ENABLED;
859    }
860
861    /**
862     * Calculates the level of the signal. This should be used any time a signal
863     * is being shown.
864     *
865     * @param rssi The power of the signal measured in RSSI.
866     * @param numLevels The number of levels to consider in the calculated
867     *            level.
868     * @return A level of the signal, given in the range of 0 to numLevels-1
869     *         (both inclusive).
870     */
871    public static int calculateSignalLevel(int rssi, int numLevels) {
872        if (rssi <= MIN_RSSI) {
873            return 0;
874        } else if (rssi >= MAX_RSSI) {
875            return numLevels - 1;
876        } else {
877            float inputRange = (MAX_RSSI - MIN_RSSI);
878            float outputRange = (numLevels - 1);
879            return (int)((float)(rssi - MIN_RSSI) * outputRange / inputRange);
880        }
881    }
882
883    /**
884     * Compares two signal strengths.
885     *
886     * @param rssiA The power of the first signal measured in RSSI.
887     * @param rssiB The power of the second signal measured in RSSI.
888     * @return Returns <0 if the first signal is weaker than the second signal,
889     *         0 if the two signals have the same strength, and >0 if the first
890     *         signal is stronger than the second signal.
891     */
892    public static int compareSignalLevel(int rssiA, int rssiB) {
893        return rssiA - rssiB;
894    }
895
896    /**
897     * Start AccessPoint mode with the specified
898     * configuration. If the radio is already running in
899     * AP mode, update the new configuration
900     * Note that starting in access point mode disables station
901     * mode operation
902     * @param wifiConfig SSID, security and channel details as
903     *        part of WifiConfiguration
904     * @return {@code true} if the operation succeeds, {@code false} otherwise
905     *
906     * @hide Dont open up yet
907     */
908    public boolean setWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled) {
909        try {
910            mService.setWifiApEnabled(wifiConfig, enabled);
911            return true;
912        } catch (RemoteException e) {
913            return false;
914        }
915    }
916
917    /**
918     * Gets the Wi-Fi enabled state.
919     * @return One of {@link #WIFI_AP_STATE_DISABLED},
920     *         {@link #WIFI_AP_STATE_DISABLING}, {@link #WIFI_AP_STATE_ENABLED},
921     *         {@link #WIFI_AP_STATE_ENABLING}, {@link #WIFI_AP_STATE_FAILED}
922     * @see #isWifiApEnabled()
923     *
924     * @hide Dont open yet
925     */
926    public int getWifiApState() {
927        try {
928            return mService.getWifiApEnabledState();
929        } catch (RemoteException e) {
930            return WIFI_AP_STATE_FAILED;
931        }
932    }
933
934    /**
935     * Return whether Wi-Fi AP is enabled or disabled.
936     * @return {@code true} if Wi-Fi AP is enabled
937     * @see #getWifiApState()
938     *
939     * @hide Dont open yet
940     */
941    public boolean isWifiApEnabled() {
942        return getWifiApState() == WIFI_AP_STATE_ENABLED;
943    }
944
945    /**
946     * Gets the Wi-Fi AP Configuration.
947     * @return AP details in WifiConfiguration
948     *
949     * @hide Dont open yet
950     */
951    public WifiConfiguration getWifiApConfiguration() {
952        try {
953            return mService.getWifiApConfiguration();
954        } catch (RemoteException e) {
955            return null;
956        }
957    }
958
959    /**
960     * Sets the Wi-Fi AP Configuration.
961     * @return {@code true} if the operation succeeded, {@code false} otherwise
962     *
963     * @hide Dont open yet
964     */
965    public boolean setWifiApConfiguration(WifiConfiguration wifiConfig) {
966        try {
967            mService.setWifiApConfiguration(wifiConfig);
968            return true;
969        } catch (RemoteException e) {
970            return false;
971        }
972    }
973
974   /**
975     * Start the driver and connect to network.
976     *
977     * This function will over-ride WifiLock and device idle status. For example,
978     * even if the device is idle or there is only a scan-only lock held,
979     * a start wifi would mean that wifi connection is kept active until
980     * a stopWifi() is sent.
981     *
982     * This API is used by WifiStateTracker
983     *
984     * @return {@code true} if the operation succeeds else {@code false}
985     * @hide
986     */
987    public boolean startWifi() {
988        try {
989            mService.startWifi();
990            return true;
991        } catch (RemoteException e) {
992            return false;
993        }
994    }
995
996    /**
997     * Disconnect from a network (if any) and stop the driver.
998     *
999     * This function will over-ride WifiLock and device idle status. Wi-Fi
1000     * stays inactive until a startWifi() is issued.
1001     *
1002     * This API is used by WifiStateTracker
1003     *
1004     * @return {@code true} if the operation succeeds else {@code false}
1005     * @hide
1006     */
1007    public boolean stopWifi() {
1008        try {
1009            mService.stopWifi();
1010            return true;
1011        } catch (RemoteException e) {
1012            return false;
1013        }
1014    }
1015
1016    /**
1017     * Add a bssid to the supplicant blacklist
1018     *
1019     * This API is used by WifiWatchdogService
1020     *
1021     * @return {@code true} if the operation succeeds else {@code false}
1022     * @hide
1023     */
1024    public boolean addToBlacklist(String bssid) {
1025        try {
1026            mService.addToBlacklist(bssid);
1027            return true;
1028        } catch (RemoteException e) {
1029            return false;
1030        }
1031    }
1032
1033    /**
1034     * Clear the supplicant blacklist
1035     *
1036     * This API is used by WifiWatchdogService
1037     *
1038     * @return {@code true} if the operation succeeds else {@code false}
1039     * @hide
1040     */
1041    public boolean clearBlacklist() {
1042        try {
1043            mService.clearBlacklist();
1044            return true;
1045        } catch (RemoteException e) {
1046            return false;
1047        }
1048    }
1049
1050    /* TODO: deprecate synchronous API and open up the following API */
1051
1052    /* Commands to WifiService */
1053    /** @hide */
1054    public static final int CMD_CONNECT_NETWORK             = 1;
1055    /** @hide */
1056    public static final int CMD_FORGET_NETWORK              = 2;
1057    /** @hide */
1058    public static final int CMD_SAVE_NETWORK                = 3;
1059    /** @hide */
1060    public static final int CMD_START_WPS                   = 4;
1061
1062    /* Events from WifiService */
1063    /** @hide */
1064    public static final int CMD_WPS_COMPLETED               = 11;
1065
1066    /* For system use only */
1067    /** @hide */
1068    public static final int CMD_ENABLE_TRAFFIC_STATS_POLL   = 21;
1069    /** @hide */
1070    public static final int CMD_TRAFFIC_STATS_POLL          = 22;
1071
1072    /**
1073     * Initiate an asynchronous channel connection setup
1074     * @param srcContext is the context of the source
1075     * @param srcHandler is the handler on which the source receives messages
1076     * @hide
1077     */
1078     public void asyncConnect(Context srcContext, Handler srcHandler) {
1079        mAsyncChannel.connect(srcContext, srcHandler, getMessenger());
1080     }
1081
1082    /**
1083     * Connect to a network with the given configuration. The network also
1084     * gets added to the supplicant configuration.
1085     *
1086     * For a new network, this function is used instead of a
1087     * sequence of addNetwork(), enableNetwork(), saveConfiguration() and
1088     * reconnect()
1089     *
1090     * @param config the set of variables that describe the configuration,
1091     *            contained in a {@link WifiConfiguration} object.
1092     * @hide
1093     */
1094    public void connectNetwork(WifiConfiguration config) {
1095        if (config == null) {
1096            return;
1097        }
1098        mAsyncChannel.sendMessage(CMD_CONNECT_NETWORK, config);
1099    }
1100
1101    /**
1102     * Connect to a network with the given networkId.
1103     *
1104     * This function is used instead of a enableNetwork(), saveConfiguration() and
1105     * reconnect()
1106     *
1107     * @param networkId the network id identifiying the network in the
1108     *                supplicant configuration list
1109     * @hide
1110     */
1111    public void connectNetwork(int networkId) {
1112        if (networkId < 0) {
1113            return;
1114        }
1115        mAsyncChannel.sendMessage(CMD_CONNECT_NETWORK, networkId);
1116    }
1117
1118    /**
1119     * Save the given network in the supplicant config. If the network already
1120     * exists, the configuration is updated. A new network is enabled
1121     * by default.
1122     *
1123     * For a new network, this function is used instead of a
1124     * sequence of addNetwork(), enableNetwork() and saveConfiguration().
1125     *
1126     * For an existing network, it accomplishes the task of updateNetwork()
1127     * and saveConfiguration()
1128     *
1129     * @param config the set of variables that describe the configuration,
1130     *            contained in a {@link WifiConfiguration} object.
1131     * @hide
1132     */
1133    public void saveNetwork(WifiConfiguration config) {
1134        if (config == null) {
1135            return;
1136        }
1137
1138        mAsyncChannel.sendMessage(CMD_SAVE_NETWORK, config);
1139    }
1140
1141    /**
1142     * Delete the network in the supplicant config.
1143     *
1144     * This function is used instead of a sequence of removeNetwork()
1145     * and saveConfiguration().
1146     *
1147     * @param config the set of variables that describe the configuration,
1148     *            contained in a {@link WifiConfiguration} object.
1149     * @hide
1150     */
1151    public void forgetNetwork(int netId) {
1152        if (netId < 0) {
1153            return;
1154        }
1155
1156        mAsyncChannel.sendMessage(CMD_FORGET_NETWORK, netId);
1157    }
1158
1159    /**
1160     * Start Wi-fi Protected Setup
1161     *
1162     * @param config WPS configuration
1163     * @hide
1164     */
1165    public void startWps(WpsConfiguration config) {
1166        if (config == null) {
1167            return;
1168        }
1169
1170        mAsyncChannel.sendMessage(CMD_START_WPS, config);
1171    }
1172
1173    /**
1174     * Get a reference to WifiService handler. This is used by a client to establish
1175     * an AsyncChannel communication with WifiService
1176     *
1177     * @return Messenger pointing to the WifiService handler
1178     * @hide
1179     */
1180    public Messenger getMessenger() {
1181        try {
1182            return mService.getMessenger();
1183        } catch (RemoteException e) {
1184            return null;
1185        }
1186    }
1187
1188    /**
1189     * Returns the file in which IP and proxy configuration data is stored
1190     * @hide
1191     */
1192    public String getConfigFile() {
1193        try {
1194            return mService.getConfigFile();
1195        } catch (RemoteException e) {
1196            return null;
1197        }
1198    }
1199
1200    /**
1201     * Allows an application to keep the Wi-Fi radio awake.
1202     * Normally the Wi-Fi radio may turn off when the user has not used the device in a while.
1203     * Acquiring a WifiLock will keep the radio on until the lock is released.  Multiple
1204     * applications may hold WifiLocks, and the radio will only be allowed to turn off when no
1205     * WifiLocks are held in any application.
1206     * <p>
1207     * Before using a WifiLock, consider carefully if your application requires Wi-Fi access, or
1208     * could function over a mobile network, if available.  A program that needs to download large
1209     * files should hold a WifiLock to ensure that the download will complete, but a program whose
1210     * network usage is occasional or low-bandwidth should not hold a WifiLock to avoid adversely
1211     * affecting battery life.
1212     * <p>
1213     * Note that WifiLocks cannot override the user-level "Wi-Fi Enabled" setting, nor Airplane
1214     * Mode.  They simply keep the radio from turning off when Wi-Fi is already on but the device
1215     * is idle.
1216     * <p>
1217     * Any application using a WifiLock must request the {@code android.permission.WAKE_LOCK}
1218     * permission in an {@code &lt;uses-permission&gt;} element of the application's manifest.
1219     */
1220    public class WifiLock {
1221        private String mTag;
1222        private final IBinder mBinder;
1223        private int mRefCount;
1224        int mLockType;
1225        private boolean mRefCounted;
1226        private boolean mHeld;
1227        private WorkSource mWorkSource;
1228
1229        private WifiLock(int lockType, String tag) {
1230            mTag = tag;
1231            mLockType = lockType;
1232            mBinder = new Binder();
1233            mRefCount = 0;
1234            mRefCounted = true;
1235            mHeld = false;
1236        }
1237
1238        /**
1239         * Locks the Wi-Fi radio on until {@link #release} is called.
1240         *
1241         * If this WifiLock is reference-counted, each call to {@code acquire} will increment the
1242         * reference count, and the radio will remain locked as long as the reference count is
1243         * above zero.
1244         *
1245         * If this WifiLock is not reference-counted, the first call to {@code acquire} will lock
1246         * the radio, but subsequent calls will be ignored.  Only one call to {@link #release}
1247         * will be required, regardless of the number of times that {@code acquire} is called.
1248         */
1249        public void acquire() {
1250            synchronized (mBinder) {
1251                if (mRefCounted ? (++mRefCount > 0) : (!mHeld)) {
1252                    try {
1253                        mService.acquireWifiLock(mBinder, mLockType, mTag, mWorkSource);
1254                        synchronized (WifiManager.this) {
1255                            if (mActiveLockCount >= MAX_ACTIVE_LOCKS) {
1256                                mService.releaseWifiLock(mBinder);
1257                                throw new UnsupportedOperationException(
1258                                            "Exceeded maximum number of wifi locks");
1259                            }
1260                            mActiveLockCount++;
1261                        }
1262                    } catch (RemoteException ignore) {
1263                    }
1264                    mHeld = true;
1265                }
1266            }
1267        }
1268
1269        /**
1270         * Unlocks the Wi-Fi radio, allowing it to turn off when the device is idle.
1271         *
1272         * If this WifiLock is reference-counted, each call to {@code release} will decrement the
1273         * reference count, and the radio will be unlocked only when the reference count reaches
1274         * zero.  If the reference count goes below zero (that is, if {@code release} is called
1275         * a greater number of times than {@link #acquire}), an exception is thrown.
1276         *
1277         * If this WifiLock is not reference-counted, the first call to {@code release} (after
1278         * the radio was locked using {@link #acquire}) will unlock the radio, and subsequent
1279         * calls will be ignored.
1280         */
1281        public void release() {
1282            synchronized (mBinder) {
1283                if (mRefCounted ? (--mRefCount == 0) : (mHeld)) {
1284                    try {
1285                        mService.releaseWifiLock(mBinder);
1286                        synchronized (WifiManager.this) {
1287                            mActiveLockCount--;
1288                        }
1289                    } catch (RemoteException ignore) {
1290                    }
1291                    mHeld = false;
1292                }
1293                if (mRefCount < 0) {
1294                    throw new RuntimeException("WifiLock under-locked " + mTag);
1295                }
1296            }
1297        }
1298
1299        /**
1300         * Controls whether this is a reference-counted or non-reference-counted WifiLock.
1301         *
1302         * Reference-counted WifiLocks keep track of the number of calls to {@link #acquire} and
1303         * {@link #release}, and only allow the radio to sleep when every call to {@link #acquire}
1304         * has been balanced with a call to {@link #release}.  Non-reference-counted WifiLocks
1305         * lock the radio whenever {@link #acquire} is called and it is unlocked, and unlock the
1306         * radio whenever {@link #release} is called and it is locked.
1307         *
1308         * @param refCounted true if this WifiLock should keep a reference count
1309         */
1310        public void setReferenceCounted(boolean refCounted) {
1311            mRefCounted = refCounted;
1312        }
1313
1314        /**
1315         * Checks whether this WifiLock is currently held.
1316         *
1317         * @return true if this WifiLock is held, false otherwise
1318         */
1319        public boolean isHeld() {
1320            synchronized (mBinder) {
1321                return mHeld;
1322            }
1323        }
1324
1325        public void setWorkSource(WorkSource ws) {
1326            synchronized (mBinder) {
1327                if (ws != null && ws.size() == 0) {
1328                    ws = null;
1329                }
1330                boolean changed = true;
1331                if (ws == null) {
1332                    mWorkSource = null;
1333                } else if (mWorkSource == null) {
1334                    changed = mWorkSource != null;
1335                    mWorkSource = new WorkSource(ws);
1336                } else {
1337                    changed = mWorkSource.diff(ws);
1338                    if (changed) {
1339                        mWorkSource.set(ws);
1340                    }
1341                }
1342                if (changed && mHeld) {
1343                    try {
1344                        mService.updateWifiLockWorkSource(mBinder, mWorkSource);
1345                    } catch (RemoteException e) {
1346                    }
1347                }
1348            }
1349        }
1350
1351        public String toString() {
1352            String s1, s2, s3;
1353            synchronized (mBinder) {
1354                s1 = Integer.toHexString(System.identityHashCode(this));
1355                s2 = mHeld ? "held; " : "";
1356                if (mRefCounted) {
1357                    s3 = "refcounted: refcount = " + mRefCount;
1358                } else {
1359                    s3 = "not refcounted";
1360                }
1361                return "WifiLock{ " + s1 + "; " + s2 + s3 + " }";
1362            }
1363        }
1364
1365        @Override
1366        protected void finalize() throws Throwable {
1367            super.finalize();
1368            synchronized (mBinder) {
1369                if (mHeld) {
1370                    try {
1371                        mService.releaseWifiLock(mBinder);
1372                        synchronized (WifiManager.this) {
1373                            mActiveLockCount--;
1374                        }
1375                    } catch (RemoteException ignore) {
1376                    }
1377                }
1378            }
1379        }
1380    }
1381
1382    /**
1383     * Creates a new WifiLock.
1384     *
1385     * @param lockType the type of lock to create. See {@link #WIFI_MODE_FULL},
1386     * {@link #WIFI_MODE_FULL_HIGH_PERF} and {@link #WIFI_MODE_SCAN_ONLY} for
1387     * descriptions of the types of Wi-Fi locks.
1388     * @param tag a tag for the WifiLock to identify it in debugging messages.  This string is
1389     *            never shown to the user under normal conditions, but should be descriptive
1390     *            enough to identify your application and the specific WifiLock within it, if it
1391     *            holds multiple WifiLocks.
1392     *
1393     * @return a new, unacquired WifiLock with the given tag.
1394     *
1395     * @see WifiLock
1396     */
1397    public WifiLock createWifiLock(int lockType, String tag) {
1398        return new WifiLock(lockType, tag);
1399    }
1400
1401    /**
1402     * Creates a new WifiLock.
1403     *
1404     * @param tag a tag for the WifiLock to identify it in debugging messages.  This string is
1405     *            never shown to the user under normal conditions, but should be descriptive
1406     *            enough to identify your application and the specific WifiLock within it, if it
1407     *            holds multiple WifiLocks.
1408     *
1409     * @return a new, unacquired WifiLock with the given tag.
1410     *
1411     * @see WifiLock
1412     */
1413    public WifiLock createWifiLock(String tag) {
1414        return new WifiLock(WIFI_MODE_FULL, tag);
1415    }
1416
1417
1418    /**
1419     * Create a new MulticastLock
1420     *
1421     * @param tag a tag for the MulticastLock to identify it in debugging
1422     *            messages.  This string is never shown to the user under
1423     *            normal conditions, but should be descriptive enough to
1424     *            identify your application and the specific MulticastLock
1425     *            within it, if it holds multiple MulticastLocks.
1426     *
1427     * @return a new, unacquired MulticastLock with the given tag.
1428     *
1429     * @see MulticastLock
1430     */
1431    public MulticastLock createMulticastLock(String tag) {
1432        return new MulticastLock(tag);
1433    }
1434
1435    /**
1436     * Allows an application to receive Wifi Multicast packets.
1437     * Normally the Wifi stack filters out packets not explicitly
1438     * addressed to this device.  Acquring a MulticastLock will
1439     * cause the stack to receive packets addressed to multicast
1440     * addresses.  Processing these extra packets can cause a noticable
1441     * battery drain and should be disabled when not needed.
1442     */
1443    public class MulticastLock {
1444        private String mTag;
1445        private final IBinder mBinder;
1446        private int mRefCount;
1447        private boolean mRefCounted;
1448        private boolean mHeld;
1449
1450        private MulticastLock(String tag) {
1451            mTag = tag;
1452            mBinder = new Binder();
1453            mRefCount = 0;
1454            mRefCounted = true;
1455            mHeld = false;
1456        }
1457
1458        /**
1459         * Locks Wifi Multicast on until {@link #release} is called.
1460         *
1461         * If this MulticastLock is reference-counted each call to
1462         * {@code acquire} will increment the reference count, and the
1463         * wifi interface will receive multicast packets as long as the
1464         * reference count is above zero.
1465         *
1466         * If this MulticastLock is not reference-counted, the first call to
1467         * {@code acquire} will turn on the multicast packets, but subsequent
1468         * calls will be ignored.  Only one call to {@link #release} will
1469         * be required, regardless of the number of times that {@code acquire}
1470         * is called.
1471         *
1472         * Note that other applications may also lock Wifi Multicast on.
1473         * Only they can relinquish their lock.
1474         *
1475         * Also note that applications cannot leave Multicast locked on.
1476         * When an app exits or crashes, any Multicast locks will be released.
1477         */
1478        public void acquire() {
1479            synchronized (mBinder) {
1480                if (mRefCounted ? (++mRefCount > 0) : (!mHeld)) {
1481                    try {
1482                        mService.acquireMulticastLock(mBinder, mTag);
1483                        synchronized (WifiManager.this) {
1484                            if (mActiveLockCount >= MAX_ACTIVE_LOCKS) {
1485                                mService.releaseMulticastLock();
1486                                throw new UnsupportedOperationException(
1487                                        "Exceeded maximum number of wifi locks");
1488                            }
1489                            mActiveLockCount++;
1490                        }
1491                    } catch (RemoteException ignore) {
1492                    }
1493                    mHeld = true;
1494                }
1495            }
1496        }
1497
1498        /**
1499         * Unlocks Wifi Multicast, restoring the filter of packets
1500         * not addressed specifically to this device and saving power.
1501         *
1502         * If this MulticastLock is reference-counted, each call to
1503         * {@code release} will decrement the reference count, and the
1504         * multicast packets will only stop being received when the reference
1505         * count reaches zero.  If the reference count goes below zero (that
1506         * is, if {@code release} is called a greater number of times than
1507         * {@link #acquire}), an exception is thrown.
1508         *
1509         * If this MulticastLock is not reference-counted, the first call to
1510         * {@code release} (after the radio was multicast locked using
1511         * {@link #acquire}) will unlock the multicast, and subsequent calls
1512         * will be ignored.
1513         *
1514         * Note that if any other Wifi Multicast Locks are still outstanding
1515         * this {@code release} call will not have an immediate effect.  Only
1516         * when all applications have released all their Multicast Locks will
1517         * the Multicast filter be turned back on.
1518         *
1519         * Also note that when an app exits or crashes all of its Multicast
1520         * Locks will be automatically released.
1521         */
1522        public void release() {
1523            synchronized (mBinder) {
1524                if (mRefCounted ? (--mRefCount == 0) : (mHeld)) {
1525                    try {
1526                        mService.releaseMulticastLock();
1527                        synchronized (WifiManager.this) {
1528                            mActiveLockCount--;
1529                        }
1530                    } catch (RemoteException ignore) {
1531                    }
1532                    mHeld = false;
1533                }
1534                if (mRefCount < 0) {
1535                    throw new RuntimeException("MulticastLock under-locked "
1536                            + mTag);
1537                }
1538            }
1539        }
1540
1541        /**
1542         * Controls whether this is a reference-counted or non-reference-
1543         * counted MulticastLock.
1544         *
1545         * Reference-counted MulticastLocks keep track of the number of calls
1546         * to {@link #acquire} and {@link #release}, and only stop the
1547         * reception of multicast packets when every call to {@link #acquire}
1548         * has been balanced with a call to {@link #release}.  Non-reference-
1549         * counted MulticastLocks allow the reception of multicast packets
1550         * whenever {@link #acquire} is called and stop accepting multicast
1551         * packets whenever {@link #release} is called.
1552         *
1553         * @param refCounted true if this MulticastLock should keep a reference
1554         * count
1555         */
1556        public void setReferenceCounted(boolean refCounted) {
1557            mRefCounted = refCounted;
1558        }
1559
1560        /**
1561         * Checks whether this MulticastLock is currently held.
1562         *
1563         * @return true if this MulticastLock is held, false otherwise
1564         */
1565        public boolean isHeld() {
1566            synchronized (mBinder) {
1567                return mHeld;
1568            }
1569        }
1570
1571        public String toString() {
1572            String s1, s2, s3;
1573            synchronized (mBinder) {
1574                s1 = Integer.toHexString(System.identityHashCode(this));
1575                s2 = mHeld ? "held; " : "";
1576                if (mRefCounted) {
1577                    s3 = "refcounted: refcount = " + mRefCount;
1578                } else {
1579                    s3 = "not refcounted";
1580                }
1581                return "MulticastLock{ " + s1 + "; " + s2 + s3 + " }";
1582            }
1583        }
1584
1585        @Override
1586        protected void finalize() throws Throwable {
1587            super.finalize();
1588            setReferenceCounted(false);
1589            release();
1590        }
1591    }
1592
1593    /**
1594     * Check multicast filter status.
1595     *
1596     * @return true if multicast packets are allowed.
1597     *
1598     * @hide pending API council approval
1599     */
1600    public boolean isMulticastEnabled() {
1601        try {
1602            return mService.isMulticastEnabled();
1603        } catch (RemoteException e) {
1604            return false;
1605        }
1606    }
1607
1608    /**
1609     * Initialize the multicast filtering to 'on'
1610     * @hide no intent to publish
1611     */
1612    public boolean initializeMulticastFiltering() {
1613        try {
1614            mService.initializeMulticastFiltering();
1615            return true;
1616        } catch (RemoteException e) {
1617             return false;
1618        }
1619    }
1620}
1621