/* * Copyright (C) 2008 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.net.wifi; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.net.DhcpInfo; import android.os.Binder; import android.os.IBinder; import android.os.Handler; import android.os.RemoteException; import java.util.List; /** * This class provides the primary API for managing all aspects of Wi-Fi * connectivity. Get an instance of this class by calling * {@link android.content.Context#getSystemService(String) Context.getSystemService(Context.WIFI_SERVICE)}. * It deals with several categories of items: * <ul> * <li>The list of configured networks. The list can be viewed and updated, * and attributes of individual entries can be modified.</li> * <li>The currently active Wi-Fi network, if any. Connectivity can be * established or torn down, and dynamic information about the state of * the network can be queried.</li> * <li>Results of access point scans, containing enough information to * make decisions about what access point to connect to.</li> * <li>It defines the names of various Intent actions that are broadcast * upon any sort of change in Wi-Fi state. * </ul> * This is the API to use when performing Wi-Fi specific operations. To * perform operations that pertain to network connectivity at an abstract * level, use {@link android.net.ConnectivityManager}. */ public class WifiManager { // Supplicant error codes: /** * The error code if there was a problem authenticating. */ public static final int ERROR_AUTHENTICATING = 1; /** * Broadcast intent action indicating that Wi-Fi has been enabled, disabled, * enabling, disabling, or unknown. One extra provides this state as an int. * Another extra provides the previous state, if available. * * @see #EXTRA_WIFI_STATE * @see #EXTRA_PREVIOUS_WIFI_STATE */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String WIFI_STATE_CHANGED_ACTION = "android.net.wifi.WIFI_STATE_CHANGED"; /** * The lookup key for an int that indicates whether Wi-Fi is enabled, * disabled, enabling, disabling, or unknown. Retrieve it with * {@link android.content.Intent#getIntExtra(String,int)}. * * @see #WIFI_STATE_DISABLED * @see #WIFI_STATE_DISABLING * @see #WIFI_STATE_ENABLED * @see #WIFI_STATE_ENABLING * @see #WIFI_STATE_UNKNOWN */ public static final String EXTRA_WIFI_STATE = "wifi_state"; /** * The previous Wi-Fi state. * * @see #EXTRA_WIFI_STATE */ public static final String EXTRA_PREVIOUS_WIFI_STATE = "previous_wifi_state"; /** * Wi-Fi is currently being disabled. The state will change to {@link #WIFI_STATE_DISABLED} if * it finishes successfully. * * @see #WIFI_STATE_CHANGED_ACTION * @see #getWifiState() */ public static final int WIFI_STATE_DISABLING = 0; /** * Wi-Fi is disabled. * * @see #WIFI_STATE_CHANGED_ACTION * @see #getWifiState() */ public static final int WIFI_STATE_DISABLED = 1; /** * Wi-Fi is currently being enabled. The state will change to {@link #WIFI_STATE_ENABLED} if * it finishes successfully. * * @see #WIFI_STATE_CHANGED_ACTION * @see #getWifiState() */ public static final int WIFI_STATE_ENABLING = 2; /** * Wi-Fi is enabled. * * @see #WIFI_STATE_CHANGED_ACTION * @see #getWifiState() */ public static final int WIFI_STATE_ENABLED = 3; /** * Wi-Fi is in an unknown state. This state will occur when an error happens while enabling * or disabling. * * @see #WIFI_STATE_CHANGED_ACTION * @see #getWifiState() */ public static final int WIFI_STATE_UNKNOWN = 4; /** * Broadcast intent action indicating that a connection to the supplicant has * been established (and it is now possible * to perform Wi-Fi operations) or the connection to the supplicant has been * lost. One extra provides the connection state as a boolean, where {@code true} * means CONNECTED. * @see #EXTRA_SUPPLICANT_CONNECTED */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String SUPPLICANT_CONNECTION_CHANGE_ACTION = "android.net.wifi.supplicant.CONNECTION_CHANGE"; /** * The lookup key for a boolean that indicates whether a connection to * the supplicant daemon has been gained or lost. {@code true} means * a connection now exists. * Retrieve it with {@link android.content.Intent#getBooleanExtra(String,boolean)}. */ public static final String EXTRA_SUPPLICANT_CONNECTED = "connected"; /** * Broadcast intent action indicating that the state of Wi-Fi connectivity * has changed. One extra provides the new state * in the form of a {@link android.net.NetworkInfo} object. If the new state is * CONNECTED, a second extra may provide the BSSID of the access point, * as a {@code String}. * @see #EXTRA_NETWORK_INFO * @see #EXTRA_BSSID */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String NETWORK_STATE_CHANGED_ACTION = "android.net.wifi.STATE_CHANGE"; /** * The lookup key for a {@link android.net.NetworkInfo} object associated with the * Wi-Fi network. Retrieve with * {@link android.content.Intent#getParcelableExtra(String)}. */ public static final String EXTRA_NETWORK_INFO = "networkInfo"; /** * The lookup key for a String giving the BSSID of the access point to which * we are connected. Only present when the new state is CONNECTED. * Retrieve with * {@link android.content.Intent#getStringExtra(String)}. */ public static final String EXTRA_BSSID = "bssid"; /** * Broadcast intent action indicating that the state of establishing a connection to * an access point has changed.One extra provides the new * {@link SupplicantState}. Note that the supplicant state is Wi-Fi specific, and * is not generally the most useful thing to look at if you are just interested in * the overall state of connectivity. * @see #EXTRA_NEW_STATE * @see #EXTRA_SUPPLICANT_ERROR */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String SUPPLICANT_STATE_CHANGED_ACTION = "android.net.wifi.supplicant.STATE_CHANGE"; /** * The lookup key for a {@link SupplicantState} describing the new state * Retrieve with * {@link android.content.Intent#getParcelableExtra(String)}. */ public static final String EXTRA_NEW_STATE = "newState"; /** * The lookup key for a {@link SupplicantState} describing the supplicant * error code if any * Retrieve with * {@link android.content.Intent#getIntExtra(String, int)}. * @see #ERROR_AUTHENTICATING */ public static final String EXTRA_SUPPLICANT_ERROR = "supplicantError"; /** * An access point scan has completed, and results are available from the supplicant. * Call {@link #getScanResults()} to obtain the results. */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String SCAN_RESULTS_AVAILABLE_ACTION = "android.net.wifi.SCAN_RESULTS"; /** * The RSSI (signal strength) has changed. * @see #EXTRA_NEW_RSSI */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String RSSI_CHANGED_ACTION = "android.net.wifi.RSSI_CHANGED"; /** * The lookup key for an {@code int} giving the new RSSI in dBm. */ public static final String EXTRA_NEW_RSSI = "newRssi"; /** * The network IDs of the configured networks could have changed. */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String NETWORK_IDS_CHANGED_ACTION = "android.net.wifi.NETWORK_IDS_CHANGED"; /** * Activity Action: Pick a Wi-Fi network to connect to. * <p>Input: Nothing. * <p>Output: Nothing. */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_PICK_WIFI_NETWORK = "android.net.wifi.PICK_WIFI_NETWORK"; /** * In this Wi-Fi lock mode, Wi-Fi will be kept active, * and will behave normally, i.e., it will attempt to automatically * establish a connection to a remembered access point that is * within range, and will do periodic scans if there are remembered * access points but none are in range. */ public static final int WIFI_MODE_FULL = 1; /** * In this Wi-Fi lock mode, Wi-Fi will be kept active, * but the only operation that will be supported is initiation of * scans, and the subsequent reporting of scan results. No attempts * will be made to automatically connect to remembered access points, * nor will periodic scans be automatically performed looking for * remembered access points. Scans must be explicitly requested by * an application in this mode. */ public static final int WIFI_MODE_SCAN_ONLY = 2; /** Anything worse than or equal to this will show 0 bars. */ private static final int MIN_RSSI = -100; /** Anything better than or equal to this will show the max bars. */ private static final int MAX_RSSI = -55; IWifiManager mService; Handler mHandler; /** * Create a new WifiManager instance. * Applications will almost always want to use * {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve * the standard {@link android.content.Context#WIFI_SERVICE Context.WIFI_SERVICE}. * @param service the Binder interface * @param handler target for messages * @hide - hide this because it takes in a parameter of type IWifiManager, which * is a system private class. */ public WifiManager(IWifiManager service, Handler handler) { mService = service; mHandler = handler; } /** * Return a list of all the networks configured in the supplicant. * Not all fields of WifiConfiguration are returned. Only the following * fields are filled in: * <ul> * <li>networkId</li> * <li>SSID</li> * <li>BSSID</li> * <li>priority</li> * <li>allowedProtocols</li> * <li>allowedKeyManagement</li> * <li>allowedAuthAlgorithms</li> * <li>allowedPairwiseCiphers</li> * <li>allowedGroupCiphers</li> * </ul> * @return a list of network configurations in the form of a list * of {@link WifiConfiguration} objects. */ public List<WifiConfiguration> getConfiguredNetworks() { try { return mService.getConfiguredNetworks(); } catch (RemoteException e) { return null; } } /** * Add a new network description to the set of configured networks. * The {@code networkId} field of the supplied configuration object * is ignored. * <p/> * The new network will be marked DISABLED by default. To enable it, * called {@link #enableNetwork}. * * @param config the set of variables that describe the configuration, * contained in a {@link WifiConfiguration} object. * @return the ID of the newly created network description. This is used in * other operations to specified the network to be acted upon. * Returns {@code -1} on failure. */ public int addNetwork(WifiConfiguration config) { if (config == null) { return -1; } config.networkId = -1; return addOrUpdateNetwork(config); } /** * Update the network description of an existing configured network. * * @param config the set of variables that describe the configuration, * contained in a {@link WifiConfiguration} object. It may * be sparse, so that only the items that are being changed * are non-<code>null</code>. The {@code networkId} field * must be set to the ID of the existing network being updated. * @return Returns the {@code networkId} of the supplied * {@code WifiConfiguration} on success. * <br/> * Returns {@code -1} on failure, including when the {@code networkId} * field of the {@code WifiConfiguration} does not refer to an * existing network. */ public int updateNetwork(WifiConfiguration config) { if (config == null || config.networkId < 0) { return -1; } return addOrUpdateNetwork(config); } /** * Internal method for doing the RPC that creates a new network description * or updates an existing one. * * @param config The possibly sparse object containing the variables that * are to set or updated in the network description. * @return the ID of the network on success, {@code -1} on failure. */ private int addOrUpdateNetwork(WifiConfiguration config) { try { return mService.addOrUpdateNetwork(config); } catch (RemoteException e) { return -1; } } /** * Remove the specified network from the list of configured networks. * This may result in the asynchronous delivery of state change * events. * @param netId the integer that identifies the network configuration * to the supplicant * @return {@code true} if the operation succeeded */ public boolean removeNetwork(int netId) { try { return mService.removeNetwork(netId); } catch (RemoteException e) { return false; } } /** * Allow a previously configured network to be associated with. If * <code>disableOthers</code> is true, then all other configured * networks are disabled, and an attempt to connect to the selected * network is initiated. This may result in the asynchronous delivery * of state change events. * @param netId the ID of the network in the list of configured networks * @param disableOthers if true, disable all other networks. The way to * select a particular network to connect to is specify {@code true} * for this parameter. * @return {@code true} if the operation succeeded */ public boolean enableNetwork(int netId, boolean disableOthers) { try { return mService.enableNetwork(netId, disableOthers); } catch (RemoteException e) { return false; } } /** * Disable a configured network. The specified network will not be * a candidate for associating. This may result in the asynchronous * delivery of state change events. * @param netId the ID of the network as returned by {@link #addNetwork}. * @return {@code true} if the operation succeeded */ public boolean disableNetwork(int netId) { try { return mService.disableNetwork(netId); } catch (RemoteException e) { return false; } } /** * Disassociate from the currently active access point. This may result * in the asynchronous delivery of state change events. * @return {@code true} if the operation succeeded */ public boolean disconnect() { try { return mService.disconnect(); } catch (RemoteException e) { return false; } } /** * Reconnect to the currently active access point, if we are currently * disconnected. This may result in the asynchronous delivery of state * change events. * @return {@code true} if the operation succeeded */ public boolean reconnect() { try { return mService.reconnect(); } catch (RemoteException e) { return false; } } /** * Reconnect to the currently active access point, even if we are already * connected. This may result in the asynchronous delivery of state * change events. * @return {@code true} if the operation succeeded */ public boolean reassociate() { try { return mService.reassociate(); } catch (RemoteException e) { return false; } } /** * Check that the supplicant daemon is responding to requests. * @return {@code true} if we were able to communicate with the supplicant and * it returned the expected response to the PING message. */ public boolean pingSupplicant() { if (mService == null) return false; try { return mService.pingSupplicant(); } catch (RemoteException e) { return false; } } /** * Request a scan for access points. Returns immediately. The availability * of the results is made known later by means of an asynchronous event sent * on completion of the scan. * @return {@code true} if the operation succeeded, i.e., the scan was initiated */ public boolean startScan() { try { return mService.startScan(); } catch (RemoteException e) { return false; } } /** * Return dynamic information about the current Wi-Fi connection, if any is active. * @return the Wi-Fi information, contained in {@link WifiInfo}. */ public WifiInfo getConnectionInfo() { try { return mService.getConnectionInfo(); } catch (RemoteException e) { return null; } } /** * Return the results of the latest access point scan. * @return the list of access points found in the most recent scan. */ public List<ScanResult> getScanResults() { try { return mService.getScanResults(); } catch (RemoteException e) { return null; } } /** * Tell the supplicant to persist the current list of configured networks. * <p> * Note: It is possible for this method to change the network IDs of * existing networks. You should assume the network IDs can be different * after calling this method. * * @return {@code true} if the operation succeeded */ public boolean saveConfiguration() { try { return mService.saveConfiguration(); } catch (RemoteException e) { return false; } } /** * Return the number of frequency channels that are allowed * to be used in the current regulatory domain. * @return the number of allowed channels, or {@code -1} if an error occurs * * @hide pending API council */ public int getNumAllowedChannels() { try { return mService.getNumAllowedChannels(); } catch (RemoteException e) { return -1; } } /** * Set the number of frequency channels that are allowed to be used * in the current regulatory domain. This method should be used only * if the correct number of channels cannot be determined automatically * for some reason. * @param numChannels the number of allowed channels. Must be greater than 0 * and less than or equal to 16. * @return {@code true} if the operation succeeds, {@code false} otherwise, e.g., * {@code numChannels} is out of range. * * @hide pending API council */ public boolean setNumAllowedChannels(int numChannels) { try { return mService.setNumAllowedChannels(numChannels); } catch (RemoteException e) { return false; } } /** * Return the list of valid values for the number of allowed radio channels * for various regulatory domains. * @return the list of channel counts, or {@code null} if the operation fails * * @hide pending API council review */ public int[] getValidChannelCounts() { try { return mService.getValidChannelCounts(); } catch (RemoteException e) { return null; } } /** * Return the DHCP-assigned addresses from the last successful DHCP request, * if any. * @return the DHCP information */ public DhcpInfo getDhcpInfo() { try { return mService.getDhcpInfo(); } catch (RemoteException e) { return null; } } /** * Enable or disable Wi-Fi. * @param enabled {@code true} to enable, {@code false} to disable. * @return {@code true} if the operation succeeds (or if the existing state * is the same as the requested state). */ public boolean setWifiEnabled(boolean enabled) { try { return mService.setWifiEnabled(enabled); } catch (RemoteException e) { return false; } } /** * Gets the Wi-Fi enabled state. * @return One of {@link #WIFI_STATE_DISABLED}, * {@link #WIFI_STATE_DISABLING}, {@link #WIFI_STATE_ENABLED}, * {@link #WIFI_STATE_ENABLING}, {@link #WIFI_STATE_UNKNOWN} * @see #isWifiEnabled() */ public int getWifiState() { try { return mService.getWifiEnabledState(); } catch (RemoteException e) { return WIFI_STATE_UNKNOWN; } } /** * Return whether Wi-Fi is enabled or disabled. * @return {@code true} if Wi-Fi is enabled * @see #getWifiState() */ public boolean isWifiEnabled() { return getWifiState() == WIFI_STATE_ENABLED; } /** * Calculates the level of the signal. This should be used any time a signal * is being shown. * * @param rssi The power of the signal measured in RSSI. * @param numLevels The number of levels to consider in the calculated * level. * @return A level of the signal, given in the range of 0 to numLevels-1 * (both inclusive). */ public static int calculateSignalLevel(int rssi, int numLevels) { if (rssi <= MIN_RSSI) { return 0; } else if (rssi >= MAX_RSSI) { return numLevels - 1; } else { int partitionSize = (MAX_RSSI - MIN_RSSI) / (numLevels - 1); return (rssi - MIN_RSSI) / partitionSize; } } /** * Compares two signal strengths. * * @param rssiA The power of the first signal measured in RSSI. * @param rssiB The power of the second signal measured in RSSI. * @return Returns <0 if the first signal is weaker than the second signal, * 0 if the two signals have the same strength, and >0 if the first * signal is stronger than the second signal. */ public static int compareSignalLevel(int rssiA, int rssiB) { return rssiA - rssiB; } /** * Allows an application to keep the Wi-Fi radio awake. * Normally the Wi-Fi radio may turn off when the user has not used the device in a while. * Acquiring a WifiLock will keep the radio on until the lock is released. Multiple * applications may hold WifiLocks, and the radio will only be allowed to turn off when no * WifiLocks are held in any application. * * Before using a WifiLock, consider carefully if your application requires Wi-Fi access, or * could function over a mobile network, if available. A program that needs to download large * files should hold a WifiLock to ensure that the download will complete, but a program whose * network usage is occasional or low-bandwidth should not hold a WifiLock to avoid adversely * affecting battery life. * * Note that WifiLocks cannot override the user-level "Wi-Fi Enabled" setting, nor Airplane * Mode. They simply keep the radio from turning off when Wi-Fi is already on but the device * is idle. */ public class WifiLock { private String mTag; private final IBinder mBinder; private int mRefCount; int mLockType; private boolean mRefCounted; private boolean mHeld; private WifiLock(int lockType, String tag) { mTag = tag; mLockType = lockType; mBinder = new Binder(); mRefCount = 0; mRefCounted = true; mHeld = false; } /** * Locks the Wi-Fi radio on until {@link #release} is called. * * If this WifiLock is reference-counted, each call to {@code acquire} will increment the * reference count, and the radio will remain locked as long as the reference count is * above zero. * * If this WifiLock is not reference-counted, the first call to {@code acquire} will lock * the radio, but subsequent calls will be ignored. Only one call to {@link #release} * will be required, regardless of the number of times that {@code acquire} is called. */ public void acquire() { synchronized (mBinder) { if (mRefCounted ? (++mRefCount > 0) : (!mHeld)) { try { mService.acquireWifiLock(mBinder, mLockType, mTag); } catch (RemoteException ignore) { } mHeld = true; } } } /** * Unlocks the Wi-Fi radio, allowing it to turn off when the device is idle. * * If this WifiLock is reference-counted, each call to {@code release} will decrement the * reference count, and the radio will be unlocked only when the reference count reaches * zero. If the reference count goes below zero (that is, if {@code release} is called * a greater number of times than {@link #acquire}), an exception is thrown. * * If this WifiLock is not reference-counted, the first call to {@code release} (after * the radio was locked using {@link #acquire}) will unlock the radio, and subsequent * calls will be ignored. */ public void release() { synchronized (mBinder) { if (mRefCounted ? (--mRefCount == 0) : (mHeld)) { try { mService.releaseWifiLock(mBinder); } catch (RemoteException ignore) { } mHeld = false; } if (mRefCount < 0) { throw new RuntimeException("WifiLock under-locked " + mTag); } } } /** * Controls whether this is a reference-counted or non-reference-counted WifiLock. * * Reference-counted WifiLocks keep track of the number of calls to {@link #acquire} and * {@link #release}, and only allow the radio to sleep when every call to {@link #acquire} * has been balanced with a call to {@link #release}. Non-reference-counted WifiLocks * lock the radio whenever {@link #acquire} is called and it is unlocked, and unlock the * radio whenever {@link #release} is called and it is locked. * * @param refCounted true if this WifiLock should keep a reference count */ public void setReferenceCounted(boolean refCounted) { mRefCounted = refCounted; } /** * Checks whether this WifiLock is currently held. * * @return true if this WifiLock is held, false otherwise */ public boolean isHeld() { synchronized (mBinder) { return mHeld; } } public String toString() { String s1, s2, s3; synchronized (mBinder) { s1 = Integer.toHexString(System.identityHashCode(this)); s2 = mHeld ? "held; " : ""; if (mRefCounted) { s3 = "refcounted: refcount = " + mRefCount; } else { s3 = "not refcounted"; } return "WifiLock{ " + s1 + "; " + s2 + s3 + " }"; } } @Override protected void finalize() throws Throwable { super.finalize(); synchronized (mBinder) { if (mHeld) { try { mService.releaseWifiLock(mBinder); } catch (RemoteException ignore) { } } } } } /** * Creates a new WifiLock. * * @param lockType the type of lock to create. See {@link #WIFI_MODE_FULL} and * {@link #WIFI_MODE_SCAN_ONLY} for descriptions of the types of Wi-Fi locks. * @param tag a tag for the WifiLock to identify it in debugging messages. This string is * never shown to the user under normal conditions, but should be descriptive * enough to identify your application and the specific WifiLock within it, if it * holds multiple WifiLocks. * * @return a new, unacquired WifiLock with the given tag. * * @see WifiLock */ public WifiLock createWifiLock(int lockType, String tag) { return new WifiLock(lockType, tag); } /** * Creates a new WifiLock. * * @param tag a tag for the WifiLock to identify it in debugging messages. This string is * never shown to the user under normal conditions, but should be descriptive * enough to identify your application and the specific WifiLock within it, if it * holds multiple WifiLocks. * * @return a new, unacquired WifiLock with the given tag. * * @see WifiLock */ public WifiLock createWifiLock(String tag) { return new WifiLock(WIFI_MODE_FULL, tag); } }