1/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.net.wifi;
18
19import android.annotation.SdkConstant;
20import android.annotation.SdkConstant.SdkConstantType;
21import android.net.DhcpInfo;
22import android.os.Binder;
23import android.os.IBinder;
24import android.os.Handler;
25import android.os.RemoteException;
26
27import java.util.List;
28
29/**
30 * This class provides the primary API for managing all aspects of Wi-Fi
31 * connectivity. Get an instance of this class by calling
32 * {@link android.content.Context#getSystemService(String) Context.getSystemService(Context.WIFI_SERVICE)}.
33
34 * It deals with several categories of items:
35 * <ul>
36 * <li>The list of configured networks. The list can be viewed and updated,
37 * and attributes of individual entries can be modified.</li>
38 * <li>The currently active Wi-Fi network, if any. Connectivity can be
39 * established or torn down, and dynamic information about the state of
40 * the network can be queried.</li>
41 * <li>Results of access point scans, containing enough information to
42 * make decisions about what access point to connect to.</li>
43 * <li>It defines the names of various Intent actions that are broadcast
44 * upon any sort of change in Wi-Fi state.
45 * </ul>
46 * This is the API to use when performing Wi-Fi specific operations. To
47 * perform operations that pertain to network connectivity at an abstract
48 * level, use {@link android.net.ConnectivityManager}.
49 */
50public class WifiManager {
51
52    // Supplicant error codes:
53    /**
54     * The error code if there was a problem authenticating.
55     */
56    public static final int ERROR_AUTHENTICATING = 1;
57
58    /**
59     * Broadcast intent action indicating that Wi-Fi has been enabled, disabled,
60     * enabling, disabling, or unknown. One extra provides this state as an int.
61     * Another extra provides the previous state, if available.
62     *
63     * @see #EXTRA_WIFI_STATE
64     * @see #EXTRA_PREVIOUS_WIFI_STATE
65     */
66    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
67    public static final String WIFI_STATE_CHANGED_ACTION =
68        "android.net.wifi.WIFI_STATE_CHANGED";
69    /**
70     * The lookup key for an int that indicates whether Wi-Fi is enabled,
71     * disabled, enabling, disabling, or unknown.  Retrieve it with
72     * {@link android.content.Intent#getIntExtra(String,int)}.
73     *
74     * @see #WIFI_STATE_DISABLED
75     * @see #WIFI_STATE_DISABLING
76     * @see #WIFI_STATE_ENABLED
77     * @see #WIFI_STATE_ENABLING
78     * @see #WIFI_STATE_UNKNOWN
79     */
80    public static final String EXTRA_WIFI_STATE = "wifi_state";
81    /**
82     * The previous Wi-Fi state.
83     *
84     * @see #EXTRA_WIFI_STATE
85     */
86    public static final String EXTRA_PREVIOUS_WIFI_STATE = "previous_wifi_state";
87
88    /**
89     * Wi-Fi is currently being disabled. The state will change to {@link #WIFI_STATE_DISABLED} if
90     * it finishes successfully.
91     *
92     * @see #WIFI_STATE_CHANGED_ACTION
93     * @see #getWifiState()
94     */
95    public static final int WIFI_STATE_DISABLING = 0;
96    /**
97     * Wi-Fi is disabled.
98     *
99     * @see #WIFI_STATE_CHANGED_ACTION
100     * @see #getWifiState()
101     */
102    public static final int WIFI_STATE_DISABLED = 1;
103    /**
104     * Wi-Fi is currently being enabled. The state will change to {@link #WIFI_STATE_ENABLED} if
105     * it finishes successfully.
106     *
107     * @see #WIFI_STATE_CHANGED_ACTION
108     * @see #getWifiState()
109     */
110    public static final int WIFI_STATE_ENABLING = 2;
111    /**
112     * Wi-Fi is enabled.
113     *
114     * @see #WIFI_STATE_CHANGED_ACTION
115     * @see #getWifiState()
116     */
117    public static final int WIFI_STATE_ENABLED = 3;
118    /**
119     * Wi-Fi is in an unknown state. This state will occur when an error happens while enabling
120     * or disabling.
121     *
122     * @see #WIFI_STATE_CHANGED_ACTION
123     * @see #getWifiState()
124     */
125    public static final int WIFI_STATE_UNKNOWN = 4;
126
127    /**
128     * Broadcast intent action indicating that Wi-Fi AP has been enabled, disabled,
129     * enabling, disabling, or failed.
130     *
131     * @hide
132     */
133    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
134    public static final String WIFI_AP_STATE_CHANGED_ACTION =
135        "android.net.wifi.WIFI_AP_STATE_CHANGED";
136
137    /**
138     * The lookup key for an int that indicates whether Wi-Fi AP is enabled,
139     * disabled, enabling, disabling, or failed.  Retrieve it with
140     * {@link android.content.Intent#getIntExtra(String,int)}.
141     *
142     * @see #WIFI_AP_STATE_DISABLED
143     * @see #WIFI_AP_STATE_DISABLING
144     * @see #WIFI_AP_STATE_ENABLED
145     * @see #WIFI_AP_STATE_ENABLING
146     * @see #WIFI_AP_STATE_FAILED
147     *
148     * @hide
149     */
150    public static final String EXTRA_WIFI_AP_STATE = "wifi_state";
151    /**
152     * The previous Wi-Fi state.
153     *
154     * @see #EXTRA_WIFI_AP_STATE
155     *
156     * @hide
157     */
158    public static final String EXTRA_PREVIOUS_WIFI_AP_STATE = "previous_wifi_state";
159    /**
160     * Wi-Fi AP is currently being disabled. The state will change to
161     * {@link #WIFI_AP_STATE_DISABLED} if it finishes successfully.
162     *
163     * @see #WIFI_AP_STATE_CHANGED_ACTION
164     * @see #getWifiApState()
165     *
166     * @hide
167     */
168    public static final int WIFI_AP_STATE_DISABLING = 0;
169    /**
170     * Wi-Fi AP is disabled.
171     *
172     * @see #WIFI_AP_STATE_CHANGED_ACTION
173     * @see #getWifiState()
174     *
175     * @hide
176     */
177    public static final int WIFI_AP_STATE_DISABLED = 1;
178    /**
179     * Wi-Fi AP is currently being enabled. The state will change to
180     * {@link #WIFI_AP_STATE_ENABLED} if it finishes successfully.
181     *
182     * @see #WIFI_AP_STATE_CHANGED_ACTION
183     * @see #getWifiApState()
184     *
185     * @hide
186     */
187    public static final int WIFI_AP_STATE_ENABLING = 2;
188    /**
189     * Wi-Fi AP is enabled.
190     *
191     * @see #WIFI_AP_STATE_CHANGED_ACTION
192     * @see #getWifiApState()
193     *
194     * @hide
195     */
196    public static final int WIFI_AP_STATE_ENABLED = 3;
197    /**
198     * Wi-Fi AP is in a failed state. This state will occur when an error occurs during
199     * enabling or disabling
200     *
201     * @see #WIFI_AP_STATE_CHANGED_ACTION
202     * @see #getWifiApState()
203     *
204     * @hide
205     */
206    public static final int WIFI_AP_STATE_FAILED = 4;
207
208    /**
209     * Broadcast intent action indicating that a connection to the supplicant has
210     * been established (and it is now possible
211     * to perform Wi-Fi operations) or the connection to the supplicant has been
212     * lost. One extra provides the connection state as a boolean, where {@code true}
213     * means CONNECTED.
214     * @see #EXTRA_SUPPLICANT_CONNECTED
215     */
216    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
217    public static final String SUPPLICANT_CONNECTION_CHANGE_ACTION =
218        "android.net.wifi.supplicant.CONNECTION_CHANGE";
219    /**
220     * The lookup key for a boolean that indicates whether a connection to
221     * the supplicant daemon has been gained or lost. {@code true} means
222     * a connection now exists.
223     * Retrieve it with {@link android.content.Intent#getBooleanExtra(String,boolean)}.
224     */
225    public static final String EXTRA_SUPPLICANT_CONNECTED = "connected";
226    /**
227     * Broadcast intent action indicating that the state of Wi-Fi connectivity
228     * has changed. One extra provides the new state
229     * in the form of a {@link android.net.NetworkInfo} object. If the new state is
230     * CONNECTED, a second extra may provide the BSSID of the access point,
231     * as a {@code String}.
232     * @see #EXTRA_NETWORK_INFO
233     * @see #EXTRA_BSSID
234     */
235    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
236    public static final String NETWORK_STATE_CHANGED_ACTION = "android.net.wifi.STATE_CHANGE";
237    /**
238     * The lookup key for a {@link android.net.NetworkInfo} object associated with the
239     * Wi-Fi network. Retrieve with
240     * {@link android.content.Intent#getParcelableExtra(String)}.
241     */
242    public static final String EXTRA_NETWORK_INFO = "networkInfo";
243    /**
244     * The lookup key for a String giving the BSSID of the access point to which
245     * we are connected. Only present when the new state is CONNECTED.
246     * Retrieve with
247     * {@link android.content.Intent#getStringExtra(String)}.
248     */
249    public static final String EXTRA_BSSID = "bssid";
250    /**
251     * Broadcast intent action indicating that the state of establishing a connection to
252     * an access point has changed.One extra provides the new
253     * {@link SupplicantState}. Note that the supplicant state is Wi-Fi specific, and
254     * is not generally the most useful thing to look at if you are just interested in
255     * the overall state of connectivity.
256     * @see #EXTRA_NEW_STATE
257     * @see #EXTRA_SUPPLICANT_ERROR
258     */
259    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
260    public static final String SUPPLICANT_STATE_CHANGED_ACTION =
261        "android.net.wifi.supplicant.STATE_CHANGE";
262    /**
263     * The lookup key for a {@link SupplicantState} describing the new state
264     * Retrieve with
265     * {@link android.content.Intent#getParcelableExtra(String)}.
266     */
267    public static final String EXTRA_NEW_STATE = "newState";
268
269    /**
270     * The lookup key for a {@link SupplicantState} describing the supplicant
271     * error code if any
272     * Retrieve with
273     * {@link android.content.Intent#getIntExtra(String, int)}.
274     * @see #ERROR_AUTHENTICATING
275     */
276    public static final String EXTRA_SUPPLICANT_ERROR = "supplicantError";
277
278    /**
279     * An access point scan has completed, and results are available from the supplicant.
280     * Call {@link #getScanResults()} to obtain the results.
281     */
282    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
283    public static final String SCAN_RESULTS_AVAILABLE_ACTION = "android.net.wifi.SCAN_RESULTS";
284    /**
285     * The RSSI (signal strength) has changed.
286     * @see #EXTRA_NEW_RSSI
287     */
288    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
289    public static final String RSSI_CHANGED_ACTION = "android.net.wifi.RSSI_CHANGED";
290    /**
291     * The lookup key for an {@code int} giving the new RSSI in dBm.
292     */
293    public static final String EXTRA_NEW_RSSI = "newRssi";
294
295    /**
296     * The network IDs of the configured networks could have changed.
297     */
298    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
299    public static final String NETWORK_IDS_CHANGED_ACTION = "android.net.wifi.NETWORK_IDS_CHANGED";
300
301    /**
302     * Activity Action: Pick a Wi-Fi network to connect to.
303     * <p>Input: Nothing.
304     * <p>Output: Nothing.
305     */
306    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
307    public static final String ACTION_PICK_WIFI_NETWORK = "android.net.wifi.PICK_WIFI_NETWORK";
308
309    /**
310     * In this Wi-Fi lock mode, Wi-Fi will be kept active,
311     * and will behave normally, i.e., it will attempt to automatically
312     * establish a connection to a remembered access point that is
313     * within range, and will do periodic scans if there are remembered
314     * access points but none are in range.
315     */
316    public static final int WIFI_MODE_FULL = 1;
317    /**
318     * In this Wi-Fi lock mode, Wi-Fi will be kept active,
319     * but the only operation that will be supported is initiation of
320     * scans, and the subsequent reporting of scan results. No attempts
321     * will be made to automatically connect to remembered access points,
322     * nor will periodic scans be automatically performed looking for
323     * remembered access points. Scans must be explicitly requested by
324     * an application in this mode.
325     */
326    public static final int WIFI_MODE_SCAN_ONLY = 2;
327
328    /** Anything worse than or equal to this will show 0 bars. */
329    private static final int MIN_RSSI = -100;
330
331    /** Anything better than or equal to this will show the max bars. */
332    private static final int MAX_RSSI = -55;
333
334    IWifiManager mService;
335    Handler mHandler;
336
337    /* Maximum number of active locks we allow.
338     * This limit was added to prevent apps from creating a ridiculous number
339     * of locks and crashing the system by overflowing the global ref table.
340     */
341    private static final int MAX_ACTIVE_LOCKS = 50;
342
343    /* Number of currently active WifiLocks and MulticastLocks */
344    private int mActiveLockCount;
345
346    /**
347     * Create a new WifiManager instance.
348     * Applications will almost always want to use
349     * {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve
350     * the standard {@link android.content.Context#WIFI_SERVICE Context.WIFI_SERVICE}.
351     * @param service the Binder interface
352     * @param handler target for messages
353     * @hide - hide this because it takes in a parameter of type IWifiManager, which
354     * is a system private class.
355     */
356    public WifiManager(IWifiManager service, Handler handler) {
357        mService = service;
358        mHandler = handler;
359    }
360
361    /**
362     * Return a list of all the networks configured in the supplicant.
363     * Not all fields of WifiConfiguration are returned. Only the following
364     * fields are filled in:
365     * <ul>
366     * <li>networkId</li>
367     * <li>SSID</li>
368     * <li>BSSID</li>
369     * <li>priority</li>
370     * <li>allowedProtocols</li>
371     * <li>allowedKeyManagement</li>
372     * <li>allowedAuthAlgorithms</li>
373     * <li>allowedPairwiseCiphers</li>
374     * <li>allowedGroupCiphers</li>
375     * </ul>
376     * @return a list of network configurations in the form of a list
377     * of {@link WifiConfiguration} objects.
378     */
379    public List<WifiConfiguration> getConfiguredNetworks() {
380        try {
381            return mService.getConfiguredNetworks();
382        } catch (RemoteException e) {
383            return null;
384        }
385    }
386
387    /**
388     * Add a new network description to the set of configured networks.
389     * The {@code networkId} field of the supplied configuration object
390     * is ignored.
391     * <p/>
392     * The new network will be marked DISABLED by default. To enable it,
393     * called {@link #enableNetwork}.
394     *
395     * @param config the set of variables that describe the configuration,
396     *            contained in a {@link WifiConfiguration} object.
397     * @return the ID of the newly created network description. This is used in
398     *         other operations to specified the network to be acted upon.
399     *         Returns {@code -1} on failure.
400     */
401    public int addNetwork(WifiConfiguration config) {
402        if (config == null) {
403            return -1;
404        }
405        config.networkId = -1;
406        return addOrUpdateNetwork(config);
407    }
408
409    /**
410     * Update the network description of an existing configured network.
411     *
412     * @param config the set of variables that describe the configuration,
413     *            contained in a {@link WifiConfiguration} object. It may
414     *            be sparse, so that only the items that are being changed
415     *            are non-<code>null</code>. The {@code networkId} field
416     *            must be set to the ID of the existing network being updated.
417     * @return Returns the {@code networkId} of the supplied
418     *         {@code WifiConfiguration} on success.
419     *         <br/>
420     *         Returns {@code -1} on failure, including when the {@code networkId}
421     *         field of the {@code WifiConfiguration} does not refer to an
422     *         existing network.
423     */
424    public int updateNetwork(WifiConfiguration config) {
425        if (config == null || config.networkId < 0) {
426            return -1;
427        }
428        return addOrUpdateNetwork(config);
429    }
430
431    /**
432     * Internal method for doing the RPC that creates a new network description
433     * or updates an existing one.
434     *
435     * @param config The possibly sparse object containing the variables that
436     *         are to set or updated in the network description.
437     * @return the ID of the network on success, {@code -1} on failure.
438     */
439    private int addOrUpdateNetwork(WifiConfiguration config) {
440        try {
441            return mService.addOrUpdateNetwork(config);
442        } catch (RemoteException e) {
443            return -1;
444        }
445    }
446
447    /**
448     * Remove the specified network from the list of configured networks.
449     * This may result in the asynchronous delivery of state change
450     * events.
451     * @param netId the integer that identifies the network configuration
452     * to the supplicant
453     * @return {@code true} if the operation succeeded
454     */
455    public boolean removeNetwork(int netId) {
456        try {
457            return mService.removeNetwork(netId);
458        } catch (RemoteException e) {
459            return false;
460        }
461    }
462
463    /**
464     * Allow a previously configured network to be associated with. If
465     * <code>disableOthers</code> is true, then all other configured
466     * networks are disabled, and an attempt to connect to the selected
467     * network is initiated. This may result in the asynchronous delivery
468     * of state change events.
469     * @param netId the ID of the network in the list of configured networks
470     * @param disableOthers if true, disable all other networks. The way to
471     * select a particular network to connect to is specify {@code true}
472     * for this parameter.
473     * @return {@code true} if the operation succeeded
474     */
475    public boolean enableNetwork(int netId, boolean disableOthers) {
476        try {
477            return mService.enableNetwork(netId, disableOthers);
478        } catch (RemoteException e) {
479            return false;
480        }
481    }
482
483    /**
484     * Disable a configured network. The specified network will not be
485     * a candidate for associating. This may result in the asynchronous
486     * delivery of state change events.
487     * @param netId the ID of the network as returned by {@link #addNetwork}.
488     * @return {@code true} if the operation succeeded
489     */
490    public boolean disableNetwork(int netId) {
491        try {
492            return mService.disableNetwork(netId);
493        } catch (RemoteException e) {
494            return false;
495        }
496    }
497
498    /**
499     * Disassociate from the currently active access point. This may result
500     * in the asynchronous delivery of state change events.
501     * @return {@code true} if the operation succeeded
502     */
503    public boolean disconnect() {
504        try {
505            return mService.disconnect();
506        } catch (RemoteException e) {
507            return false;
508        }
509    }
510
511    /**
512     * Reconnect to the currently active access point, if we are currently
513     * disconnected. This may result in the asynchronous delivery of state
514     * change events.
515     * @return {@code true} if the operation succeeded
516     */
517    public boolean reconnect() {
518        try {
519            return mService.reconnect();
520        } catch (RemoteException e) {
521            return false;
522        }
523    }
524
525    /**
526     * Reconnect to the currently active access point, even if we are already
527     * connected. This may result in the asynchronous delivery of state
528     * change events.
529     * @return {@code true} if the operation succeeded
530     */
531    public boolean reassociate() {
532        try {
533            return mService.reassociate();
534        } catch (RemoteException e) {
535            return false;
536        }
537    }
538
539    /**
540     * Check that the supplicant daemon is responding to requests.
541     * @return {@code true} if we were able to communicate with the supplicant and
542     * it returned the expected response to the PING message.
543     */
544    public boolean pingSupplicant() {
545        if (mService == null)
546            return false;
547        try {
548            return mService.pingSupplicant();
549        } catch (RemoteException e) {
550            return false;
551        }
552    }
553
554    /**
555     * Request a scan for access points. Returns immediately. The availability
556     * of the results is made known later by means of an asynchronous event sent
557     * on completion of the scan.
558     * @return {@code true} if the operation succeeded, i.e., the scan was initiated
559     */
560    public boolean startScan() {
561        try {
562            return mService.startScan(false);
563        } catch (RemoteException e) {
564            return false;
565        }
566    }
567
568    /**
569     * Request a scan for access points. Returns immediately. The availability
570     * of the results is made known later by means of an asynchronous event sent
571     * on completion of the scan.
572     * This is a variant of startScan that forces an active scan, even if passive
573     * scans are the current default
574     * @return {@code true} if the operation succeeded, i.e., the scan was initiated
575     *
576     * @hide
577     */
578    public boolean startScanActive() {
579        try {
580            return mService.startScan(true);
581        } catch (RemoteException e) {
582            return false;
583        }
584    }
585
586    /**
587     * Return dynamic information about the current Wi-Fi connection, if any is active.
588     * @return the Wi-Fi information, contained in {@link WifiInfo}.
589     */
590    public WifiInfo getConnectionInfo() {
591        try {
592            return mService.getConnectionInfo();
593        } catch (RemoteException e) {
594            return null;
595        }
596    }
597
598    /**
599     * Return the results of the latest access point scan.
600     * @return the list of access points found in the most recent scan.
601     */
602    public List<ScanResult> getScanResults() {
603        try {
604            return mService.getScanResults();
605        } catch (RemoteException e) {
606            return null;
607        }
608    }
609
610    /**
611     * Tell the supplicant to persist the current list of configured networks.
612     * <p>
613     * Note: It is possible for this method to change the network IDs of
614     * existing networks. You should assume the network IDs can be different
615     * after calling this method.
616     *
617     * @return {@code true} if the operation succeeded
618     */
619    public boolean saveConfiguration() {
620        try {
621            return mService.saveConfiguration();
622        } catch (RemoteException e) {
623            return false;
624        }
625    }
626
627    /**
628     * Return the number of frequency channels that are allowed
629     * to be used in the current regulatory domain.
630     * @return the number of allowed channels, or {@code -1} if an error occurs
631     *
632     * @hide pending API council
633     */
634    public int getNumAllowedChannels() {
635        try {
636            return mService.getNumAllowedChannels();
637        } catch (RemoteException e) {
638            return -1;
639        }
640    }
641
642    /**
643     * Set the number of frequency channels that are allowed to be used
644     * in the current regulatory domain. This method should be used only
645     * if the correct number of channels cannot be determined automatically
646     * for some reason.
647     * @param numChannels the number of allowed channels. Must be greater than 0
648     * and less than or equal to 16.
649     * @param persist {@code true} if you want this remembered
650     * @return {@code true} if the operation succeeds, {@code false} otherwise, e.g.,
651     * {@code numChannels} is out of range.
652     *
653     * @hide pending API council
654     */
655    public boolean setNumAllowedChannels(int numChannels, boolean persist) {
656        try {
657            return mService.setNumAllowedChannels(numChannels, persist);
658        } catch (RemoteException e) {
659            return false;
660        }
661    }
662
663    /**
664     * Return the list of valid values for the number of allowed radio channels
665     * for various regulatory domains.
666     * @return the list of channel counts, or {@code null} if the operation fails
667     *
668     * @hide pending API council review
669     */
670    public int[] getValidChannelCounts() {
671        try {
672            return mService.getValidChannelCounts();
673        } catch (RemoteException e) {
674            return null;
675        }
676   }
677
678    /**
679     * Return the DHCP-assigned addresses from the last successful DHCP request,
680     * if any.
681     * @return the DHCP information
682     */
683    public DhcpInfo getDhcpInfo() {
684        try {
685            return mService.getDhcpInfo();
686        } catch (RemoteException e) {
687            return null;
688        }
689    }
690
691
692    /**
693     * Enable or disable Wi-Fi.
694     * @param enabled {@code true} to enable, {@code false} to disable.
695     * @return {@code true} if the operation succeeds (or if the existing state
696     *         is the same as the requested state).
697     */
698    public boolean setWifiEnabled(boolean enabled) {
699        try {
700            return mService.setWifiEnabled(enabled);
701        } catch (RemoteException e) {
702            return false;
703        }
704    }
705
706    /**
707     * Gets the Wi-Fi enabled state.
708     * @return One of {@link #WIFI_STATE_DISABLED},
709     *         {@link #WIFI_STATE_DISABLING}, {@link #WIFI_STATE_ENABLED},
710     *         {@link #WIFI_STATE_ENABLING}, {@link #WIFI_STATE_UNKNOWN}
711     * @see #isWifiEnabled()
712     */
713    public int getWifiState() {
714        try {
715            return mService.getWifiEnabledState();
716        } catch (RemoteException e) {
717            return WIFI_STATE_UNKNOWN;
718        }
719    }
720
721    /**
722     * Return whether Wi-Fi is enabled or disabled.
723     * @return {@code true} if Wi-Fi is enabled
724     * @see #getWifiState()
725     */
726    public boolean isWifiEnabled() {
727        return getWifiState() == WIFI_STATE_ENABLED;
728    }
729
730    /**
731     * Calculates the level of the signal. This should be used any time a signal
732     * is being shown.
733     *
734     * @param rssi The power of the signal measured in RSSI.
735     * @param numLevels The number of levels to consider in the calculated
736     *            level.
737     * @return A level of the signal, given in the range of 0 to numLevels-1
738     *         (both inclusive).
739     */
740    public static int calculateSignalLevel(int rssi, int numLevels) {
741        if (rssi <= MIN_RSSI) {
742            return 0;
743        } else if (rssi >= MAX_RSSI) {
744            return numLevels - 1;
745        } else {
746            int partitionSize = (MAX_RSSI - MIN_RSSI) / (numLevels - 1);
747            return (rssi - MIN_RSSI) / partitionSize;
748        }
749    }
750
751    /**
752     * Compares two signal strengths.
753     *
754     * @param rssiA The power of the first signal measured in RSSI.
755     * @param rssiB The power of the second signal measured in RSSI.
756     * @return Returns <0 if the first signal is weaker than the second signal,
757     *         0 if the two signals have the same strength, and >0 if the first
758     *         signal is stronger than the second signal.
759     */
760    public static int compareSignalLevel(int rssiA, int rssiB) {
761        return rssiA - rssiB;
762    }
763
764    /**
765     * Start AccessPoint mode with the specified
766     * configuration. If the radio is already running in
767     * AP mode, update the new configuration
768     * Note that starting in access point mode disables station
769     * mode operation
770     * @param wifiConfig SSID, security and channel details as
771     *        part of WifiConfiguration
772     * @return {@code true} if the operation succeeds, {@code false} otherwise
773     *
774     * @hide Dont open up yet
775     */
776    public boolean setWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled) {
777        try {
778            return mService.setWifiApEnabled(wifiConfig, enabled);
779        } catch (RemoteException e) {
780            return false;
781        }
782    }
783
784    /**
785     * Gets the Wi-Fi enabled state.
786     * @return One of {@link #WIFI_AP_STATE_DISABLED},
787     *         {@link #WIFI_AP_STATE_DISABLING}, {@link #WIFI_AP_STATE_ENABLED},
788     *         {@link #WIFI_AP_STATE_ENABLING}, {@link #WIFI_AP_STATE_FAILED}
789     * @see #isWifiApEnabled()
790     *
791     * @hide Dont open yet
792     */
793    public int getWifiApState() {
794        try {
795            return mService.getWifiApEnabledState();
796        } catch (RemoteException e) {
797            return WIFI_AP_STATE_FAILED;
798        }
799    }
800
801    /**
802     * Return whether Wi-Fi AP is enabled or disabled.
803     * @return {@code true} if Wi-Fi AP is enabled
804     * @see #getWifiApState()
805     *
806     * @hide Dont open yet
807     */
808    public boolean isWifiApEnabled() {
809        return getWifiApState() == WIFI_AP_STATE_ENABLED;
810    }
811
812    /**
813     * Gets the Wi-Fi AP Configuration.
814     * @return AP details in WifiConfiguration
815     *
816     * @hide Dont open yet
817     */
818    public WifiConfiguration getWifiApConfiguration() {
819        try {
820            return mService.getWifiApConfiguration();
821        } catch (RemoteException e) {
822            return null;
823        }
824    }
825
826    /**
827     * Allows an application to keep the Wi-Fi radio awake.
828     * Normally the Wi-Fi radio may turn off when the user has not used the device in a while.
829     * Acquiring a WifiLock will keep the radio on until the lock is released.  Multiple
830     * applications may hold WifiLocks, and the radio will only be allowed to turn off when no
831     * WifiLocks are held in any application.
832     *
833     * Before using a WifiLock, consider carefully if your application requires Wi-Fi access, or
834     * could function over a mobile network, if available.  A program that needs to download large
835     * files should hold a WifiLock to ensure that the download will complete, but a program whose
836     * network usage is occasional or low-bandwidth should not hold a WifiLock to avoid adversely
837     * affecting battery life.
838     *
839     * Note that WifiLocks cannot override the user-level "Wi-Fi Enabled" setting, nor Airplane
840     * Mode.  They simply keep the radio from turning off when Wi-Fi is already on but the device
841     * is idle.
842     */
843    public class WifiLock {
844        private String mTag;
845        private final IBinder mBinder;
846        private int mRefCount;
847        int mLockType;
848        private boolean mRefCounted;
849        private boolean mHeld;
850
851        private WifiLock(int lockType, String tag) {
852            mTag = tag;
853            mLockType = lockType;
854            mBinder = new Binder();
855            mRefCount = 0;
856            mRefCounted = true;
857            mHeld = false;
858        }
859
860        /**
861         * Locks the Wi-Fi radio on until {@link #release} is called.
862         *
863         * If this WifiLock is reference-counted, each call to {@code acquire} will increment the
864         * reference count, and the radio will remain locked as long as the reference count is
865         * above zero.
866         *
867         * If this WifiLock is not reference-counted, the first call to {@code acquire} will lock
868         * the radio, but subsequent calls will be ignored.  Only one call to {@link #release}
869         * will be required, regardless of the number of times that {@code acquire} is called.
870         */
871        public void acquire() {
872            synchronized (mBinder) {
873                if (mRefCounted ? (++mRefCount > 0) : (!mHeld)) {
874                    try {
875                        mService.acquireWifiLock(mBinder, mLockType, mTag);
876                        synchronized (WifiManager.this) {
877                            if (mActiveLockCount >= MAX_ACTIVE_LOCKS) {
878                                mService.releaseWifiLock(mBinder);
879                                throw new UnsupportedOperationException(
880                                            "Exceeded maximum number of wifi locks");
881                            }
882                            mActiveLockCount++;
883                        }
884                    } catch (RemoteException ignore) {
885                    }
886                    mHeld = true;
887                }
888            }
889        }
890
891        /**
892         * Unlocks the Wi-Fi radio, allowing it to turn off when the device is idle.
893         *
894         * If this WifiLock is reference-counted, each call to {@code release} will decrement the
895         * reference count, and the radio will be unlocked only when the reference count reaches
896         * zero.  If the reference count goes below zero (that is, if {@code release} is called
897         * a greater number of times than {@link #acquire}), an exception is thrown.
898         *
899         * If this WifiLock is not reference-counted, the first call to {@code release} (after
900         * the radio was locked using {@link #acquire}) will unlock the radio, and subsequent
901         * calls will be ignored.
902         */
903        public void release() {
904            synchronized (mBinder) {
905                if (mRefCounted ? (--mRefCount == 0) : (mHeld)) {
906                    try {
907                        mService.releaseWifiLock(mBinder);
908                        synchronized (WifiManager.this) {
909                            mActiveLockCount--;
910                        }
911                    } catch (RemoteException ignore) {
912                    }
913                    mHeld = false;
914                }
915                if (mRefCount < 0) {
916                    throw new RuntimeException("WifiLock under-locked " + mTag);
917                }
918            }
919        }
920
921        /**
922         * Controls whether this is a reference-counted or non-reference-counted WifiLock.
923         *
924         * Reference-counted WifiLocks keep track of the number of calls to {@link #acquire} and
925         * {@link #release}, and only allow the radio to sleep when every call to {@link #acquire}
926         * has been balanced with a call to {@link #release}.  Non-reference-counted WifiLocks
927         * lock the radio whenever {@link #acquire} is called and it is unlocked, and unlock the
928         * radio whenever {@link #release} is called and it is locked.
929         *
930         * @param refCounted true if this WifiLock should keep a reference count
931         */
932        public void setReferenceCounted(boolean refCounted) {
933            mRefCounted = refCounted;
934        }
935
936        /**
937         * Checks whether this WifiLock is currently held.
938         *
939         * @return true if this WifiLock is held, false otherwise
940         */
941        public boolean isHeld() {
942            synchronized (mBinder) {
943                return mHeld;
944            }
945        }
946
947        public String toString() {
948            String s1, s2, s3;
949            synchronized (mBinder) {
950                s1 = Integer.toHexString(System.identityHashCode(this));
951                s2 = mHeld ? "held; " : "";
952                if (mRefCounted) {
953                    s3 = "refcounted: refcount = " + mRefCount;
954                } else {
955                    s3 = "not refcounted";
956                }
957                return "WifiLock{ " + s1 + "; " + s2 + s3 + " }";
958            }
959        }
960
961        @Override
962        protected void finalize() throws Throwable {
963            super.finalize();
964            synchronized (mBinder) {
965                if (mHeld) {
966                    try {
967                        mService.releaseWifiLock(mBinder);
968                        synchronized (WifiManager.this) {
969                            mActiveLockCount--;
970                        }
971                    } catch (RemoteException ignore) {
972                    }
973                }
974            }
975        }
976    }
977
978    /**
979     * Creates a new WifiLock.
980     *
981     * @param lockType the type of lock to create. See {@link #WIFI_MODE_FULL} and
982     * {@link #WIFI_MODE_SCAN_ONLY} for descriptions of the types of Wi-Fi locks.
983     * @param tag a tag for the WifiLock to identify it in debugging messages.  This string is
984     *            never shown to the user under normal conditions, but should be descriptive
985     *            enough to identify your application and the specific WifiLock within it, if it
986     *            holds multiple WifiLocks.
987     *
988     * @return a new, unacquired WifiLock with the given tag.
989     *
990     * @see WifiLock
991     */
992    public WifiLock createWifiLock(int lockType, String tag) {
993        return new WifiLock(lockType, tag);
994    }
995
996    /**
997     * Creates a new WifiLock.
998     *
999     * @param tag a tag for the WifiLock to identify it in debugging messages.  This string is
1000     *            never shown to the user under normal conditions, but should be descriptive
1001     *            enough to identify your application and the specific WifiLock within it, if it
1002     *            holds multiple WifiLocks.
1003     *
1004     * @return a new, unacquired WifiLock with the given tag.
1005     *
1006     * @see WifiLock
1007     */
1008    public WifiLock createWifiLock(String tag) {
1009        return new WifiLock(WIFI_MODE_FULL, tag);
1010    }
1011
1012
1013    /**
1014     * Create a new MulticastLock
1015     *
1016     * @param tag a tag for the MulticastLock to identify it in debugging
1017     *            messages.  This string is never shown to the user under
1018     *            normal conditions, but should be descriptive enough to
1019     *            identify your application and the specific MulticastLock
1020     *            within it, if it holds multiple MulticastLocks.
1021     *
1022     * @return a new, unacquired MulticastLock with the given tag.
1023     *
1024     * @see MulticastLock
1025     */
1026    public MulticastLock createMulticastLock(String tag) {
1027        return new MulticastLock(tag);
1028    }
1029
1030    /**
1031     * Allows an application to receive Wifi Multicast packets.
1032     * Normally the Wifi stack filters out packets not explicitly
1033     * addressed to this device.  Acquring a MulticastLock will
1034     * cause the stack to receive packets addressed to multicast
1035     * addresses.  Processing these extra packets can cause a noticable
1036     * battery drain and should be disabled when not needed.
1037     */
1038    public class MulticastLock {
1039        private String mTag;
1040        private final IBinder mBinder;
1041        private int mRefCount;
1042        private boolean mRefCounted;
1043        private boolean mHeld;
1044
1045        private MulticastLock(String tag) {
1046            mTag = tag;
1047            mBinder = new Binder();
1048            mRefCount = 0;
1049            mRefCounted = true;
1050            mHeld = false;
1051        }
1052
1053        /**
1054         * Locks Wifi Multicast on until {@link #release} is called.
1055         *
1056         * If this MulticastLock is reference-counted each call to
1057         * {@code acquire} will increment the reference count, and the
1058         * wifi interface will receive multicast packets as long as the
1059         * reference count is above zero.
1060         *
1061         * If this MulticastLock is not reference-counted, the first call to
1062         * {@code acquire} will turn on the multicast packets, but subsequent
1063         * calls will be ignored.  Only one call to {@link #release} will
1064         * be required, regardless of the number of times that {@code acquire}
1065         * is called.
1066         *
1067         * Note that other applications may also lock Wifi Multicast on.
1068         * Only they can relinquish their lock.
1069         *
1070         * Also note that applications cannot leave Multicast locked on.
1071         * When an app exits or crashes, any Multicast locks will be released.
1072         */
1073        public void acquire() {
1074            synchronized (mBinder) {
1075                if (mRefCounted ? (++mRefCount > 0) : (!mHeld)) {
1076                    try {
1077                        mService.acquireMulticastLock(mBinder, mTag);
1078                        synchronized (WifiManager.this) {
1079                            if (mActiveLockCount >= MAX_ACTIVE_LOCKS) {
1080                                mService.releaseMulticastLock();
1081                                throw new UnsupportedOperationException(
1082                                        "Exceeded maximum number of wifi locks");
1083                            }
1084                            mActiveLockCount++;
1085                        }
1086                    } catch (RemoteException ignore) {
1087                    }
1088                    mHeld = true;
1089                }
1090            }
1091        }
1092
1093        /**
1094         * Unlocks Wifi Multicast, restoring the filter of packets
1095         * not addressed specifically to this device and saving power.
1096         *
1097         * If this MulticastLock is reference-counted, each call to
1098         * {@code release} will decrement the reference count, and the
1099         * multicast packets will only stop being received when the reference
1100         * count reaches zero.  If the reference count goes below zero (that
1101         * is, if {@code release} is called a greater number of times than
1102         * {@link #acquire}), an exception is thrown.
1103         *
1104         * If this MulticastLock is not reference-counted, the first call to
1105         * {@code release} (after the radio was multicast locked using
1106         * {@link #acquire}) will unlock the multicast, and subsequent calls
1107         * will be ignored.
1108         *
1109         * Note that if any other Wifi Multicast Locks are still outstanding
1110         * this {@code release} call will not have an immediate effect.  Only
1111         * when all applications have released all their Multicast Locks will
1112         * the Multicast filter be turned back on.
1113         *
1114         * Also note that when an app exits or crashes all of its Multicast
1115         * Locks will be automatically released.
1116         */
1117        public void release() {
1118            synchronized (mBinder) {
1119                if (mRefCounted ? (--mRefCount == 0) : (mHeld)) {
1120                    try {
1121                        mService.releaseMulticastLock();
1122                        synchronized (WifiManager.this) {
1123                            mActiveLockCount--;
1124                        }
1125                    } catch (RemoteException ignore) {
1126                    }
1127                    mHeld = false;
1128                }
1129                if (mRefCount < 0) {
1130                    throw new RuntimeException("MulticastLock under-locked "
1131                            + mTag);
1132                }
1133            }
1134        }
1135
1136        /**
1137         * Controls whether this is a reference-counted or non-reference-
1138         * counted MulticastLock.
1139         *
1140         * Reference-counted MulticastLocks keep track of the number of calls
1141         * to {@link #acquire} and {@link #release}, and only stop the
1142         * reception of multicast packets when every call to {@link #acquire}
1143         * has been balanced with a call to {@link #release}.  Non-reference-
1144         * counted MulticastLocks allow the reception of multicast packets
1145         * whenever {@link #acquire} is called and stop accepting multicast
1146         * packets whenever {@link #release} is called.
1147         *
1148         * @param refCounted true if this MulticastLock should keep a reference
1149         * count
1150         */
1151        public void setReferenceCounted(boolean refCounted) {
1152            mRefCounted = refCounted;
1153        }
1154
1155        /**
1156         * Checks whether this MulticastLock is currently held.
1157         *
1158         * @return true if this MulticastLock is held, false otherwise
1159         */
1160        public boolean isHeld() {
1161            synchronized (mBinder) {
1162                return mHeld;
1163            }
1164        }
1165
1166        public String toString() {
1167            String s1, s2, s3;
1168            synchronized (mBinder) {
1169                s1 = Integer.toHexString(System.identityHashCode(this));
1170                s2 = mHeld ? "held; " : "";
1171                if (mRefCounted) {
1172                    s3 = "refcounted: refcount = " + mRefCount;
1173                } else {
1174                    s3 = "not refcounted";
1175                }
1176                return "MulticastLock{ " + s1 + "; " + s2 + s3 + " }";
1177            }
1178        }
1179
1180        @Override
1181        protected void finalize() throws Throwable {
1182            super.finalize();
1183            setReferenceCounted(false);
1184            release();
1185        }
1186    }
1187
1188    /**
1189     * Check multicast filter status.
1190     *
1191     * @return true if multicast packets are allowed.
1192     *
1193     * @hide pending API council approval
1194     */
1195    public boolean isMulticastEnabled() {
1196        try {
1197            return mService.isMulticastEnabled();
1198        } catch (RemoteException e) {
1199            return false;
1200        }
1201    }
1202
1203    /**
1204     * Initialize the multicast filtering to 'on'
1205     * @hide no intent to publish
1206     */
1207    public boolean initializeMulticastFiltering() {
1208        try {
1209            mService.initializeMulticastFiltering();
1210            return true;
1211        } catch (RemoteException e) {
1212             return false;
1213        }
1214    }
1215}
1216