summaryrefslogtreecommitdiffstats
path: root/wifi
diff options
context:
space:
mode:
Diffstat (limited to 'wifi')
-rw-r--r--wifi/java/android/net/wifi/IWifiManager.aidl24
-rw-r--r--wifi/java/android/net/wifi/WifiConfigStore.java969
-rw-r--r--wifi/java/android/net/wifi/WifiConfiguration.java79
-rw-r--r--wifi/java/android/net/wifi/WifiManager.java240
-rw-r--r--wifi/java/android/net/wifi/WifiMonitor.java37
-rw-r--r--wifi/java/android/net/wifi/WifiNative.java8
-rw-r--r--wifi/java/android/net/wifi/WifiStateMachine.java3101
-rw-r--r--wifi/java/android/net/wifi/WifiStateTracker.java2709
8 files changed, 4546 insertions, 2621 deletions
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 0ee559e..e73bca0 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -42,15 +42,15 @@ interface IWifiManager
boolean pingSupplicant();
- boolean startScan(boolean forceActive);
+ void startScan(boolean forceActive);
List<ScanResult> getScanResults();
- boolean disconnect();
+ void disconnect();
- boolean reconnect();
+ void reconnect();
- boolean reassociate();
+ void reassociate();
WifiInfo getConnectionInfo();
@@ -89,5 +89,21 @@ interface IWifiManager
WifiConfiguration getWifiApConfiguration();
void setWifiApConfiguration(in WifiConfiguration wifiConfig);
+
+ void startWifi();
+
+ void stopWifi();
+
+ void addToBlacklist(String bssid);
+
+ void clearBlacklist();
+
+ void connectNetworkWithConfig(in WifiConfiguration wifiConfig);
+
+ void connectNetworkWithId(int networkId);
+
+ void saveNetwork(in WifiConfiguration wifiConfig);
+
+ void forgetNetwork(int networkId);
}
diff --git a/wifi/java/android/net/wifi/WifiConfigStore.java b/wifi/java/android/net/wifi/WifiConfigStore.java
new file mode 100644
index 0000000..dfa9f75
--- /dev/null
+++ b/wifi/java/android/net/wifi/WifiConfigStore.java
@@ -0,0 +1,969 @@
+/*
+ * Copyright (C) 2010 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.app.ActivityManagerNative;
+import android.content.Context;
+import android.content.Intent;
+import android.net.DhcpInfo;
+import android.net.wifi.WifiConfiguration.IpAssignment;
+import android.net.wifi.WifiConfiguration.KeyMgmt;
+import android.net.wifi.WifiConfiguration.Status;
+import android.os.Environment;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * This class provides the API to manage configured
+ * wifi networks. The API is not thread safe is being
+ * used only from WifiStateMachine.
+ *
+ * It deals with the following
+ * - Add/update/remove a WifiConfiguration
+ * The configuration contains two types of information.
+ * = IP configuration that is handled by WifiConfigStore and
+ * is saved to disk on any change.
+ * = SSID & security details that is pushed to the supplicant.
+ * supplicant saves these details to the disk on calling
+ * saveConfigCommand().
+ *
+ * We have two kinds of APIs exposed:
+ * > public API calls that provide fine grained control
+ * - enableNetwork, disableNetwork, addOrUpdateNetwork(),
+ * removeNetwork(). For these calls, the config is not persisted
+ * to the disk. (TODO: deprecate these calls in WifiManager)
+ * > The new API calls - selectNetwork(), saveNetwork() & forgetNetwork().
+ * These calls persist the supplicant config to disk.
+ * - Maintain a list of configured networks for quick access
+ *
+ * TODO:
+ * - handle proxy per configuration
+ */
+class WifiConfigStore {
+
+ private static Context sContext;
+ private static final String TAG = "WifiConfigStore";
+
+ /* configured networks with network id as the key */
+ private static HashMap<Integer, WifiConfiguration> sConfiguredNetworks =
+ new HashMap<Integer, WifiConfiguration>();
+
+ /* A network id is a unique identifier for a network configured in the
+ * supplicant. Network ids are generated when the supplicant reads
+ * the configuration file at start and can thus change for networks.
+ * We store the IP configuration for networks along with a unique id
+ * that is generated from SSID and security type of the network. A mapping
+ * from the generated unique id to network id of the network is needed to
+ * map supplicant config to IP configuration. */
+ private static HashMap<Integer, Integer> sNetworkIds =
+ new HashMap<Integer, Integer>();
+
+ /* Tracks the highest priority of configured networks */
+ private static int sLastPriority = -1;
+
+ private static final String ipConfigFile = Environment.getDataDirectory() +
+ "/misc/wifi/ipconfig.txt";
+
+ private static final int IPCONFIG_FILE_VERSION = 1;
+
+ /**
+ * Initialize context, fetch the list of configured networks
+ * and enable all stored networks in supplicant.
+ */
+ static void initialize(Context context) {
+ Log.d(TAG, "Updating config and enabling all networks");
+ sContext = context;
+ updateConfiguredNetworks();
+ enableAllNetworks();
+ }
+
+ /**
+ * Fetch the list of currently configured networks
+ * @return List of networks
+ */
+ static List<WifiConfiguration> getConfiguredNetworks() {
+ List<WifiConfiguration> networks = new ArrayList<WifiConfiguration>();
+ synchronized (sConfiguredNetworks) {
+ for(WifiConfiguration config : sConfiguredNetworks.values()) {
+ networks.add(config.clone());
+ }
+ }
+ return networks;
+ }
+
+ /**
+ * enable all networks and save config. This will be a no-op if the list
+ * of configured networks indicates all networks as being enabled
+ */
+ static void enableAllNetworks() {
+ synchronized (sConfiguredNetworks) {
+ for(WifiConfiguration config : sConfiguredNetworks.values()) {
+ if(config != null && config.status == Status.DISABLED) {
+ if(WifiNative.enableNetworkCommand(config.networkId, false)) {
+ config.status = Status.ENABLED;
+ } else {
+ Log.e(TAG, "Enable network failed on " + config.networkId);
+ }
+ }
+ }
+ }
+
+ WifiNative.saveConfigCommand();
+ sendConfigChangeBroadcast();
+ }
+
+ /**
+ * Selects the specified network config for connection. This involves
+ * addition/update of the specified config, updating the priority of
+ * all the networks and enabling the given network while disabling others.
+ *
+ * Selecting a network will leave the other networks disabled and
+ * a call to enableAllNetworks() needs to be issued upon a connection
+ * or a failure event from supplicant
+ *
+ * @param config The configuration details in WifiConfiguration
+ */
+ static void selectNetwork(WifiConfiguration config) {
+ if (config != null) {
+ int netId = addOrUpdateNetworkNative(config);
+ if (netId != -1) {
+ selectNetwork(netId);
+ } else {
+ Log.e(TAG, "Failed to update network " + config);
+ }
+ }
+ }
+
+ /**
+ * Selects the specified network for connection. This involves
+ * updating the priority of all the networks and enabling the given
+ * network while disabling others.
+ *
+ * Selecting a network will leave the other networks disabled and
+ * a call to enableAllNetworks() needs to be issued upon a connection
+ * or a failure event from supplicant
+ *
+ * @param netId network to select for connection
+ */
+ static void selectNetwork(int netId) {
+ // Reset the priority of each network at start or if it goes too high.
+ if (sLastPriority == -1 || sLastPriority > 1000000) {
+ synchronized (sConfiguredNetworks) {
+ for(WifiConfiguration config : sConfiguredNetworks.values()) {
+ if (config.networkId != -1) {
+ config.priority = 0;
+ addOrUpdateNetworkNative(config);
+ }
+ }
+ }
+ sLastPriority = 0;
+ }
+
+ // Set to the highest priority and save the configuration.
+ WifiConfiguration config = new WifiConfiguration();
+ config.networkId = netId;
+ config.priority = ++sLastPriority;
+
+ addOrUpdateNetworkNative(config);
+ WifiNative.saveConfigCommand();
+
+ /* Enable the given network while disabling all other networks */
+ enableNetworkWithoutBroadcast(netId, true);
+
+ /* Avoid saving the config & sending a broadcast to prevent settings
+ * from displaying a disabled list of networks */
+ }
+
+ /**
+ * Add/update the specified configuration and save config
+ *
+ * @param config WifiConfiguration to be saved
+ */
+ static void saveNetwork(WifiConfiguration config) {
+ boolean newNetwork = (config.networkId == -1);
+ int netId = addOrUpdateNetworkNative(config);
+ /* enable a new network */
+ if (newNetwork && netId >= 0) {
+ WifiNative.enableNetworkCommand(netId, false);
+ synchronized (sConfiguredNetworks) {
+ sConfiguredNetworks.get(netId).status = Status.ENABLED;
+ }
+ }
+ WifiNative.saveConfigCommand();
+ sendConfigChangeBroadcast();
+ }
+
+ /**
+ * Forget the specified network and save config
+ *
+ * @param netId network to forget
+ */
+ static void forgetNetwork(int netId) {
+ if (WifiNative.removeNetworkCommand(netId)) {
+ WifiNative.saveConfigCommand();
+ synchronized (sConfiguredNetworks) {
+ sConfiguredNetworks.remove(netId);
+ }
+ sendConfigChangeBroadcast();
+ } else {
+ Log.e(TAG, "Failed to remove network " + netId);
+ }
+ }
+
+ /**
+ * Add/update a network. Note that there is no saveConfig operation.
+ * This function is retained for compatibility with the public
+ * API. The more powerful saveNetwork() is used by the
+ * state machine
+ *
+ * @param config wifi configuration to add/update
+ */
+ static int addOrUpdateNetwork(WifiConfiguration config) {
+ int ret = addOrUpdateNetworkNative(config);
+ sendConfigChangeBroadcast();
+ return ret;
+ }
+
+ /**
+ * Remove a network. Note that there is no saveConfig operation.
+ * This function is retained for compatibility with the public
+ * API. The more powerful forgetNetwork() is used by the
+ * state machine for network removal
+ *
+ * @param netId network to be removed
+ */
+ static boolean removeNetwork(int netId) {
+ boolean ret = WifiNative.removeNetworkCommand(netId);
+ synchronized (sConfiguredNetworks) {
+ if (ret) sConfiguredNetworks.remove(netId);
+ }
+ sendConfigChangeBroadcast();
+ return ret;
+ }
+
+ /**
+ * Enable a network. Note that there is no saveConfig operation.
+ * This function is retained for compatibility with the public
+ * API. The more powerful selectNetwork()/saveNetwork() is used by the
+ * state machine for connecting to a network
+ *
+ * @param netId network to be removed
+ */
+ static boolean enableNetwork(int netId, boolean disableOthers) {
+ boolean ret = enableNetworkWithoutBroadcast(netId, disableOthers);
+ sendConfigChangeBroadcast();
+ return ret;
+ }
+
+ static boolean enableNetworkWithoutBroadcast(int netId, boolean disableOthers) {
+ boolean ret = WifiNative.enableNetworkCommand(netId, disableOthers);
+
+ synchronized (sConfiguredNetworks) {
+ WifiConfiguration config = sConfiguredNetworks.get(netId);
+ if (config != null) config.status = Status.ENABLED;
+ }
+
+ if (disableOthers) {
+ synchronized (sConfiguredNetworks) {
+ for(WifiConfiguration config : sConfiguredNetworks.values()) {
+ if(config != null && config.networkId != netId) {
+ config.status = Status.DISABLED;
+ }
+ }
+ }
+ }
+ return ret;
+ }
+
+ /**
+ * Disable a network. Note that there is no saveConfig operation.
+ * @param netId network to be disabled
+ */
+ static boolean disableNetwork(int netId) {
+ boolean ret = WifiNative.disableNetworkCommand(netId);
+ synchronized (sConfiguredNetworks) {
+ WifiConfiguration config = sConfiguredNetworks.get(netId);
+ if (config != null) config.status = Status.DISABLED;
+ }
+ sendConfigChangeBroadcast();
+ return ret;
+ }
+
+ /**
+ * Save the configured networks in supplicant to disk
+ */
+ static boolean saveConfig() {
+ return WifiNative.saveConfigCommand();
+ }
+
+ /**
+ * Fetch the IP configuration for a given network id
+ */
+ static DhcpInfo getIpConfiguration(int netId) {
+ synchronized (sConfiguredNetworks) {
+ WifiConfiguration config = sConfiguredNetworks.get(netId);
+ if (config != null) return new DhcpInfo(config.ipConfig);
+ }
+ return null;
+ }
+
+ /**
+ * Return if the specified network is using static IP
+ */
+ static boolean isUsingStaticIp(int netId) {
+ synchronized (sConfiguredNetworks) {
+ WifiConfiguration config = sConfiguredNetworks.get(netId);
+ if (config != null && config.ipAssignment == IpAssignment.STATIC) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static void sendConfigChangeBroadcast() {
+ if (!ActivityManagerNative.isSystemReady()) return;
+ Intent intent = new Intent(WifiManager.SUPPLICANT_CONFIG_CHANGED_ACTION);
+ sContext.sendBroadcast(intent);
+ }
+
+ private static void updateConfiguredNetworks() {
+ String listStr = WifiNative.listNetworksCommand();
+ sLastPriority = 0;
+
+ synchronized (sConfiguredNetworks) {
+ sConfiguredNetworks.clear();
+ sNetworkIds.clear();
+
+ if (listStr == null)
+ return;
+
+ String[] lines = listStr.split("\n");
+ // Skip the first line, which is a header
+ for (int i = 1; i < lines.length; i++) {
+ String[] result = lines[i].split("\t");
+ // network-id | ssid | bssid | flags
+ WifiConfiguration config = new WifiConfiguration();
+ try {
+ config.networkId = Integer.parseInt(result[0]);
+ } catch(NumberFormatException e) {
+ continue;
+ }
+ if (result.length > 3) {
+ if (result[3].indexOf("[CURRENT]") != -1)
+ config.status = WifiConfiguration.Status.CURRENT;
+ else if (result[3].indexOf("[DISABLED]") != -1)
+ config.status = WifiConfiguration.Status.DISABLED;
+ else
+ config.status = WifiConfiguration.Status.ENABLED;
+ } else {
+ config.status = WifiConfiguration.Status.ENABLED;
+ }
+ readNetworkVariables(config);
+ if (config.priority > sLastPriority) {
+ sLastPriority = config.priority;
+ }
+ sConfiguredNetworks.put(config.networkId, config);
+ sNetworkIds.put(configKey(config), config.networkId);
+ }
+ }
+ readIpConfigurations();
+ }
+
+ private static void writeIpConfigurations() {
+ StringBuilder builder = new StringBuilder();
+ BufferedWriter out = null;
+
+ builder.append(IPCONFIG_FILE_VERSION);
+ builder.append("\n");
+
+ synchronized (sConfiguredNetworks) {
+ for(WifiConfiguration config : sConfiguredNetworks.values()) {
+ if (config.ipAssignment == WifiConfiguration.IpAssignment.STATIC) {
+ builder.append("id=" + configKey(config));
+ builder.append(":");
+ builder.append("ip=" + config.ipConfig.ipAddress);
+ builder.append(":");
+ builder.append("gateway=" + config.ipConfig.gateway);
+ builder.append(":");
+ builder.append("netmask=" + config.ipConfig.netmask);
+ builder.append(":");
+ builder.append("dns1=" + config.ipConfig.dns1);
+ builder.append(":");
+ builder.append("dns2=" + config.ipConfig.dns2);
+ builder.append("\n");
+ }
+ }
+ }
+
+ try {
+ out = new BufferedWriter(new FileWriter(ipConfigFile), builder.length());
+ out.write(builder.toString());
+ } catch (IOException e) {
+ Log.e(TAG, "Error writing data file");
+ return;
+ } finally {
+ if (out != null) {
+ try {
+ out.close();
+ } catch (Exception e) {}
+ }
+ }
+ }
+
+ private static void readIpConfigurations() {
+ File f = new File(ipConfigFile);
+ byte[] buffer;
+ FileInputStream s = null;
+ try {
+ buffer = new byte[(int)f.length()];
+ s = new FileInputStream(f);
+ s.read(buffer);
+ } catch (IOException e) {
+ Log.e(TAG, "Error reading data file");
+ return;
+ } finally {
+ if (s != null) {
+ try {
+ s.close();
+ } catch (Exception e) {}
+ }
+ }
+
+ String data = new String(buffer);
+ if (data == null || data.length() == 0) {
+ Log.d(TAG, "IP configuration file empty");
+ return;
+ }
+
+ String[] parsed = data.split("\n");
+ try {
+ if (Integer.parseInt(parsed[0]) != IPCONFIG_FILE_VERSION) {
+ Log.e(TAG, "Bad version on IP configuration file, ignore read");
+ return;
+ }
+
+ for (String line : parsed) {
+ int hashKey = -1;
+ DhcpInfo ipConfig = new DhcpInfo();
+ String[] keyVals = line.split(":");
+
+ for (String keyVal : keyVals) {
+ String[] keyValPair = keyVal.split("=");
+ if (keyValPair[0].equals("id")) {
+ hashKey = Integer.parseInt(keyValPair[1]);
+ } else if (keyValPair[0].equals("ip")) {
+ ipConfig.ipAddress = Integer.parseInt(keyValPair[1]);
+ } else if (keyValPair[0].equals("gateway")) {
+ ipConfig.gateway = Integer.parseInt(keyValPair[1]);
+ } else if (keyValPair[0].equals("netmask")) {
+ ipConfig.netmask = Integer.parseInt(keyValPair[1]);
+ } else if (keyValPair[0].equals("dns1")) {
+ ipConfig.dns1 = Integer.parseInt(keyValPair[1]);
+ } else if (keyValPair[0].equals("dns2")) {
+ ipConfig.dns2 = Integer.parseInt(keyValPair[1]);
+ } else {
+ Log.w(TAG, "Ignoring " + keyVal);
+ }
+ }
+
+ if (hashKey != -1) {
+ synchronized (sConfiguredNetworks) {
+ WifiConfiguration config = sConfiguredNetworks.get(
+ sNetworkIds.get(hashKey));
+
+ if (config == null) {
+ Log.e(TAG, "IP configuration found for missing network, ignored");
+ } else {
+ config.ipAssignment = WifiConfiguration.IpAssignment.STATIC;
+ config.ipConfig = ipConfig;
+ }
+ }
+ } else {
+ Log.e(TAG,"Missing id while parsing configuration" + line);
+ }
+ }
+ } catch (NumberFormatException e) {
+ Log.e(TAG, "Error parsing configuration");
+ }
+ }
+
+ private static int addOrUpdateNetworkNative(WifiConfiguration config) {
+ /*
+ * If the supplied networkId is -1, we create a new empty
+ * network configuration. Otherwise, the networkId should
+ * refer to an existing configuration.
+ */
+ int netId = config.networkId;
+ boolean updateFailed = true;
+ boolean newNetwork = netId == -1;
+ // networkId of -1 means we want to create a new network
+
+ if (newNetwork) {
+ netId = WifiNative.addNetworkCommand();
+ if (netId < 0) {
+ Log.e(TAG, "Failed to add a network!");
+ return -1;
+ }
+ }
+
+ setVariables: {
+
+ if (config.SSID != null &&
+ !WifiNative.setNetworkVariableCommand(
+ netId,
+ WifiConfiguration.ssidVarName,
+ config.SSID)) {
+ Log.d(TAG, "failed to set SSID: "+config.SSID);
+ break setVariables;
+ }
+
+ if (config.BSSID != null &&
+ !WifiNative.setNetworkVariableCommand(
+ netId,
+ WifiConfiguration.bssidVarName,
+ config.BSSID)) {
+ Log.d(TAG, "failed to set BSSID: "+config.BSSID);
+ break setVariables;
+ }
+
+ String allowedKeyManagementString =
+ makeString(config.allowedKeyManagement, WifiConfiguration.KeyMgmt.strings);
+ if (config.allowedKeyManagement.cardinality() != 0 &&
+ !WifiNative.setNetworkVariableCommand(
+ netId,
+ WifiConfiguration.KeyMgmt.varName,
+ allowedKeyManagementString)) {
+ Log.d(TAG, "failed to set key_mgmt: "+
+ allowedKeyManagementString);
+ break setVariables;
+ }
+
+ String allowedProtocolsString =
+ makeString(config.allowedProtocols, WifiConfiguration.Protocol.strings);
+ if (config.allowedProtocols.cardinality() != 0 &&
+ !WifiNative.setNetworkVariableCommand(
+ netId,
+ WifiConfiguration.Protocol.varName,
+ allowedProtocolsString)) {
+ Log.d(TAG, "failed to set proto: "+
+ allowedProtocolsString);
+ break setVariables;
+ }
+
+ String allowedAuthAlgorithmsString =
+ makeString(config.allowedAuthAlgorithms, WifiConfiguration.AuthAlgorithm.strings);
+ if (config.allowedAuthAlgorithms.cardinality() != 0 &&
+ !WifiNative.setNetworkVariableCommand(
+ netId,
+ WifiConfiguration.AuthAlgorithm.varName,
+ allowedAuthAlgorithmsString)) {
+ Log.d(TAG, "failed to set auth_alg: "+
+ allowedAuthAlgorithmsString);
+ break setVariables;
+ }
+
+ String allowedPairwiseCiphersString =
+ makeString(config.allowedPairwiseCiphers,
+ WifiConfiguration.PairwiseCipher.strings);
+ if (config.allowedPairwiseCiphers.cardinality() != 0 &&
+ !WifiNative.setNetworkVariableCommand(
+ netId,
+ WifiConfiguration.PairwiseCipher.varName,
+ allowedPairwiseCiphersString)) {
+ Log.d(TAG, "failed to set pairwise: "+
+ allowedPairwiseCiphersString);
+ break setVariables;
+ }
+
+ String allowedGroupCiphersString =
+ makeString(config.allowedGroupCiphers, WifiConfiguration.GroupCipher.strings);
+ if (config.allowedGroupCiphers.cardinality() != 0 &&
+ !WifiNative.setNetworkVariableCommand(
+ netId,
+ WifiConfiguration.GroupCipher.varName,
+ allowedGroupCiphersString)) {
+ Log.d(TAG, "failed to set group: "+
+ allowedGroupCiphersString);
+ break setVariables;
+ }
+
+ // Prevent client screw-up by passing in a WifiConfiguration we gave it
+ // by preventing "*" as a key.
+ if (config.preSharedKey != null && !config.preSharedKey.equals("*") &&
+ !WifiNative.setNetworkVariableCommand(
+ netId,
+ WifiConfiguration.pskVarName,
+ config.preSharedKey)) {
+ Log.d(TAG, "failed to set psk: "+config.preSharedKey);
+ break setVariables;
+ }
+
+ boolean hasSetKey = false;
+ if (config.wepKeys != null) {
+ for (int i = 0; i < config.wepKeys.length; i++) {
+ // Prevent client screw-up by passing in a WifiConfiguration we gave it
+ // by preventing "*" as a key.
+ if (config.wepKeys[i] != null && !config.wepKeys[i].equals("*")) {
+ if (!WifiNative.setNetworkVariableCommand(
+ netId,
+ WifiConfiguration.wepKeyVarNames[i],
+ config.wepKeys[i])) {
+ Log.d(TAG,
+ "failed to set wep_key"+i+": " +
+ config.wepKeys[i]);
+ break setVariables;
+ }
+ hasSetKey = true;
+ }
+ }
+ }
+
+ if (hasSetKey) {
+ if (!WifiNative.setNetworkVariableCommand(
+ netId,
+ WifiConfiguration.wepTxKeyIdxVarName,
+ Integer.toString(config.wepTxKeyIndex))) {
+ Log.d(TAG,
+ "failed to set wep_tx_keyidx: "+
+ config.wepTxKeyIndex);
+ break setVariables;
+ }
+ }
+
+ if (!WifiNative.setNetworkVariableCommand(
+ netId,
+ WifiConfiguration.priorityVarName,
+ Integer.toString(config.priority))) {
+ Log.d(TAG, config.SSID + ": failed to set priority: "
+ +config.priority);
+ break setVariables;
+ }
+
+ if (config.hiddenSSID && !WifiNative.setNetworkVariableCommand(
+ netId,
+ WifiConfiguration.hiddenSSIDVarName,
+ Integer.toString(config.hiddenSSID ? 1 : 0))) {
+ Log.d(TAG, config.SSID + ": failed to set hiddenSSID: "+
+ config.hiddenSSID);
+ break setVariables;
+ }
+
+ for (WifiConfiguration.EnterpriseField field
+ : config.enterpriseFields) {
+ String varName = field.varName();
+ String value = field.value();
+ if (value != null) {
+ if (field != config.eap) {
+ value = (value.length() == 0) ? "NULL" : convertToQuotedString(value);
+ }
+ if (!WifiNative.setNetworkVariableCommand(
+ netId,
+ varName,
+ value)) {
+ Log.d(TAG, config.SSID + ": failed to set " + varName +
+ ": " + value);
+ break setVariables;
+ }
+ }
+ }
+ updateFailed = false;
+ }
+
+ if (updateFailed) {
+ if (newNetwork) {
+ WifiNative.removeNetworkCommand(netId);
+ Log.d(TAG,
+ "Failed to set a network variable, removed network: "
+ + netId);
+ }
+ return -1;
+ }
+
+ /* An update of the network variables requires reading them
+ * back from the supplicant to update sConfiguredNetworks.
+ * This is because some of the variables (SSID, wep keys &
+ * passphrases) reflect different values when read back than
+ * when written. For example, wep key is stored as * irrespective
+ * of the value sent to the supplicant
+ */
+ WifiConfiguration sConfig;
+ synchronized (sConfiguredNetworks) {
+ sConfig = sConfiguredNetworks.get(netId);
+ }
+ if (sConfig == null) {
+ sConfig = new WifiConfiguration();
+ sConfig.networkId = netId;
+ synchronized (sConfiguredNetworks) {
+ sConfiguredNetworks.put(netId, sConfig);
+ }
+ }
+ readNetworkVariables(sConfig);
+
+ if (config.ipAssignment != IpAssignment.UNASSIGNED) {
+ if (newNetwork ||
+ (sConfig.ipAssignment != config.ipAssignment) ||
+ (sConfig.ipConfig.ipAddress != config.ipConfig.ipAddress) ||
+ (sConfig.ipConfig.gateway != config.ipConfig.gateway) ||
+ (sConfig.ipConfig.netmask != config.ipConfig.netmask) ||
+ (sConfig.ipConfig.dns1 != config.ipConfig.dns1) ||
+ (sConfig.ipConfig.dns2 != config.ipConfig.dns2)) {
+ sConfig.ipAssignment = config.ipAssignment;
+ sConfig.ipConfig = config.ipConfig;
+ writeIpConfigurations();
+ }
+ }
+ return netId;
+ }
+
+ /**
+ * Read the variables from the supplicant daemon that are needed to
+ * fill in the WifiConfiguration object.
+ *
+ * @param config the {@link WifiConfiguration} object to be filled in.
+ */
+ private static void readNetworkVariables(WifiConfiguration config) {
+
+ int netId = config.networkId;
+ if (netId < 0)
+ return;
+
+ /*
+ * TODO: maybe should have a native method that takes an array of
+ * variable names and returns an array of values. But we'd still
+ * be doing a round trip to the supplicant daemon for each variable.
+ */
+ String value;
+
+ value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.ssidVarName);
+ if (!TextUtils.isEmpty(value)) {
+ config.SSID = removeDoubleQuotes(value);
+ } else {
+ config.SSID = null;
+ }
+
+ value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.bssidVarName);
+ if (!TextUtils.isEmpty(value)) {
+ config.BSSID = value;
+ } else {
+ config.BSSID = null;
+ }
+
+ value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.priorityVarName);
+ config.priority = -1;
+ if (!TextUtils.isEmpty(value)) {
+ try {
+ config.priority = Integer.parseInt(value);
+ } catch (NumberFormatException ignore) {
+ }
+ }
+
+ value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.hiddenSSIDVarName);
+ config.hiddenSSID = false;
+ if (!TextUtils.isEmpty(value)) {
+ try {
+ config.hiddenSSID = Integer.parseInt(value) != 0;
+ } catch (NumberFormatException ignore) {
+ }
+ }
+
+ value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.wepTxKeyIdxVarName);
+ config.wepTxKeyIndex = -1;
+ if (!TextUtils.isEmpty(value)) {
+ try {
+ config.wepTxKeyIndex = Integer.parseInt(value);
+ } catch (NumberFormatException ignore) {
+ }
+ }
+
+ for (int i = 0; i < 4; i++) {
+ value = WifiNative.getNetworkVariableCommand(netId,
+ WifiConfiguration.wepKeyVarNames[i]);
+ if (!TextUtils.isEmpty(value)) {
+ config.wepKeys[i] = value;
+ } else {
+ config.wepKeys[i] = null;
+ }
+ }
+
+ value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.pskVarName);
+ if (!TextUtils.isEmpty(value)) {
+ config.preSharedKey = value;
+ } else {
+ config.preSharedKey = null;
+ }
+
+ value = WifiNative.getNetworkVariableCommand(config.networkId,
+ WifiConfiguration.Protocol.varName);
+ if (!TextUtils.isEmpty(value)) {
+ String vals[] = value.split(" ");
+ for (String val : vals) {
+ int index =
+ lookupString(val, WifiConfiguration.Protocol.strings);
+ if (0 <= index) {
+ config.allowedProtocols.set(index);
+ }
+ }
+ }
+
+ value = WifiNative.getNetworkVariableCommand(config.networkId,
+ WifiConfiguration.KeyMgmt.varName);
+ if (!TextUtils.isEmpty(value)) {
+ String vals[] = value.split(" ");
+ for (String val : vals) {
+ int index =
+ lookupString(val, WifiConfiguration.KeyMgmt.strings);
+ if (0 <= index) {
+ config.allowedKeyManagement.set(index);
+ }
+ }
+ }
+
+ value = WifiNative.getNetworkVariableCommand(config.networkId,
+ WifiConfiguration.AuthAlgorithm.varName);
+ if (!TextUtils.isEmpty(value)) {
+ String vals[] = value.split(" ");
+ for (String val : vals) {
+ int index =
+ lookupString(val, WifiConfiguration.AuthAlgorithm.strings);
+ if (0 <= index) {
+ config.allowedAuthAlgorithms.set(index);
+ }
+ }
+ }
+
+ value = WifiNative.getNetworkVariableCommand(config.networkId,
+ WifiConfiguration.PairwiseCipher.varName);
+ if (!TextUtils.isEmpty(value)) {
+ String vals[] = value.split(" ");
+ for (String val : vals) {
+ int index =
+ lookupString(val, WifiConfiguration.PairwiseCipher.strings);
+ if (0 <= index) {
+ config.allowedPairwiseCiphers.set(index);
+ }
+ }
+ }
+
+ value = WifiNative.getNetworkVariableCommand(config.networkId,
+ WifiConfiguration.GroupCipher.varName);
+ if (!TextUtils.isEmpty(value)) {
+ String vals[] = value.split(" ");
+ for (String val : vals) {
+ int index =
+ lookupString(val, WifiConfiguration.GroupCipher.strings);
+ if (0 <= index) {
+ config.allowedGroupCiphers.set(index);
+ }
+ }
+ }
+
+ for (WifiConfiguration.EnterpriseField field :
+ config.enterpriseFields) {
+ value = WifiNative.getNetworkVariableCommand(netId,
+ field.varName());
+ if (!TextUtils.isEmpty(value)) {
+ if (field != config.eap) value = removeDoubleQuotes(value);
+ field.setValue(value);
+ }
+ }
+ }
+
+ private static String removeDoubleQuotes(String string) {
+ if (string.length() <= 2) return "";
+ return string.substring(1, string.length() - 1);
+ }
+
+ private static String convertToQuotedString(String string) {
+ return "\"" + string + "\"";
+ }
+
+ private static String makeString(BitSet set, String[] strings) {
+ StringBuffer buf = new StringBuffer();
+ int nextSetBit = -1;
+
+ /* Make sure all set bits are in [0, strings.length) to avoid
+ * going out of bounds on strings. (Shouldn't happen, but...) */
+ set = set.get(0, strings.length);
+
+ while ((nextSetBit = set.nextSetBit(nextSetBit + 1)) != -1) {
+ buf.append(strings[nextSetBit].replace('_', '-')).append(' ');
+ }
+
+ // remove trailing space
+ if (set.cardinality() > 0) {
+ buf.setLength(buf.length() - 1);
+ }
+
+ return buf.toString();
+ }
+
+ private static int lookupString(String string, String[] strings) {
+ int size = strings.length;
+
+ string = string.replace('-', '_');
+
+ for (int i = 0; i < size; i++)
+ if (string.equals(strings[i]))
+ return i;
+
+ // if we ever get here, we should probably add the
+ // value to WifiConfiguration to reflect that it's
+ // supported by the WPA supplicant
+ Log.w(TAG, "Failed to look-up a string: " + string);
+
+ return -1;
+ }
+
+ /* Returns a unique for a given configuration */
+ private static int configKey(WifiConfiguration config) {
+ String key;
+
+ if (config.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
+ key = config.SSID + KeyMgmt.strings[KeyMgmt.WPA_PSK];
+ } else if (config.allowedKeyManagement.get(KeyMgmt.WPA_EAP) ||
+ config.allowedKeyManagement.get(KeyMgmt.IEEE8021X)) {
+ key = config.SSID + KeyMgmt.strings[KeyMgmt.WPA_EAP];
+ } else if (config.wepKeys[0] != null) {
+ key = config.SSID + "WEP";
+ } else {
+ key = config.SSID + KeyMgmt.strings[KeyMgmt.NONE];
+ }
+
+ return key.hashCode();
+ }
+
+ static String dump() {
+ StringBuffer sb = new StringBuffer();
+ String LS = System.getProperty("line.separator");
+ sb.append("sLastPriority ").append(sLastPriority).append(LS);
+ sb.append("Configured networks ").append(LS);
+ for (WifiConfiguration conf : getConfiguredNetworks()) {
+ sb.append(conf).append(LS);
+ }
+ return sb.toString();
+ }
+}
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 01bc919..8971bdd 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -16,6 +16,7 @@
package android.net.wifi;
+import android.net.DhcpInfo;
import android.os.Parcelable;
import android.os.Parcel;
@@ -216,7 +217,7 @@ public class WifiConfiguration implements Parcelable {
/**
* The network's SSID. Can either be an ASCII string,
* which must be enclosed in double quotation marks
- * (e.g., {@code &quot;MyNetwork&quot;}, or a string of
+ * (e.g., {@code "MyNetwork"}, or a string of
* hex digits,which are not enclosed in quotes
* (e.g., {@code 01a243f405}).
*/
@@ -239,7 +240,7 @@ public class WifiConfiguration implements Parcelable {
public String preSharedKey;
/**
* Up to four WEP keys. Either an ASCII string enclosed in double
- * quotation marks (e.g., {@code &quot;abcdef&quot;} or a string
+ * quotation marks (e.g., {@code "abcdef"} or a string
* of hex digits (e.g., {@code 0102030405}).
* <p/>
* When the value of one of these keys is read, the actual key is
@@ -294,6 +295,22 @@ public class WifiConfiguration implements Parcelable {
*/
public BitSet allowedGroupCiphers;
+ /**
+ * @hide
+ */
+ public enum IpAssignment {
+ STATIC,
+ DHCP,
+ UNASSIGNED
+ }
+ /**
+ * @hide
+ */
+ public IpAssignment ipAssignment;
+ /**
+ * @hide
+ */
+ public DhcpInfo ipConfig;
public WifiConfiguration() {
networkId = -1;
@@ -312,6 +329,8 @@ public class WifiConfiguration implements Parcelable {
for (EnterpriseField field : enterpriseFields) {
field.setValue(null);
}
+ ipAssignment = IpAssignment.UNASSIGNED;
+ ipConfig = new DhcpInfo();
}
public String toString() {
@@ -393,6 +412,11 @@ public class WifiConfiguration implements Parcelable {
if (value != null) sbuf.append(value);
}
sbuf.append('\n');
+ if (ipAssignment == IpAssignment.STATIC) {
+ sbuf.append(" ").append("Static IP configuration:").append('\n');
+ sbuf.append(" ").append(ipConfig);
+ }
+ sbuf.append('\n');
return sbuf.toString();
}
@@ -432,6 +456,40 @@ public class WifiConfiguration implements Parcelable {
return 0;
}
+ /**
+ * Returns a copy of this WifiConfiguration.
+ *
+ * @return a copy of this WifiConfiguration.
+ * @hide
+ */
+ public WifiConfiguration clone() {
+ WifiConfiguration config = new WifiConfiguration();
+ config.networkId = networkId;
+ config.status = status;
+ config.SSID = SSID;
+ config.BSSID = BSSID;
+ config.preSharedKey = preSharedKey;
+
+ for (int i = 0; i < wepKeys.length; i++)
+ config.wepKeys[i] = wepKeys[i];
+
+ config.wepTxKeyIndex = wepTxKeyIndex;
+ config.priority = priority;
+ config.hiddenSSID = hiddenSSID;
+ config.allowedKeyManagement = (BitSet) allowedKeyManagement.clone();
+ config.allowedProtocols = (BitSet) allowedProtocols.clone();
+ config.allowedAuthAlgorithms = (BitSet) allowedAuthAlgorithms.clone();
+ config.allowedPairwiseCiphers = (BitSet) allowedPairwiseCiphers.clone();
+ config.allowedGroupCiphers = (BitSet) allowedGroupCiphers.clone();
+
+ for (int i = 0; i < enterpriseFields.length; i++) {
+ config.enterpriseFields[i].setValue(enterpriseFields[i].value());
+ }
+ config.ipAssignment = ipAssignment;
+ config.ipConfig = new DhcpInfo(ipConfig);
+ return config;
+ }
+
/** Implement the Parcelable interface {@hide} */
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(networkId);
@@ -454,6 +512,14 @@ public class WifiConfiguration implements Parcelable {
for (EnterpriseField field : enterpriseFields) {
dest.writeString(field.value());
}
+ dest.writeString(ipAssignment.name());
+ dest.writeInt(ipConfig.ipAddress);
+ dest.writeInt(ipConfig.netmask);
+ dest.writeInt(ipConfig.gateway);
+ dest.writeInt(ipConfig.dns1);
+ dest.writeInt(ipConfig.dns2);
+ dest.writeInt(ipConfig.serverAddress);
+ dest.writeInt(ipConfig.leaseDuration);
}
/** Implement the Parcelable interface {@hide} */
@@ -480,6 +546,15 @@ public class WifiConfiguration implements Parcelable {
for (EnterpriseField field : config.enterpriseFields) {
field.setValue(in.readString());
}
+
+ config.ipAssignment = IpAssignment.valueOf(in.readString());
+ config.ipConfig.ipAddress = in.readInt();
+ config.ipConfig.netmask = in.readInt();
+ config.ipConfig.gateway = in.readInt();
+ config.ipConfig.dns1 = in.readInt();
+ config.ipConfig.dns2 = in.readInt();
+ config.ipConfig.serverAddress = in.readInt();
+ config.ipConfig.leaseDuration = in.readInt();
return config;
}
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index dd162f2..4435110 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -166,7 +166,7 @@ public class WifiManager {
*
* @hide
*/
- public static final int WIFI_AP_STATE_DISABLING = 0;
+ public static final int WIFI_AP_STATE_DISABLING = 10;
/**
* Wi-Fi AP is disabled.
*
@@ -175,7 +175,7 @@ public class WifiManager {
*
* @hide
*/
- public static final int WIFI_AP_STATE_DISABLED = 1;
+ public static final int WIFI_AP_STATE_DISABLED = 11;
/**
* Wi-Fi AP is currently being enabled. The state will change to
* {@link #WIFI_AP_STATE_ENABLED} if it finishes successfully.
@@ -185,7 +185,7 @@ public class WifiManager {
*
* @hide
*/
- public static final int WIFI_AP_STATE_ENABLING = 2;
+ public static final int WIFI_AP_STATE_ENABLING = 12;
/**
* Wi-Fi AP is enabled.
*
@@ -194,7 +194,7 @@ public class WifiManager {
*
* @hide
*/
- public static final int WIFI_AP_STATE_ENABLED = 3;
+ public static final int WIFI_AP_STATE_ENABLED = 13;
/**
* Wi-Fi AP is in a failed state. This state will occur when an error occurs during
* enabling or disabling
@@ -204,7 +204,7 @@ public class WifiManager {
*
* @hide
*/
- public static final int WIFI_AP_STATE_FAILED = 4;
+ public static final int WIFI_AP_STATE_FAILED = 14;
/**
* Broadcast intent action indicating that a connection to the supplicant has
@@ -275,7 +275,14 @@ public class WifiManager {
* @see #ERROR_AUTHENTICATING
*/
public static final String EXTRA_SUPPLICANT_ERROR = "supplicantError";
-
+ /**
+ * Broadcast intent action indicating that the supplicant configuration changed.
+ * This can be as a result of adding/updating/deleting a network
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String SUPPLICANT_CONFIG_CHANGED_ACTION =
+ "android.net.wifi.supplicant.CONFIG_CHANGE";
/**
* An access point scan has completed, and results are available from the supplicant.
* Call {@link #getScanResults()} to obtain the results.
@@ -294,11 +301,35 @@ public class WifiManager {
public static final String EXTRA_NEW_RSSI = "newRssi";
/**
+ * Broadcast intent action indicating that the IP configuration
+ * changed on wifi.
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String CONFIG_CHANGED_ACTION = "android.net.wifi.CONFIG_CHANGED";
+
+ /**
+ * The lookup key for a {@link android.net.LinkProperties} object associated with the
+ * Wi-Fi network. Retrieve with
+ * {@link android.content.Intent#getParcelableExtra(String)}.
+ * @hide
+ */
+ public static final String EXTRA_LINK_PROPERTIES = "linkProperties";
+
+ /**
+ * The lookup key for a {@link android.net.LinkCapabilities} object associated with the
+ * Wi-Fi network. Retrieve with
+ * {@link android.content.Intent#getParcelableExtra(String)}.
+ * @hide
+ */
+ public static final String EXTRA_LINK_CAPABILITIES = "linkCapabilities";
+
+ /**
* 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.
@@ -308,16 +339,6 @@ public class WifiManager {
public static final String ACTION_PICK_WIFI_NETWORK = "android.net.wifi.PICK_WIFI_NETWORK";
/**
- * In this Wi-Fi lock mode, Wi-Fi will behave as in the mode
- * {@link #WIFI_MODE_FULL} but it operates at high performance
- * at the expense of power. This mode should be used
- * only when the wifi connection needs to have minimum loss and low
- * latency as it can impact the battery life.
- * @hide
- */
- public static final int WIFI_MODE_FULL_HIGH_PERF = 3;
-
- /**
* 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
@@ -513,7 +534,8 @@ public class WifiManager {
*/
public boolean disconnect() {
try {
- return mService.disconnect();
+ mService.disconnect();
+ return true;
} catch (RemoteException e) {
return false;
}
@@ -527,7 +549,8 @@ public class WifiManager {
*/
public boolean reconnect() {
try {
- return mService.reconnect();
+ mService.reconnect();
+ return true;
} catch (RemoteException e) {
return false;
}
@@ -541,7 +564,8 @@ public class WifiManager {
*/
public boolean reassociate() {
try {
- return mService.reassociate();
+ mService.reassociate();
+ return true;
} catch (RemoteException e) {
return false;
}
@@ -570,7 +594,8 @@ public class WifiManager {
*/
public boolean startScan() {
try {
- return mService.startScan(false);
+ mService.startScan(false);
+ return true;
} catch (RemoteException e) {
return false;
}
@@ -588,7 +613,8 @@ public class WifiManager {
*/
public boolean startScanActive() {
try {
- return mService.startScan(true);
+ mService.startScan(true);
+ return true;
} catch (RemoteException e) {
return false;
}
@@ -754,8 +780,9 @@ public class WifiManager {
} else if (rssi >= MAX_RSSI) {
return numLevels - 1;
} else {
- int partitionSize = (MAX_RSSI - MIN_RSSI) / (numLevels - 1);
- return (rssi - MIN_RSSI) / partitionSize;
+ float inputRange = (MAX_RSSI - MIN_RSSI);
+ float outputRange = (numLevels - 1);
+ return (int)((float)(rssi - MIN_RSSI) * outputRange / inputRange);
}
}
@@ -849,6 +876,166 @@ public class WifiManager {
}
}
+ /**
+ * Start the driver and connect to network.
+ *
+ * This function will over-ride WifiLock and device idle status. For example,
+ * even if the device is idle or there is only a scan-only lock held,
+ * a start wifi would mean that wifi connection is kept active until
+ * a stopWifi() is sent.
+ *
+ * This API is used by WifiStateTracker
+ *
+ * @return {@code true} if the operation succeeds else {@code false}
+ * @hide
+ */
+ public boolean startWifi() {
+ try {
+ mService.startWifi();
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Disconnect from a network (if any) and stop the driver.
+ *
+ * This function will over-ride WifiLock and device idle status. Wi-Fi
+ * stays inactive until a startWifi() is issued.
+ *
+ * This API is used by WifiStateTracker
+ *
+ * @return {@code true} if the operation succeeds else {@code false}
+ * @hide
+ */
+ public boolean stopWifi() {
+ try {
+ mService.stopWifi();
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Add a bssid to the supplicant blacklist
+ *
+ * This API is used by WifiWatchdogService
+ *
+ * @return {@code true} if the operation succeeds else {@code false}
+ * @hide
+ */
+ public boolean addToBlacklist(String bssid) {
+ try {
+ mService.addToBlacklist(bssid);
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Clear the supplicant blacklist
+ *
+ * This API is used by WifiWatchdogService
+ *
+ * @return {@code true} if the operation succeeds else {@code false}
+ * @hide
+ */
+ public boolean clearBlacklist() {
+ try {
+ mService.clearBlacklist();
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /* TODO: deprecate synchronous API and open up the following API */
+ /**
+ * Connect to a network with the given configuration. The network also
+ * gets added to the supplicant configuration.
+ *
+ * For a new network, this function is used instead of a
+ * sequence of addNetwork(), enableNetwork(), saveConfiguration() and
+ * reconnect()
+ *
+ * @param config the set of variables that describe the configuration,
+ * contained in a {@link WifiConfiguration} object.
+ * @hide
+ */
+ public void connectNetwork(WifiConfiguration config) {
+ if (config == null) {
+ return;
+ }
+ try {
+ mService.connectNetworkWithConfig(config);
+ } catch (RemoteException e) { }
+ }
+
+ /**
+ * Connect to a network with the given networkId.
+ *
+ * This function is used instead of a enableNetwork(), saveConfiguration() and
+ * reconnect()
+ *
+ * @param networkId the network id identifiying the network in the
+ * supplicant configuration list
+ * @hide
+ */
+ public void connectNetwork(int networkId) {
+ if (networkId < 0) {
+ return;
+ }
+ try {
+ mService.connectNetworkWithId(networkId);
+ } catch (RemoteException e) { }
+ }
+
+ /**
+ * Save the given network in the supplicant config. If the network already
+ * exists, the configuration is updated. A new network is enabled
+ * by default.
+ *
+ * For a new network, this function is used instead of a
+ * sequence of addNetwork(), enableNetwork() and saveConfiguration().
+ *
+ * For an existing network, it accomplishes the task of updateNetwork()
+ * and saveConfiguration()
+ *
+ * @param config the set of variables that describe the configuration,
+ * contained in a {@link WifiConfiguration} object.
+ * @hide
+ */
+ public void saveNetwork(WifiConfiguration config) {
+ if (config == null) {
+ return;
+ }
+ try {
+ mService.saveNetwork(config);
+ } catch (RemoteException e) { }
+ }
+
+ /**
+ * Delete the network in the supplicant config.
+ *
+ * This function is used instead of a sequence of removeNetwork()
+ * and saveConfiguration().
+ *
+ * @param config the set of variables that describe the configuration,
+ * contained in a {@link WifiConfiguration} object.
+ * @hide
+ */
+ public void forgetNetwork(int netId) {
+ if (netId < 0) {
+ return;
+ }
+ try {
+ mService.forgetNetwork(netId);
+ } catch (RemoteException e) { }
+ }
+
/**
* 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.
@@ -1031,9 +1218,8 @@ public class WifiManager {
/**
* 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 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
diff --git a/wifi/java/android/net/wifi/WifiMonitor.java b/wifi/java/android/net/wifi/WifiMonitor.java
index 266d801..af3132f 100644
--- a/wifi/java/android/net/wifi/WifiMonitor.java
+++ b/wifi/java/android/net/wifi/WifiMonitor.java
@@ -19,14 +19,13 @@ package android.net.wifi;
import android.util.Log;
import android.util.Config;
import android.net.NetworkInfo;
-import android.net.NetworkStateTracker;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
/**
* Listens for events from the wpa_supplicant server, and passes them on
- * to the {@link WifiStateTracker} for handling. Runs in its own thread.
+ * to the {@link WifiStateMachine} for handling. Runs in its own thread.
*
* @hide
*/
@@ -117,7 +116,7 @@ public class WifiMonitor {
private static Pattern mConnectedEventPattern =
Pattern.compile("((?:[0-9a-f]{2}:){5}[0-9a-f]{2}) .* \\[id=([0-9]+) ");
- private final WifiStateTracker mWifiStateTracker;
+ private final WifiStateMachine mWifiStateMachine;
/**
* This indicates the supplicant connection for the monitor is closed
@@ -139,31 +138,27 @@ public class WifiMonitor {
*/
private static final int MAX_RECV_ERRORS = 10;
- public WifiMonitor(WifiStateTracker tracker) {
- mWifiStateTracker = tracker;
+ public WifiMonitor(WifiStateMachine wifiStateMachine) {
+ mWifiStateMachine = wifiStateMachine;
}
public void startMonitoring() {
new MonitorThread().start();
}
- public NetworkStateTracker getNetworkStateTracker() {
- return mWifiStateTracker;
- }
-
class MonitorThread extends Thread {
public MonitorThread() {
super("WifiMonitor");
}
-
+
public void run() {
if (connectToSupplicant()) {
// Send a message indicating that it is now possible to send commands
// to the supplicant
- mWifiStateTracker.notifySupplicantConnection();
+ mWifiStateMachine.notifySupplicantConnection();
} else {
- mWifiStateTracker.notifySupplicantLost();
+ mWifiStateMachine.notifySupplicantLost();
return;
}
@@ -259,7 +254,7 @@ public class WifiMonitor {
}
// notify and exit
- mWifiStateTracker.notifySupplicantLost();
+ mWifiStateMachine.notifySupplicantLost();
break;
} else {
handleEvent(event, eventData);
@@ -272,7 +267,7 @@ public class WifiMonitor {
int connectTries = 0;
while (true) {
- if (mWifiStateTracker.connectToSupplicant()) {
+ if (WifiNative.connectToSupplicant()) {
return true;
}
if (connectTries++ < 3) {
@@ -285,7 +280,7 @@ public class WifiMonitor {
}
private void handlePasswordKeyMayBeIncorrect() {
- mWifiStateTracker.notifyPasswordKeyMayBeIncorrect();
+ mWifiStateMachine.notifyPasswordKeyMayBeIncorrect();
}
private void handleDriverEvent(String state) {
@@ -293,11 +288,11 @@ public class WifiMonitor {
return;
}
if (state.equals("STOPPED")) {
- mWifiStateTracker.notifyDriverStopped();
+ mWifiStateMachine.notifyDriverStopped();
} else if (state.equals("STARTED")) {
- mWifiStateTracker.notifyDriverStarted();
+ mWifiStateMachine.notifyDriverStarted();
} else if (state.equals("HANGED")) {
- mWifiStateTracker.notifyDriverHung();
+ mWifiStateMachine.notifyDriverHung();
}
}
@@ -318,7 +313,7 @@ public class WifiMonitor {
break;
case SCAN_RESULTS:
- mWifiStateTracker.notifyScanResultsAvailable();
+ mWifiStateMachine.notifyScanResultsAvailable();
break;
case UNKNOWN:
@@ -375,7 +370,7 @@ public class WifiMonitor {
if (newSupplicantState == SupplicantState.INVALID) {
Log.w(TAG, "Invalid supplicant state: " + newState);
}
- mWifiStateTracker.notifyStateChange(networkId, BSSID, newSupplicantState);
+ mWifiStateMachine.notifySupplicantStateChange(networkId, BSSID, newSupplicantState);
}
}
@@ -395,7 +390,7 @@ public class WifiMonitor {
}
}
}
- mWifiStateTracker.notifyStateChange(newState, BSSID, networkId);
+ mWifiStateMachine.notifyNetworkStateChange(newState, BSSID, networkId);
}
/**
diff --git a/wifi/java/android/net/wifi/WifiNative.java b/wifi/java/android/net/wifi/WifiNative.java
index 25f05c0..862a61b 100644
--- a/wifi/java/android/net/wifi/WifiNative.java
+++ b/wifi/java/android/net/wifi/WifiNative.java
@@ -41,6 +41,8 @@ public class WifiNative {
public native static String getErrorString(int errorCode);
public native static boolean loadDriver();
+
+ public native static boolean isDriverLoaded();
public native static boolean unloadDriver();
@@ -109,6 +111,10 @@ public class WifiNative {
public native static boolean setPowerModeCommand(int mode);
+ public native static int getBandCommand();
+
+ public native static boolean setBandCommand(int band);
+
public native static int getPowerModeCommand();
public native static boolean setNumAllowedChannelsCommand(int numChannels);
@@ -149,8 +155,6 @@ public class WifiNative {
public native static String getDhcpError();
- public native static boolean setSuspendOptimizationsCommand(boolean enabled);
-
/**
* Wait for the supplicant to send an event, returning the event string.
* @return the event string sent by the supplicant.
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
new file mode 100644
index 0000000..6fe7529
--- /dev/null
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -0,0 +1,3101 @@
+/*
+ * Copyright (C) 2010 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 static android.net.wifi.WifiManager.WIFI_STATE_DISABLED;
+import static android.net.wifi.WifiManager.WIFI_STATE_DISABLING;
+import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED;
+import static android.net.wifi.WifiManager.WIFI_STATE_ENABLING;
+import static android.net.wifi.WifiManager.WIFI_STATE_UNKNOWN;
+
+/**
+ * TODO: Add soft AP states as part of WIFI_STATE_XXX
+ * Retain WIFI_STATE_ENABLING that indicates driver is loading
+ * Add WIFI_STATE_AP_ENABLED to indicate soft AP has started
+ * and WIFI_STATE_FAILED for failure
+ * Deprecate WIFI_STATE_UNKNOWN
+ *
+ * Doing this will simplify the logic for sending broadcasts
+ */
+import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED;
+import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLING;
+import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
+import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLING;
+import static android.net.wifi.WifiManager.WIFI_AP_STATE_FAILED;
+
+import android.app.ActivityManagerNative;
+import android.net.NetworkInfo;
+import android.net.DhcpInfo;
+import android.net.NetworkUtils;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo.DetailedState;
+import android.net.LinkProperties;
+import android.net.wifi.WifiConfiguration.Status;
+import android.os.Binder;
+import android.os.Message;
+import android.os.Parcelable;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.INetworkManagementService;
+import android.os.PowerManager;
+import android.os.SystemProperties;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.Process;
+import android.os.WorkSource;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.EventLog;
+import android.util.Log;
+import android.util.Slog;
+import android.app.backup.IBackupManager;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHeadset;
+import android.bluetooth.BluetoothA2dp;
+import android.content.ContentResolver;
+import android.content.Intent;
+import android.content.Context;
+import android.database.ContentObserver;
+import com.android.internal.app.IBatteryStats;
+import com.android.internal.util.HierarchicalState;
+import com.android.internal.util.HierarchicalStateMachine;
+
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.regex.Pattern;
+
+/**
+ * Track the state of Wifi connectivity. All event handling is done here,
+ * and all changes in connectivity state are initiated here.
+ *
+ * @hide
+ */
+//TODO: we still need frequent scanning for the case when
+// we issue disconnect but need scan results for open network notification
+public class WifiStateMachine extends HierarchicalStateMachine {
+
+ private static final String TAG = "WifiStateMachine";
+ private static final String NETWORKTYPE = "WIFI";
+ private static final boolean DBG = false;
+
+ /* TODO: fetch a configurable interface */
+ private static final String SOFTAP_IFACE = "wl0.1";
+
+ private WifiMonitor mWifiMonitor;
+ private INetworkManagementService nwService;
+ private ConnectivityManager mCm;
+
+ /* Scan results handling */
+ private List<ScanResult> mScanResults;
+ private static final Pattern scanResultPattern = Pattern.compile("\t+");
+ private static final int SCAN_RESULT_CACHE_SIZE = 80;
+ private final LinkedHashMap<String, ScanResult> mScanResultCache;
+
+ private String mInterfaceName;
+
+ private int mNumAllowedChannels = 0;
+ private int mLastSignalLevel = -1;
+ private String mLastBssid;
+ private int mLastNetworkId;
+ private boolean mEnableRssiPolling = false;
+ private boolean mPasswordKeyMayBeIncorrect = false;
+ private int mReconnectCount = 0;
+ private boolean mIsScanMode = false;
+
+ /**
+ * Instance of the bluetooth headset helper. This needs to be created
+ * early because there is a delay before it actually 'connects', as
+ * noted by its javadoc. If we check before it is connected, it will be
+ * in an error state and we will not disable coexistence.
+ */
+ private BluetoothHeadset mBluetoothHeadset;
+
+ private BluetoothA2dp mBluetoothA2dp;
+
+ private LinkProperties mLinkProperties;
+
+ // Held during driver load and unload
+ private static PowerManager.WakeLock sWakeLock;
+
+ private Context mContext;
+
+ private DhcpInfo mDhcpInfo;
+ private WifiInfo mWifiInfo;
+ private NetworkInfo mNetworkInfo;
+ private SupplicantStateTracker mSupplicantStateTracker;
+ /* Connection to a specific network involves disabling all networks,
+ * this flag tracks if networks need to be re-enabled */
+ private boolean mEnableAllNetworks = false;
+
+
+ // Event log tags (must be in sync with event-log-tags)
+ private static final int EVENTLOG_WIFI_STATE_CHANGED = 50021;
+ private static final int EVENTLOG_WIFI_EVENT_HANDLED = 50022;
+ private static final int EVENTLOG_SUPPLICANT_STATE_CHANGED = 50023;
+
+ /* Load the driver */
+ private static final int CMD_LOAD_DRIVER = 1;
+ /* Unload the driver */
+ private static final int CMD_UNLOAD_DRIVER = 2;
+ /* Indicates driver load succeeded */
+ private static final int CMD_LOAD_DRIVER_SUCCESS = 3;
+ /* Indicates driver load failed */
+ private static final int CMD_LOAD_DRIVER_FAILURE = 4;
+ /* Indicates driver unload succeeded */
+ private static final int CMD_UNLOAD_DRIVER_SUCCESS = 5;
+ /* Indicates driver unload failed */
+ private static final int CMD_UNLOAD_DRIVER_FAILURE = 6;
+
+ /* Start the supplicant */
+ private static final int CMD_START_SUPPLICANT = 11;
+ /* Stop the supplicant */
+ private static final int CMD_STOP_SUPPLICANT = 12;
+ /* Start the driver */
+ private static final int CMD_START_DRIVER = 13;
+ /* Start the driver */
+ private static final int CMD_STOP_DRIVER = 14;
+ /* Indicates DHCP succeded */
+ private static final int CMD_IP_CONFIG_SUCCESS = 15;
+ /* Indicates DHCP failed */
+ private static final int CMD_IP_CONFIG_FAILURE = 16;
+ /* Re-configure interface */
+ private static final int CMD_RECONFIGURE_IP = 17;
+
+
+ /* Start the soft access point */
+ private static final int CMD_START_AP = 21;
+ /* Stop the soft access point */
+ private static final int CMD_STOP_AP = 22;
+
+
+ /* Supplicant events */
+ /* Connection to supplicant established */
+ private static final int SUP_CONNECTION_EVENT = 31;
+ /* Connection to supplicant lost */
+ private static final int SUP_DISCONNECTION_EVENT = 32;
+ /* Driver start completed */
+ private static final int DRIVER_START_EVENT = 33;
+ /* Driver stop completed */
+ private static final int DRIVER_STOP_EVENT = 34;
+ /* Network connection completed */
+ private static final int NETWORK_CONNECTION_EVENT = 36;
+ /* Network disconnection completed */
+ private static final int NETWORK_DISCONNECTION_EVENT = 37;
+ /* Scan results are available */
+ private static final int SCAN_RESULTS_EVENT = 38;
+ /* Supplicate state changed */
+ private static final int SUPPLICANT_STATE_CHANGE_EVENT = 39;
+ /* Password may be incorrect */
+ private static final int PASSWORD_MAY_BE_INCORRECT_EVENT = 40;
+
+ /* Supplicant commands */
+ /* Is supplicant alive ? */
+ private static final int CMD_PING_SUPPLICANT = 51;
+ /* Add/update a network configuration */
+ private static final int CMD_ADD_OR_UPDATE_NETWORK = 52;
+ /* Delete a network */
+ private static final int CMD_REMOVE_NETWORK = 53;
+ /* Enable a network. The device will attempt a connection to the given network. */
+ private static final int CMD_ENABLE_NETWORK = 54;
+ /* Disable a network. The device does not attempt a connection to the given network. */
+ private static final int CMD_DISABLE_NETWORK = 55;
+ /* Blacklist network. De-prioritizes the given BSSID for connection. */
+ private static final int CMD_BLACKLIST_NETWORK = 56;
+ /* Clear the blacklist network list */
+ private static final int CMD_CLEAR_BLACKLIST = 57;
+ /* Save configuration */
+ private static final int CMD_SAVE_CONFIG = 58;
+
+ /* Supplicant commands after driver start*/
+ /* Initiate a scan */
+ private static final int CMD_START_SCAN = 71;
+ /* Set scan mode. CONNECT_MODE or SCAN_ONLY_MODE */
+ private static final int CMD_SET_SCAN_MODE = 72;
+ /* Set scan type. SCAN_ACTIVE or SCAN_PASSIVE */
+ private static final int CMD_SET_SCAN_TYPE = 73;
+ /* Disconnect from a network */
+ private static final int CMD_DISCONNECT = 74;
+ /* Reconnect to a network */
+ private static final int CMD_RECONNECT = 75;
+ /* Reassociate to a network */
+ private static final int CMD_REASSOCIATE = 76;
+ /* Set power mode
+ * POWER_MODE_ACTIVE
+ * POWER_MODE_AUTO
+ */
+ private static final int CMD_SET_POWER_MODE = 77;
+ /* Set bluetooth co-existence
+ * BLUETOOTH_COEXISTENCE_MODE_ENABLED
+ * BLUETOOTH_COEXISTENCE_MODE_DISABLED
+ * BLUETOOTH_COEXISTENCE_MODE_SENSE
+ */
+ private static final int CMD_SET_BLUETOOTH_COEXISTENCE = 78;
+ /* Enable/disable bluetooth scan mode
+ * true(1)
+ * false(0)
+ */
+ private static final int CMD_SET_BLUETOOTH_SCAN_MODE = 79;
+ /* Set number of allowed channels */
+ private static final int CMD_SET_NUM_ALLOWED_CHANNELS = 80;
+ /* Request connectivity manager wake lock before driver stop */
+ private static final int CMD_REQUEST_CM_WAKELOCK = 81;
+ /* Enables RSSI poll */
+ private static final int CMD_ENABLE_RSSI_POLL = 82;
+ /* RSSI poll */
+ private static final int CMD_RSSI_POLL = 83;
+ /* Get current RSSI */
+ private static final int CMD_GET_RSSI = 84;
+ /* Get approx current RSSI */
+ private static final int CMD_GET_RSSI_APPROX = 85;
+ /* Get link speed on connection */
+ private static final int CMD_GET_LINK_SPEED = 86;
+ /* Radio mac address */
+ private static final int CMD_GET_MAC_ADDR = 87;
+ /* Set up packet filtering */
+ private static final int CMD_START_PACKET_FILTERING = 88;
+ /* Clear packet filter */
+ private static final int CMD_STOP_PACKET_FILTERING = 89;
+ /* Connect to a specified network (network id
+ * or WifiConfiguration) This involves increasing
+ * the priority of the network, enabling the network
+ * (while disabling others) and issuing a reconnect.
+ * Note that CMD_RECONNECT just does a reconnect to
+ * an existing network. All the networks get enabled
+ * upon a successful connection or a failure.
+ */
+ private static final int CMD_CONNECT_NETWORK = 90;
+ /* Save the specified network. This involves adding
+ * an enabled network (if new) and updating the
+ * config and issuing a save on supplicant config.
+ */
+ private static final int CMD_SAVE_NETWORK = 91;
+ /* Delete the specified network. This involves
+ * removing the network and issuing a save on
+ * supplicant config.
+ */
+ private static final int CMD_FORGET_NETWORK = 92;
+
+
+
+ /**
+ * Interval in milliseconds between polling for connection
+ * status items that are not sent via asynchronous events.
+ * An example is RSSI (signal strength).
+ */
+ private static final int POLL_RSSI_INTERVAL_MSECS = 3000;
+
+ private static final int CONNECT_MODE = 1;
+ private static final int SCAN_ONLY_MODE = 2;
+
+ private static final int SCAN_ACTIVE = 1;
+ private static final int SCAN_PASSIVE = 2;
+
+ /* Auto allows 802.11A/B/G operation */
+ private static final int BAND_AUTO = 0;
+ /* 5GHz allows 802.11A operation */
+ private static final int BAND_5G = 1;
+ /* 2.4GHz allows 802.11B/G operation */
+ private static final int BAND_2G = 2;
+
+ /**
+ * The maximum number of times we will retry a connection to an access point
+ * for which we have failed in acquiring an IP address from DHCP. A value of
+ * N means that we will make N+1 connection attempts in all.
+ * <p>
+ * See {@link Settings.Secure#WIFI_MAX_DHCP_RETRY_COUNT}. This is the default
+ * value if a Settings value is not present.
+ */
+ private static final int DEFAULT_MAX_DHCP_RETRIES = 9;
+
+ private static final int DRIVER_POWER_MODE_ACTIVE = 1;
+ private static final int DRIVER_POWER_MODE_AUTO = 0;
+
+ /* Default parent state */
+ private HierarchicalState mDefaultState = new DefaultState();
+ /* Temporary initial state */
+ private HierarchicalState mInitialState = new InitialState();
+ /* Unloading the driver */
+ private HierarchicalState mDriverUnloadingState = new DriverUnloadingState();
+ /* Loading the driver */
+ private HierarchicalState mDriverUnloadedState = new DriverUnloadedState();
+ /* Driver load/unload failed */
+ private HierarchicalState mDriverFailedState = new DriverFailedState();
+ /* Driver loading */
+ private HierarchicalState mDriverLoadingState = new DriverLoadingState();
+ /* Driver loaded */
+ private HierarchicalState mDriverLoadedState = new DriverLoadedState();
+ /* Driver loaded, waiting for supplicant to start */
+ private HierarchicalState mWaitForSupState = new WaitForSupState();
+
+ /* Driver loaded and supplicant ready */
+ private HierarchicalState mDriverSupReadyState = new DriverSupReadyState();
+ /* Driver start issued, waiting for completed event */
+ private HierarchicalState mDriverStartingState = new DriverStartingState();
+ /* Driver started */
+ private HierarchicalState mDriverStartedState = new DriverStartedState();
+ /* Driver stopping */
+ private HierarchicalState mDriverStoppingState = new DriverStoppingState();
+ /* Driver stopped */
+ private HierarchicalState mDriverStoppedState = new DriverStoppedState();
+ /* Scan for networks, no connection will be established */
+ private HierarchicalState mScanModeState = new ScanModeState();
+ /* Connecting to an access point */
+ private HierarchicalState mConnectModeState = new ConnectModeState();
+ /* Fetching IP after network connection (assoc+auth complete) */
+ private HierarchicalState mConnectingState = new ConnectingState();
+ /* Connected with IP addr */
+ private HierarchicalState mConnectedState = new ConnectedState();
+ /* disconnect issued, waiting for network disconnect confirmation */
+ private HierarchicalState mDisconnectingState = new DisconnectingState();
+ /* Network is not connected, supplicant assoc+auth is not complete */
+ private HierarchicalState mDisconnectedState = new DisconnectedState();
+
+ /* Soft Ap is running */
+ private HierarchicalState mSoftApStartedState = new SoftApStartedState();
+
+
+ /**
+ * One of {@link WifiManager#WIFI_STATE_DISABLED},
+ * {@link WifiManager#WIFI_STATE_DISABLING},
+ * {@link WifiManager#WIFI_STATE_ENABLED},
+ * {@link WifiManager#WIFI_STATE_ENABLING},
+ * {@link WifiManager#WIFI_STATE_UNKNOWN}
+ *
+ */
+ private final AtomicInteger mWifiState = new AtomicInteger(WIFI_STATE_DISABLED);
+
+ /**
+ * One of {@link WifiManager#WIFI_AP_STATE_DISABLED},
+ * {@link WifiManager#WIFI_AP_STATE_DISABLING},
+ * {@link WifiManager#WIFI_AP_STATE_ENABLED},
+ * {@link WifiManager#WIFI_AP_STATE_ENABLING},
+ * {@link WifiManager#WIFI_AP_STATE_FAILED}
+ *
+ */
+ private final AtomicInteger mWifiApState = new AtomicInteger(WIFI_AP_STATE_DISABLED);
+
+ private final AtomicInteger mLastEnableUid = new AtomicInteger(Process.myUid());
+ private final AtomicInteger mLastApEnableUid = new AtomicInteger(Process.myUid());
+
+ /**
+ * Keep track of whether WIFI is running.
+ */
+ private boolean mIsRunning = false;
+
+ /**
+ * Keep track of whether we last told the battery stats we had started.
+ */
+ private boolean mReportedRunning = false;
+
+ /**
+ * Most recently set source of starting WIFI.
+ */
+ private final WorkSource mRunningWifiUids = new WorkSource();
+
+ /**
+ * The last reported UIDs that were responsible for starting WIFI.
+ */
+ private final WorkSource mLastRunningWifiUids = new WorkSource();
+
+ private final IBatteryStats mBatteryStats;
+
+ public WifiStateMachine(Context context) {
+ super(TAG);
+
+ mContext = context;
+
+ mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, NETWORKTYPE, "");
+ mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo"));
+
+ IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+ nwService = INetworkManagementService.Stub.asInterface(b);
+
+ mWifiMonitor = new WifiMonitor(this);
+ mDhcpInfo = new DhcpInfo();
+ mWifiInfo = new WifiInfo();
+ mInterfaceName = SystemProperties.get("wifi.interface", "tiwlan0");
+ mSupplicantStateTracker = new SupplicantStateTracker(context, getHandler());
+
+ mBluetoothHeadset = new BluetoothHeadset(mContext, null);
+ mLinkProperties = new LinkProperties();
+
+ mNetworkInfo.setIsAvailable(false);
+ mLinkProperties.clear();
+ mLastBssid = null;
+ mLastNetworkId = -1;
+ mLastSignalLevel = -1;
+
+ mScanResultCache = new LinkedHashMap<String, ScanResult>(
+ SCAN_RESULT_CACHE_SIZE, 0.75f, true) {
+ /*
+ * Limit the cache size by SCAN_RESULT_CACHE_SIZE
+ * elements
+ */
+ @Override
+ public boolean removeEldestEntry(Map.Entry eldest) {
+ return SCAN_RESULT_CACHE_SIZE < this.size();
+ }
+ };
+
+ PowerManager powerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
+ sWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
+
+ addState(mDefaultState);
+ addState(mInitialState, mDefaultState);
+ addState(mDriverUnloadingState, mDefaultState);
+ addState(mDriverUnloadedState, mDefaultState);
+ addState(mDriverFailedState, mDriverUnloadedState);
+ addState(mDriverLoadingState, mDefaultState);
+ addState(mDriverLoadedState, mDefaultState);
+ addState(mWaitForSupState, mDriverLoadedState);
+ addState(mDriverSupReadyState, mDefaultState);
+ addState(mDriverStartingState, mDriverSupReadyState);
+ addState(mDriverStartedState, mDriverSupReadyState);
+ addState(mScanModeState, mDriverStartedState);
+ addState(mConnectModeState, mDriverStartedState);
+ addState(mConnectingState, mConnectModeState);
+ addState(mConnectedState, mConnectModeState);
+ addState(mDisconnectingState, mConnectModeState);
+ addState(mDisconnectedState, mConnectModeState);
+ addState(mDriverStoppingState, mDriverSupReadyState);
+ addState(mDriverStoppedState, mDriverSupReadyState);
+ addState(mSoftApStartedState, mDefaultState);
+
+ setInitialState(mInitialState);
+
+ if (DBG) setDbg(true);
+
+ //start the state machine
+ start();
+ }
+
+ /*********************************************************
+ * Methods exposed for public use
+ ********************************************************/
+
+ /**
+ * TODO: doc
+ */
+ public boolean syncPingSupplicant() {
+ return sendSyncMessage(CMD_PING_SUPPLICANT).boolValue;
+ }
+
+ /**
+ * TODO: doc
+ */
+ public void startScan(boolean forceActive) {
+ sendMessage(obtainMessage(CMD_START_SCAN, forceActive ?
+ SCAN_ACTIVE : SCAN_PASSIVE, 0));
+ }
+
+ /**
+ * TODO: doc
+ */
+ public void setWifiEnabled(boolean enable) {
+ mLastEnableUid.set(Binder.getCallingUid());
+ if (enable) {
+ /* Argument is the state that is entered prior to load */
+ sendMessage(obtainMessage(CMD_LOAD_DRIVER, WIFI_STATE_ENABLING, 0));
+ sendMessage(CMD_START_SUPPLICANT);
+ } else {
+ sendMessage(CMD_STOP_SUPPLICANT);
+ /* Argument is the state that is entered upon success */
+ sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_STATE_DISABLED, 0));
+ }
+ }
+
+ /**
+ * TODO: doc
+ */
+ public void setWifiApEnabled(WifiConfiguration wifiConfig, boolean enable) {
+ mLastApEnableUid.set(Binder.getCallingUid());
+ if (enable) {
+ /* Argument is the state that is entered prior to load */
+ sendMessage(obtainMessage(CMD_LOAD_DRIVER, WIFI_AP_STATE_ENABLING, 0));
+ sendMessage(obtainMessage(CMD_START_AP, wifiConfig));
+ } else {
+ sendMessage(CMD_STOP_AP);
+ /* Argument is the state that is entered upon success */
+ sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_AP_STATE_DISABLED, 0));
+ }
+ }
+
+ /**
+ * TODO: doc
+ */
+ public int syncGetWifiState() {
+ return mWifiState.get();
+ }
+
+ /**
+ * TODO: doc
+ */
+ public String syncGetWifiStateByName() {
+ switch (mWifiState.get()) {
+ case WIFI_STATE_DISABLING:
+ return "disabling";
+ case WIFI_STATE_DISABLED:
+ return "disabled";
+ case WIFI_STATE_ENABLING:
+ return "enabling";
+ case WIFI_STATE_ENABLED:
+ return "enabled";
+ case WIFI_STATE_UNKNOWN:
+ return "unknown state";
+ default:
+ return "[invalid state]";
+ }
+ }
+
+ /**
+ * TODO: doc
+ */
+ public int syncGetWifiApState() {
+ return mWifiApState.get();
+ }
+
+ /**
+ * TODO: doc
+ */
+ public String syncGetWifiApStateByName() {
+ switch (mWifiApState.get()) {
+ case WIFI_AP_STATE_DISABLING:
+ return "disabling";
+ case WIFI_AP_STATE_DISABLED:
+ return "disabled";
+ case WIFI_AP_STATE_ENABLING:
+ return "enabling";
+ case WIFI_AP_STATE_ENABLED:
+ return "enabled";
+ case WIFI_AP_STATE_FAILED:
+ return "failed";
+ default:
+ return "[invalid state]";
+ }
+ }
+
+ /**
+ * Get status information for the current connection, if any.
+ * @return a {@link WifiInfo} object containing information about the current connection
+ *
+ */
+ public WifiInfo syncRequestConnectionInfo() {
+ return mWifiInfo;
+ }
+
+ public DhcpInfo syncGetDhcpInfo() {
+ synchronized (mDhcpInfo) {
+ return new DhcpInfo(mDhcpInfo);
+ }
+ }
+
+ /**
+ * TODO: doc
+ */
+ public void setDriverStart(boolean enable) {
+ if (enable) {
+ sendMessage(CMD_START_DRIVER);
+ } else {
+ sendMessage(CMD_STOP_DRIVER);
+ }
+ }
+
+ /**
+ * TODO: doc
+ */
+ public void setScanOnlyMode(boolean enable) {
+ if (enable) {
+ sendMessage(obtainMessage(CMD_SET_SCAN_MODE, SCAN_ONLY_MODE, 0));
+ } else {
+ sendMessage(obtainMessage(CMD_SET_SCAN_MODE, CONNECT_MODE, 0));
+ }
+ }
+
+ /**
+ * TODO: doc
+ */
+ public void setScanType(boolean active) {
+ if (active) {
+ sendMessage(obtainMessage(CMD_SET_SCAN_TYPE, SCAN_ACTIVE, 0));
+ } else {
+ sendMessage(obtainMessage(CMD_SET_SCAN_TYPE, SCAN_PASSIVE, 0));
+ }
+ }
+
+ /**
+ * TODO: doc
+ */
+ public List<ScanResult> syncGetScanResultsList() {
+ return mScanResults;
+ }
+
+ /**
+ * Disconnect from Access Point
+ */
+ public void disconnectCommand() {
+ sendMessage(CMD_DISCONNECT);
+ }
+
+ /**
+ * Initiate a reconnection to AP
+ */
+ public void reconnectCommand() {
+ sendMessage(CMD_RECONNECT);
+ }
+
+ /**
+ * Initiate a re-association to AP
+ */
+ public void reassociateCommand() {
+ sendMessage(CMD_REASSOCIATE);
+ }
+
+ /**
+ * Add a network synchronously
+ *
+ * @return network id of the new network
+ */
+ public int syncAddOrUpdateNetwork(WifiConfiguration config) {
+ return sendSyncMessage(CMD_ADD_OR_UPDATE_NETWORK, config).intValue;
+ }
+
+ public List<WifiConfiguration> syncGetConfiguredNetworks() {
+ return WifiConfigStore.getConfiguredNetworks();
+ }
+
+ /**
+ * Delete a network
+ *
+ * @param networkId id of the network to be removed
+ */
+ public boolean syncRemoveNetwork(int networkId) {
+ return sendSyncMessage(obtainMessage(CMD_REMOVE_NETWORK, networkId, 0)).boolValue;
+ }
+
+ private class EnableNetParams {
+ private int netId;
+ private boolean disableOthers;
+ EnableNetParams(int n, boolean b) {
+ netId = n;
+ disableOthers = b;
+ }
+ }
+ /**
+ * Enable a network
+ *
+ * @param netId network id of the network
+ * @param disableOthers true, if all other networks have to be disabled
+ * @return {@code true} if the operation succeeds, {@code false} otherwise
+ */
+ public boolean syncEnableNetwork(int netId, boolean disableOthers) {
+ return sendSyncMessage(CMD_ENABLE_NETWORK,
+ new EnableNetParams(netId, disableOthers)).boolValue;
+ }
+
+ /**
+ * Disable a network
+ *
+ * @param netId network id of the network
+ * @return {@code true} if the operation succeeds, {@code false} otherwise
+ */
+ public boolean syncDisableNetwork(int netId) {
+ return sendSyncMessage(obtainMessage(CMD_DISABLE_NETWORK, netId, 0)).boolValue;
+ }
+
+ /**
+ * Blacklist a BSSID. This will avoid the AP if there are
+ * alternate APs to connect
+ *
+ * @param bssid BSSID of the network
+ */
+ public void addToBlacklist(String bssid) {
+ sendMessage(obtainMessage(CMD_BLACKLIST_NETWORK, bssid));
+ }
+
+ /**
+ * Clear the blacklist list
+ *
+ */
+ public void clearBlacklist() {
+ sendMessage(obtainMessage(CMD_CLEAR_BLACKLIST));
+ }
+
+ public void connectNetwork(int netId) {
+ sendMessage(obtainMessage(CMD_CONNECT_NETWORK, netId, 0));
+ }
+
+ public void connectNetwork(WifiConfiguration wifiConfig) {
+ /* arg1 is used to indicate netId, force a netId value of -1 when
+ * we are passing a configuration since the default value of
+ * 0 is a valid netId
+ */
+ sendMessage(obtainMessage(CMD_CONNECT_NETWORK, -1, 0, wifiConfig));
+ }
+
+ public void saveNetwork(WifiConfiguration wifiConfig) {
+ sendMessage(obtainMessage(CMD_SAVE_NETWORK, wifiConfig));
+ }
+
+ public void forgetNetwork(int netId) {
+ sendMessage(obtainMessage(CMD_FORGET_NETWORK, netId, 0));
+ }
+
+ public void enableRssiPolling(boolean enabled) {
+ sendMessage(obtainMessage(CMD_ENABLE_RSSI_POLL, enabled ? 1 : 0, 0));
+ }
+ /**
+ * Get RSSI to currently connected network
+ *
+ * @return RSSI value, -1 on failure
+ */
+ public int syncGetRssi() {
+ return sendSyncMessage(CMD_GET_RSSI).intValue;
+ }
+
+ /**
+ * Get approx RSSI to currently connected network
+ *
+ * @return RSSI value, -1 on failure
+ */
+ public int syncGetRssiApprox() {
+ return sendSyncMessage(CMD_GET_RSSI_APPROX).intValue;
+ }
+
+ /**
+ * Get link speed to currently connected network
+ *
+ * @return link speed, -1 on failure
+ */
+ public int syncGetLinkSpeed() {
+ return sendSyncMessage(CMD_GET_LINK_SPEED).intValue;
+ }
+
+ /**
+ * Get MAC address of radio
+ *
+ * @return MAC address, null on failure
+ */
+ public String syncGetMacAddress() {
+ return sendSyncMessage(CMD_GET_MAC_ADDR).stringValue;
+ }
+
+ /**
+ * Start packet filtering
+ */
+ public void startPacketFiltering() {
+ sendMessage(CMD_START_PACKET_FILTERING);
+ }
+
+ /**
+ * Stop packet filtering
+ */
+ public void stopPacketFiltering() {
+ sendMessage(CMD_STOP_PACKET_FILTERING);
+ }
+
+ /**
+ * Set power mode
+ * @param mode
+ * DRIVER_POWER_MODE_AUTO
+ * DRIVER_POWER_MODE_ACTIVE
+ */
+ public void setPowerMode(int mode) {
+ sendMessage(obtainMessage(CMD_SET_POWER_MODE, mode, 0));
+ }
+
+ /**
+ * Set the number of allowed radio frequency channels from the system
+ * setting value, if any.
+ */
+ public void setNumAllowedChannels() {
+ try {
+ setNumAllowedChannels(
+ Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS));
+ } catch (Settings.SettingNotFoundException e) {
+ if (mNumAllowedChannels != 0) {
+ setNumAllowedChannels(mNumAllowedChannels);
+ }
+ // otherwise, use the driver default
+ }
+ }
+
+ /**
+ * Set the number of radio frequency channels that are allowed to be used
+ * in the current regulatory domain.
+ * @param numChannels the number of allowed channels. Must be greater than 0
+ * and less than or equal to 16.
+ */
+ public void setNumAllowedChannels(int numChannels) {
+ sendMessage(obtainMessage(CMD_SET_NUM_ALLOWED_CHANNELS, numChannels, 0));
+ }
+
+ /**
+ * Get number of allowed channels
+ *
+ * @return channel count, -1 on failure
+ *
+ * TODO: this is not a public API and needs to be removed in favor
+ * of asynchronous reporting. unused for now.
+ */
+ public int getNumAllowedChannels() {
+ return -1;
+ }
+
+ /**
+ * Set bluetooth coex mode:
+ *
+ * @param mode
+ * BLUETOOTH_COEXISTENCE_MODE_ENABLED
+ * BLUETOOTH_COEXISTENCE_MODE_DISABLED
+ * BLUETOOTH_COEXISTENCE_MODE_SENSE
+ */
+ public void setBluetoothCoexistenceMode(int mode) {
+ sendMessage(obtainMessage(CMD_SET_BLUETOOTH_COEXISTENCE, mode, 0));
+ }
+
+ /**
+ * Enable or disable Bluetooth coexistence scan mode. When this mode is on,
+ * some of the low-level scan parameters used by the driver are changed to
+ * reduce interference with A2DP streaming.
+ *
+ * @param isBluetoothPlaying whether to enable or disable this mode
+ */
+ public void setBluetoothScanMode(boolean isBluetoothPlaying) {
+ sendMessage(obtainMessage(CMD_SET_BLUETOOTH_SCAN_MODE, isBluetoothPlaying ? 1 : 0, 0));
+ }
+
+ /**
+ * Save configuration on supplicant
+ *
+ * @return {@code true} if the operation succeeds, {@code false} otherwise
+ *
+ * TODO: deprecate this
+ */
+ public boolean syncSaveConfig() {
+ return sendSyncMessage(CMD_SAVE_CONFIG).boolValue;
+ }
+
+ /**
+ * TODO: doc
+ */
+ public void requestCmWakeLock() {
+ sendMessage(CMD_REQUEST_CM_WAKELOCK);
+ }
+
+ public void updateBatteryWorkSource(WorkSource newSource) {
+ synchronized (mRunningWifiUids) {
+ try {
+ if (newSource != null) {
+ mRunningWifiUids.set(newSource);
+ }
+ if (mIsRunning) {
+ if (mReportedRunning) {
+ // If the work source has changed since last time, need
+ // to remove old work from battery stats.
+ if (mLastRunningWifiUids.diff(mRunningWifiUids)) {
+ mBatteryStats.noteWifiRunningChanged(mLastRunningWifiUids,
+ mRunningWifiUids);
+ mLastRunningWifiUids.set(mRunningWifiUids);
+ }
+ } else {
+ // Now being started, report it.
+ mBatteryStats.noteWifiRunning(mRunningWifiUids);
+ mLastRunningWifiUids.set(mRunningWifiUids);
+ mReportedRunning = true;
+ }
+ } else {
+ if (mReportedRunning) {
+ // Last reported we were running, time to stop.
+ mBatteryStats.noteWifiStopped(mLastRunningWifiUids);
+ mLastRunningWifiUids.clear();
+ mReportedRunning = false;
+ }
+ }
+ } catch (RemoteException ignore) {
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ String LS = System.getProperty("line.separator");
+ sb.append("current HSM state: ").append(getCurrentState().getName()).append(LS);
+ sb.append("mLinkProperties ").append(mLinkProperties).append(LS);
+ sb.append("mWifiInfo ").append(mWifiInfo).append(LS);
+ sb.append("mDhcpInfo ").append(mDhcpInfo).append(LS);
+ sb.append("mNetworkInfo ").append(mNetworkInfo).append(LS);
+ sb.append("mNumAllowedChannels ").append(mNumAllowedChannels).append(LS);
+ sb.append("mLastSignalLevel ").append(mLastSignalLevel).append(LS);
+ sb.append("mLastBssid ").append(mLastBssid).append(LS);
+ sb.append("mLastNetworkId ").append(mLastNetworkId).append(LS);
+ sb.append("mEnableAllNetworks ").append(mEnableAllNetworks).append(LS);
+ sb.append("mEnableRssiPolling ").append(mEnableRssiPolling).append(LS);
+ sb.append("mPasswordKeyMayBeIncorrect ").append(mPasswordKeyMayBeIncorrect).append(LS);
+ sb.append("mReconnectCount ").append(mReconnectCount).append(LS);
+ sb.append("mIsScanMode ").append(mIsScanMode).append(LS);
+ sb.append("Supplicant status").append(LS)
+ .append(WifiNative.statusCommand()).append(LS).append(LS);
+
+ sb.append(WifiConfigStore.dump());
+ return sb.toString();
+ }
+
+ /*********************************************************
+ * Internal private functions
+ ********************************************************/
+
+ class SyncReturn {
+ boolean boolValue;
+ int intValue;
+ String stringValue;
+ Object objValue;
+ }
+
+ class SyncParams {
+ Object mParameter;
+ SyncReturn mSyncReturn;
+ SyncParams() {
+ mSyncReturn = new SyncReturn();
+ }
+ SyncParams(Object p) {
+ mParameter = p;
+ mSyncReturn = new SyncReturn();
+ }
+ }
+
+ /**
+ * message.obj is used to store SyncParams
+ */
+ private SyncReturn syncedSend(Message msg) {
+ SyncParams syncParams = (SyncParams) msg.obj;
+ synchronized(syncParams) {
+ if (DBG) Log.d(TAG, "syncedSend " + msg);
+ sendMessage(msg);
+ try {
+ syncParams.wait();
+ } catch (InterruptedException e) {
+ Log.e(TAG, "sendSyncMessage: unexpected interruption of wait()");
+ return null;
+ }
+ }
+ return syncParams.mSyncReturn;
+ }
+
+ private SyncReturn sendSyncMessage(Message msg) {
+ SyncParams syncParams = new SyncParams();
+ msg.obj = syncParams;
+ return syncedSend(msg);
+ }
+
+ private SyncReturn sendSyncMessage(int what, Object param) {
+ SyncParams syncParams = new SyncParams(param);
+ Message msg = obtainMessage(what, syncParams);
+ return syncedSend(msg);
+ }
+
+
+ private SyncReturn sendSyncMessage(int what) {
+ return sendSyncMessage(obtainMessage(what));
+ }
+
+ private void notifyOnMsgObject(Message msg) {
+ SyncParams syncParams = (SyncParams) msg.obj;
+ if (syncParams != null) {
+ synchronized(syncParams) {
+ if (DBG) Log.d(TAG, "notifyOnMsgObject " + msg);
+ syncParams.notify();
+ }
+ }
+ else {
+ Log.e(TAG, "Error! syncParams in notifyOnMsgObject is null");
+ }
+ }
+
+ private void setWifiState(int wifiState) {
+ final int previousWifiState = mWifiState.get();
+
+ try {
+ if (wifiState == WIFI_STATE_ENABLED) {
+ mBatteryStats.noteWifiOn();
+ } else if (wifiState == WIFI_STATE_DISABLED) {
+ mBatteryStats.noteWifiOff();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to note battery stats in wifi");
+ }
+
+ mWifiState.set(wifiState);
+
+ if (DBG) Log.d(TAG, "setWifiState: " + syncGetWifiStateByName());
+
+ final Intent intent = new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ intent.putExtra(WifiManager.EXTRA_WIFI_STATE, wifiState);
+ intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_STATE, previousWifiState);
+ mContext.sendStickyBroadcast(intent);
+ }
+
+ private void setWifiApState(int wifiApState) {
+ final int previousWifiApState = mWifiApState.get();
+
+ try {
+ if (wifiApState == WIFI_AP_STATE_ENABLED) {
+ mBatteryStats.noteWifiOn();
+ } else if (wifiApState == WIFI_AP_STATE_DISABLED) {
+ mBatteryStats.noteWifiOff();
+ }
+ } catch (RemoteException e) {
+ Log.d(TAG, "Failed to note battery stats in wifi");
+ }
+
+ // Update state
+ mWifiApState.set(wifiApState);
+
+ if (DBG) Log.d(TAG, "setWifiApState: " + syncGetWifiApStateByName());
+
+ final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ intent.putExtra(WifiManager.EXTRA_WIFI_AP_STATE, wifiApState);
+ intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_AP_STATE, previousWifiApState);
+ mContext.sendStickyBroadcast(intent);
+ }
+
+ /**
+ * Parse the scan result line passed to us by wpa_supplicant (helper).
+ * @param line the line to parse
+ * @return the {@link ScanResult} object
+ */
+ private ScanResult parseScanResult(String line) {
+ ScanResult scanResult = null;
+ if (line != null) {
+ /*
+ * Cache implementation (LinkedHashMap) is not synchronized, thus,
+ * must synchronized here!
+ */
+ synchronized (mScanResultCache) {
+ String[] result = scanResultPattern.split(line);
+ if (3 <= result.length && result.length <= 5) {
+ String bssid = result[0];
+ // bssid | frequency | level | flags | ssid
+ int frequency;
+ int level;
+ try {
+ frequency = Integer.parseInt(result[1]);
+ level = Integer.parseInt(result[2]);
+ /* some implementations avoid negative values by adding 256
+ * so we need to adjust for that here.
+ */
+ if (level > 0) level -= 256;
+ } catch (NumberFormatException e) {
+ frequency = 0;
+ level = 0;
+ }
+
+ /*
+ * The formatting of the results returned by
+ * wpa_supplicant is intended to make the fields
+ * line up nicely when printed,
+ * not to make them easy to parse. So we have to
+ * apply some heuristics to figure out which field
+ * is the SSID and which field is the flags.
+ */
+ String ssid;
+ String flags;
+ if (result.length == 4) {
+ if (result[3].charAt(0) == '[') {
+ flags = result[3];
+ ssid = "";
+ } else {
+ flags = "";
+ ssid = result[3];
+ }
+ } else if (result.length == 5) {
+ flags = result[3];
+ ssid = result[4];
+ } else {
+ // Here, we must have 3 fields: no flags and ssid
+ // set
+ flags = "";
+ ssid = "";
+ }
+
+ // bssid + ssid is the hash key
+ String key = bssid + ssid;
+ scanResult = mScanResultCache.get(key);
+ if (scanResult != null) {
+ scanResult.level = level;
+ scanResult.SSID = ssid;
+ scanResult.capabilities = flags;
+ scanResult.frequency = frequency;
+ } else {
+ // Do not add scan results that have no SSID set
+ if (0 < ssid.trim().length()) {
+ scanResult =
+ new ScanResult(
+ ssid, bssid, flags, level, frequency);
+ mScanResultCache.put(key, scanResult);
+ }
+ }
+ } else {
+ Log.w(TAG, "Misformatted scan result text with " +
+ result.length + " fields: " + line);
+ }
+ }
+ }
+
+ return scanResult;
+ }
+
+ /**
+ * scanResults input format
+ * 00:bb:cc:dd:cc:ee 2427 166 [WPA-EAP-TKIP][WPA2-EAP-CCMP] Net1
+ * 00:bb:cc:dd:cc:ff 2412 165 [WPA-EAP-TKIP][WPA2-EAP-CCMP] Net2
+ */
+ private void setScanResults(String scanResults) {
+ if (scanResults == null) {
+ return;
+ }
+
+ List<ScanResult> scanList = new ArrayList<ScanResult>();
+
+ int lineCount = 0;
+
+ int scanResultsLen = scanResults.length();
+ // Parse the result string, keeping in mind that the last line does
+ // not end with a newline.
+ for (int lineBeg = 0, lineEnd = 0; lineEnd <= scanResultsLen; ++lineEnd) {
+ if (lineEnd == scanResultsLen || scanResults.charAt(lineEnd) == '\n') {
+ ++lineCount;
+
+ if (lineCount == 1) {
+ lineBeg = lineEnd + 1;
+ continue;
+ }
+ if (lineEnd > lineBeg) {
+ String line = scanResults.substring(lineBeg, lineEnd);
+ ScanResult scanResult = parseScanResult(line);
+ if (scanResult != null) {
+ scanList.add(scanResult);
+ } else {
+ Log.w(TAG, "misformatted scan result for: " + line);
+ }
+ }
+ lineBeg = lineEnd + 1;
+ }
+ }
+
+ mScanResults = scanList;
+ }
+
+ private String fetchSSID() {
+ String status = WifiNative.statusCommand();
+ if (status == null) {
+ return null;
+ }
+ // extract ssid from a series of "name=value"
+ String[] lines = status.split("\n");
+ for (String line : lines) {
+ String[] prop = line.split(" *= *");
+ if (prop.length < 2) continue;
+ String name = prop[0];
+ String value = prop[1];
+ if (name.equalsIgnoreCase("ssid")) return value;
+ }
+ return null;
+ }
+
+ private void configureLinkProperties() {
+ try {
+ mLinkProperties.setInterface(NetworkInterface.getByName(mInterfaceName));
+ } catch (SocketException e) {
+ Log.e(TAG, "SocketException creating NetworkInterface from " + mInterfaceName +
+ ". e=" + e);
+ return;
+ } catch (NullPointerException e) {
+ Log.e(TAG, "NPE creating NetworkInterface. e=" + e);
+ return;
+ }
+ // TODO - fix this for v6
+ synchronized (mDhcpInfo) {
+ mLinkProperties.addAddress(NetworkUtils.intToInetAddress(mDhcpInfo.ipAddress));
+ mLinkProperties.setGateway(NetworkUtils.intToInetAddress(mDhcpInfo.gateway));
+ mLinkProperties.addDns(NetworkUtils.intToInetAddress(mDhcpInfo.dns1));
+ mLinkProperties.addDns(NetworkUtils.intToInetAddress(mDhcpInfo.dns2));
+ }
+ // TODO - add proxy info
+ }
+
+ private int getMaxDhcpRetries() {
+ return Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.WIFI_MAX_DHCP_RETRY_COUNT,
+ DEFAULT_MAX_DHCP_RETRIES);
+ }
+
+ /**
+ * Whether to disable coexistence mode while obtaining IP address. This
+ * logic will return true only if the current bluetooth
+ * headset/handsfree state is disconnected. This means if it is in an
+ * error state, we will NOT disable coexistence mode to err on the side
+ * of safety.
+ *
+ * @return Whether to disable coexistence mode.
+ */
+ private boolean shouldDisableCoexistenceMode() {
+ int state = mBluetoothHeadset.getState(mBluetoothHeadset.getCurrentHeadset());
+ return state == BluetoothHeadset.STATE_DISCONNECTED;
+ }
+
+ private void checkIsBluetoothPlaying() {
+ boolean isBluetoothPlaying = false;
+ Set<BluetoothDevice> connected = mBluetoothA2dp.getConnectedSinks();
+
+ for (BluetoothDevice device : connected) {
+ if (mBluetoothA2dp.getSinkState(device) == BluetoothA2dp.STATE_PLAYING) {
+ isBluetoothPlaying = true;
+ break;
+ }
+ }
+ setBluetoothScanMode(isBluetoothPlaying);
+ }
+
+ private void sendScanResultsAvailableBroadcast() {
+ if (!ActivityManagerNative.isSystemReady()) return;
+
+ mContext.sendBroadcast(new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
+ }
+
+ private void sendRssiChangeBroadcast(final int newRssi) {
+ if (!ActivityManagerNative.isSystemReady()) return;
+
+ Intent intent = new Intent(WifiManager.RSSI_CHANGED_ACTION);
+ intent.putExtra(WifiManager.EXTRA_NEW_RSSI, newRssi);
+ mContext.sendBroadcast(intent);
+ }
+
+ private void sendNetworkStateChangeBroadcast(String bssid) {
+ Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
+ | Intent.FLAG_RECEIVER_REPLACE_PENDING);
+ intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, mNetworkInfo);
+ intent.putExtra(WifiManager.EXTRA_LINK_PROPERTIES, mLinkProperties);
+ if (bssid != null)
+ intent.putExtra(WifiManager.EXTRA_BSSID, bssid);
+ mContext.sendStickyBroadcast(intent);
+ }
+
+ /* TODO: Unused for now, will be used when ip change on connected network is handled */
+ private void sendConfigChangeBroadcast() {
+ if (!ActivityManagerNative.isSystemReady()) return;
+ Intent intent = new Intent(WifiManager.CONFIG_CHANGED_ACTION);
+ intent.putExtra(WifiManager.EXTRA_LINK_PROPERTIES, mLinkProperties);
+ mContext.sendBroadcast(intent);
+ }
+
+ private void sendSupplicantStateChangedBroadcast(StateChangeResult sc, boolean failedAuth) {
+ Intent intent = new Intent(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
+ | Intent.FLAG_RECEIVER_REPLACE_PENDING);
+ intent.putExtra(WifiManager.EXTRA_NEW_STATE, (Parcelable)sc.state);
+ if (failedAuth) {
+ intent.putExtra(
+ WifiManager.EXTRA_SUPPLICANT_ERROR,
+ WifiManager.ERROR_AUTHENTICATING);
+ }
+ mContext.sendStickyBroadcast(intent);
+ }
+
+ private void sendSupplicantConnectionChangedBroadcast(boolean connected) {
+ if (!ActivityManagerNative.isSystemReady()) return;
+
+ Intent intent = new Intent(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
+ intent.putExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, connected);
+ mContext.sendBroadcast(intent);
+ }
+
+ /**
+ * Record the detailed state of a network.
+ * @param state the new @{code DetailedState}
+ */
+ private void setDetailedState(NetworkInfo.DetailedState state) {
+ Log.d(TAG, "setDetailed state, old ="
+ + mNetworkInfo.getDetailedState() + " and new state=" + state);
+ if (state != mNetworkInfo.getDetailedState()) {
+ mNetworkInfo.setDetailedState(state, null, null);
+ }
+ }
+
+ /**
+ * Poll for info not reported via events
+ * RSSI & Linkspeed
+ */
+ private void requestPolledInfo() {
+ int newRssi = WifiNative.getRssiCommand();
+ if (newRssi != -1 && -200 < newRssi && newRssi < 256) { // screen out invalid values
+ /* some implementations avoid negative values by adding 256
+ * so we need to adjust for that here.
+ */
+ if (newRssi > 0) newRssi -= 256;
+ mWifiInfo.setRssi(newRssi);
+ /*
+ * Rather then sending the raw RSSI out every time it
+ * changes, we precalculate the signal level that would
+ * be displayed in the status bar, and only send the
+ * broadcast if that much more coarse-grained number
+ * changes. This cuts down greatly on the number of
+ * broadcasts, at the cost of not mWifiInforming others
+ * interested in RSSI of all the changes in signal
+ * level.
+ */
+ // TODO: The second arg to the call below needs to be a symbol somewhere, but
+ // it's actually the size of an array of icons that's private
+ // to StatusBar Policy.
+ int newSignalLevel = WifiManager.calculateSignalLevel(newRssi, 4);
+ if (newSignalLevel != mLastSignalLevel) {
+ sendRssiChangeBroadcast(newRssi);
+ }
+ mLastSignalLevel = newSignalLevel;
+ } else {
+ mWifiInfo.setRssi(-200);
+ }
+ int newLinkSpeed = WifiNative.getLinkSpeedCommand();
+ if (newLinkSpeed != -1) {
+ mWifiInfo.setLinkSpeed(newLinkSpeed);
+ }
+ }
+
+ /**
+ * Resets the Wi-Fi Connections by clearing any state, resetting any sockets
+ * using the interface, stopping DHCP & disabling interface
+ */
+ private void handleNetworkDisconnect() {
+ Log.d(TAG, "Reset connections and stopping DHCP");
+
+ /*
+ * Reset connections & stop DHCP
+ */
+ NetworkUtils.resetConnections(mInterfaceName);
+
+ if (!NetworkUtils.stopDhcp(mInterfaceName)) {
+ Log.e(TAG, "Could not stop DHCP");
+ }
+
+ /* Disable interface */
+ NetworkUtils.disableInterface(mInterfaceName);
+
+ /* send event to CM & network change broadcast */
+ setDetailedState(DetailedState.DISCONNECTED);
+ sendNetworkStateChangeBroadcast(mLastBssid);
+
+ /* Reset data structures */
+ mWifiInfo.setIpAddress(0);
+ mWifiInfo.setBSSID(null);
+ mWifiInfo.setSSID(null);
+ mWifiInfo.setNetworkId(-1);
+
+ /* Clear network properties */
+ mLinkProperties.clear();
+
+ mLastBssid= null;
+ mLastNetworkId = -1;
+
+ }
+
+
+ /*********************************************************
+ * Notifications from WifiMonitor
+ ********************************************************/
+
+ /**
+ * A structure for supplying information about a supplicant state
+ * change in the STATE_CHANGE event message that comes from the
+ * WifiMonitor
+ * thread.
+ */
+ private static class StateChangeResult {
+ StateChangeResult(int networkId, String BSSID, Object state) {
+ this.state = state;
+ this.BSSID = BSSID;
+ this.networkId = networkId;
+ }
+ int networkId;
+ String BSSID;
+ Object state;
+ }
+
+ /**
+ * Send the tracker a notification that a user-entered password key
+ * may be incorrect (i.e., caused authentication to fail).
+ */
+ void notifyPasswordKeyMayBeIncorrect() {
+ sendMessage(PASSWORD_MAY_BE_INCORRECT_EVENT);
+ }
+
+ /**
+ * Send the tracker a notification that a connection to the supplicant
+ * daemon has been established.
+ */
+ void notifySupplicantConnection() {
+ sendMessage(SUP_CONNECTION_EVENT);
+ }
+
+ /**
+ * Send the tracker a notification that a connection to the supplicant
+ * daemon has been established.
+ */
+ void notifySupplicantLost() {
+ sendMessage(SUP_DISCONNECTION_EVENT);
+ }
+
+ /**
+ * Send the tracker a notification that the state of Wifi connectivity
+ * has changed.
+ * @param networkId the configured network on which the state change occurred
+ * @param newState the new network state
+ * @param BSSID when the new state is {@link DetailedState#CONNECTED
+ * NetworkInfo.DetailedState.CONNECTED},
+ * this is the MAC address of the access point. Otherwise, it
+ * is {@code null}.
+ */
+ void notifyNetworkStateChange(DetailedState newState, String BSSID, int networkId) {
+ if (newState == NetworkInfo.DetailedState.CONNECTED) {
+ sendMessage(obtainMessage(NETWORK_CONNECTION_EVENT,
+ new StateChangeResult(networkId, BSSID, newState)));
+ } else {
+ sendMessage(obtainMessage(NETWORK_DISCONNECTION_EVENT,
+ new StateChangeResult(networkId, BSSID, newState)));
+ }
+ }
+
+ /**
+ * Send the tracker a notification that the state of the supplicant
+ * has changed.
+ * @param networkId the configured network on which the state change occurred
+ * @param newState the new {@code SupplicantState}
+ */
+ void notifySupplicantStateChange(int networkId, String BSSID, SupplicantState newState) {
+ sendMessage(obtainMessage(SUPPLICANT_STATE_CHANGE_EVENT,
+ new StateChangeResult(networkId, BSSID, newState)));
+ }
+
+ /**
+ * Send the tracker a notification that a scan has completed, and results
+ * are available.
+ */
+ void notifyScanResultsAvailable() {
+ /**
+ * Switch scan mode over to passive.
+ * Turning off scan-only mode happens only in "Connect" mode
+ */
+ setScanType(false);
+ sendMessage(SCAN_RESULTS_EVENT);
+ }
+
+ void notifyDriverStarted() {
+ sendMessage(DRIVER_START_EVENT);
+ }
+
+ void notifyDriverStopped() {
+ sendMessage(DRIVER_STOP_EVENT);
+ }
+
+ void notifyDriverHung() {
+ setWifiEnabled(false);
+ setWifiEnabled(true);
+ }
+
+
+ /********************************************************
+ * HSM states
+ *******************************************************/
+
+ class DefaultState extends HierarchicalState {
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ SyncParams syncParams;
+ switch (message.what) {
+ /* Synchronous call returns */
+ case CMD_PING_SUPPLICANT:
+ case CMD_REMOVE_NETWORK:
+ case CMD_ENABLE_NETWORK:
+ case CMD_DISABLE_NETWORK:
+ case CMD_ADD_OR_UPDATE_NETWORK:
+ case CMD_GET_RSSI:
+ case CMD_GET_RSSI_APPROX:
+ case CMD_GET_LINK_SPEED:
+ case CMD_GET_MAC_ADDR:
+ case CMD_SAVE_CONFIG:
+ syncParams = (SyncParams) message.obj;
+ syncParams.mSyncReturn.boolValue = false;
+ syncParams.mSyncReturn.intValue = -1;
+ syncParams.mSyncReturn.stringValue = null;
+ notifyOnMsgObject(message);
+ break;
+ case CMD_ENABLE_RSSI_POLL:
+ mEnableRssiPolling = (message.arg1 == 1);
+ mSupplicantStateTracker.sendMessage(CMD_ENABLE_RSSI_POLL);
+ break;
+ /* Discard */
+ case CMD_LOAD_DRIVER:
+ case CMD_UNLOAD_DRIVER:
+ case CMD_START_SUPPLICANT:
+ case CMD_STOP_SUPPLICANT:
+ case CMD_START_DRIVER:
+ case CMD_STOP_DRIVER:
+ case CMD_START_AP:
+ case CMD_STOP_AP:
+ case CMD_START_SCAN:
+ case CMD_DISCONNECT:
+ case CMD_RECONNECT:
+ case CMD_REASSOCIATE:
+ case CMD_RECONFIGURE_IP:
+ case SUP_CONNECTION_EVENT:
+ case SUP_DISCONNECTION_EVENT:
+ case DRIVER_START_EVENT:
+ case DRIVER_STOP_EVENT:
+ case NETWORK_CONNECTION_EVENT:
+ case NETWORK_DISCONNECTION_EVENT:
+ case SCAN_RESULTS_EVENT:
+ case SUPPLICANT_STATE_CHANGE_EVENT:
+ case PASSWORD_MAY_BE_INCORRECT_EVENT:
+ case CMD_BLACKLIST_NETWORK:
+ case CMD_CLEAR_BLACKLIST:
+ case CMD_SET_SCAN_MODE:
+ case CMD_SET_SCAN_TYPE:
+ case CMD_SET_POWER_MODE:
+ case CMD_SET_BLUETOOTH_COEXISTENCE:
+ case CMD_SET_BLUETOOTH_SCAN_MODE:
+ case CMD_SET_NUM_ALLOWED_CHANNELS:
+ case CMD_REQUEST_CM_WAKELOCK:
+ case CMD_CONNECT_NETWORK:
+ case CMD_SAVE_NETWORK:
+ case CMD_FORGET_NETWORK:
+ break;
+ default:
+ Log.e(TAG, "Error! unhandled message" + message);
+ break;
+ }
+ return HANDLED;
+ }
+ }
+
+ class InitialState extends HierarchicalState {
+ @Override
+ //TODO: could move logging into a common class
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ // [31-8] Reserved for future use
+ // [7 - 0] HSM state change
+ // 50021 wifi_state_changed (custom|1|5)
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+
+ if (WifiNative.isDriverLoaded()) {
+ transitionTo(mDriverLoadedState);
+ }
+ else {
+ transitionTo(mDriverUnloadedState);
+ }
+ }
+ }
+
+ class DriverLoadingState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+
+ final Message message = new Message();
+ message.copyFrom(getCurrentMessage());
+ /* TODO: add a timeout to fail when driver load is hung.
+ * Similarly for driver unload.
+ */
+ new Thread(new Runnable() {
+ public void run() {
+ sWakeLock.acquire();
+ //enabling state
+ switch(message.arg1) {
+ case WIFI_STATE_ENABLING:
+ setWifiState(WIFI_STATE_ENABLING);
+ break;
+ case WIFI_AP_STATE_ENABLING:
+ setWifiApState(WIFI_AP_STATE_ENABLING);
+ break;
+ }
+
+ if(WifiNative.loadDriver()) {
+ Log.d(TAG, "Driver load successful");
+ sendMessage(CMD_LOAD_DRIVER_SUCCESS);
+ } else {
+ Log.e(TAG, "Failed to load driver!");
+ switch(message.arg1) {
+ case WIFI_STATE_ENABLING:
+ setWifiState(WIFI_STATE_UNKNOWN);
+ break;
+ case WIFI_AP_STATE_ENABLING:
+ setWifiApState(WIFI_AP_STATE_FAILED);
+ break;
+ }
+ sendMessage(CMD_LOAD_DRIVER_FAILURE);
+ }
+ sWakeLock.release();
+ }
+ }).start();
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ switch (message.what) {
+ case CMD_LOAD_DRIVER_SUCCESS:
+ transitionTo(mDriverLoadedState);
+ break;
+ case CMD_LOAD_DRIVER_FAILURE:
+ transitionTo(mDriverFailedState);
+ break;
+ case CMD_LOAD_DRIVER:
+ case CMD_UNLOAD_DRIVER:
+ case CMD_START_SUPPLICANT:
+ case CMD_STOP_SUPPLICANT:
+ case CMD_START_AP:
+ case CMD_STOP_AP:
+ case CMD_START_DRIVER:
+ case CMD_STOP_DRIVER:
+ case CMD_SET_SCAN_MODE:
+ case CMD_SET_SCAN_TYPE:
+ case CMD_SET_POWER_MODE:
+ case CMD_SET_BLUETOOTH_COEXISTENCE:
+ case CMD_SET_BLUETOOTH_SCAN_MODE:
+ case CMD_SET_NUM_ALLOWED_CHANNELS:
+ case CMD_START_PACKET_FILTERING:
+ case CMD_STOP_PACKET_FILTERING:
+ deferMessage(message);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+ }
+
+ class DriverLoadedState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ switch(message.what) {
+ case CMD_UNLOAD_DRIVER:
+ transitionTo(mDriverUnloadingState);
+ break;
+ case CMD_START_SUPPLICANT:
+ if(WifiNative.startSupplicant()) {
+ Log.d(TAG, "Supplicant start successful");
+ mWifiMonitor.startMonitoring();
+ setWifiState(WIFI_STATE_ENABLED);
+ transitionTo(mWaitForSupState);
+ } else {
+ Log.e(TAG, "Failed to start supplicant!");
+ sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_STATE_UNKNOWN, 0));
+ }
+ break;
+ case CMD_START_AP:
+ try {
+ nwService.startAccessPoint((WifiConfiguration) message.obj,
+ mInterfaceName,
+ SOFTAP_IFACE);
+ } catch(Exception e) {
+ Log.e(TAG, "Exception in startAccessPoint()");
+ sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_AP_STATE_FAILED, 0));
+ break;
+ }
+ Log.d(TAG, "Soft AP start successful");
+ setWifiApState(WIFI_AP_STATE_ENABLED);
+ transitionTo(mSoftApStartedState);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+ }
+
+ class DriverUnloadingState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+
+ final Message message = new Message();
+ message.copyFrom(getCurrentMessage());
+ new Thread(new Runnable() {
+ public void run() {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ sWakeLock.acquire();
+ if(WifiNative.unloadDriver()) {
+ Log.d(TAG, "Driver unload successful");
+ sendMessage(CMD_UNLOAD_DRIVER_SUCCESS);
+
+ switch(message.arg1) {
+ case WIFI_STATE_DISABLED:
+ case WIFI_STATE_UNKNOWN:
+ setWifiState(message.arg1);
+ break;
+ case WIFI_AP_STATE_DISABLED:
+ case WIFI_AP_STATE_FAILED:
+ setWifiApState(message.arg1);
+ break;
+ }
+ } else {
+ Log.e(TAG, "Failed to unload driver!");
+ sendMessage(CMD_UNLOAD_DRIVER_FAILURE);
+
+ switch(message.arg1) {
+ case WIFI_STATE_DISABLED:
+ case WIFI_STATE_UNKNOWN:
+ setWifiState(WIFI_STATE_UNKNOWN);
+ break;
+ case WIFI_AP_STATE_DISABLED:
+ case WIFI_AP_STATE_FAILED:
+ setWifiApState(WIFI_AP_STATE_FAILED);
+ break;
+ }
+ }
+ sWakeLock.release();
+ }
+ }).start();
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ switch (message.what) {
+ case CMD_UNLOAD_DRIVER_SUCCESS:
+ transitionTo(mDriverUnloadedState);
+ break;
+ case CMD_UNLOAD_DRIVER_FAILURE:
+ transitionTo(mDriverFailedState);
+ break;
+ case CMD_LOAD_DRIVER:
+ case CMD_UNLOAD_DRIVER:
+ case CMD_START_SUPPLICANT:
+ case CMD_STOP_SUPPLICANT:
+ case CMD_START_AP:
+ case CMD_STOP_AP:
+ case CMD_START_DRIVER:
+ case CMD_STOP_DRIVER:
+ case CMD_SET_SCAN_MODE:
+ case CMD_SET_SCAN_TYPE:
+ case CMD_SET_POWER_MODE:
+ case CMD_SET_BLUETOOTH_COEXISTENCE:
+ case CMD_SET_BLUETOOTH_SCAN_MODE:
+ case CMD_SET_NUM_ALLOWED_CHANNELS:
+ case CMD_START_PACKET_FILTERING:
+ case CMD_STOP_PACKET_FILTERING:
+ deferMessage(message);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+ }
+
+ class DriverUnloadedState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ switch (message.what) {
+ case CMD_LOAD_DRIVER:
+ transitionTo(mDriverLoadingState);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+ }
+
+ class DriverFailedState extends HierarchicalState {
+ @Override
+ public void enter() {
+ Log.e(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ return NOT_HANDLED;
+ }
+ }
+
+
+ class WaitForSupState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ switch(message.what) {
+ case SUP_CONNECTION_EVENT:
+ Log.d(TAG, "Supplicant connection established");
+ mSupplicantStateTracker.resetSupplicantState();
+ /* Initialize data structures */
+ mLastBssid = null;
+ mLastNetworkId = -1;
+ mLastSignalLevel = -1;
+
+ mWifiInfo.setMacAddress(WifiNative.getMacAddressCommand());
+
+ WifiConfigStore.initialize(mContext);
+
+ //TODO: initialize and fix multicast filtering
+ //mWM.initializeMulticastFiltering();
+
+ if (mBluetoothA2dp == null) {
+ mBluetoothA2dp = new BluetoothA2dp(mContext);
+ }
+ checkIsBluetoothPlaying();
+
+ sendSupplicantConnectionChangedBroadcast(true);
+ transitionTo(mDriverSupReadyState);
+ break;
+ case CMD_STOP_SUPPLICANT:
+ Log.d(TAG, "Stop supplicant received");
+ WifiNative.stopSupplicant();
+ transitionTo(mDriverLoadedState);
+ break;
+ /* Fail soft ap when waiting for supplicant start */
+ case CMD_START_AP:
+ Log.d(TAG, "Failed to start soft AP with a running supplicant");
+ setWifiApState(WIFI_AP_STATE_FAILED);
+ break;
+ case CMD_START_DRIVER:
+ case CMD_STOP_DRIVER:
+ case CMD_SET_SCAN_MODE:
+ case CMD_SET_SCAN_TYPE:
+ case CMD_SET_POWER_MODE:
+ case CMD_SET_BLUETOOTH_COEXISTENCE:
+ case CMD_SET_BLUETOOTH_SCAN_MODE:
+ case CMD_SET_NUM_ALLOWED_CHANNELS:
+ case CMD_START_PACKET_FILTERING:
+ case CMD_STOP_PACKET_FILTERING:
+ deferMessage(message);
+ break;
+ case CMD_STOP_AP:
+ case CMD_START_SUPPLICANT:
+ case CMD_UNLOAD_DRIVER:
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+ }
+
+ class DriverSupReadyState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ /* Initialize for connect mode operation at start */
+ mIsScanMode = false;
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ SyncParams syncParams;
+ WifiConfiguration config;
+ switch(message.what) {
+ case CMD_STOP_SUPPLICANT: /* Supplicant stopped by user */
+ Log.d(TAG, "Stop supplicant received");
+ WifiNative.stopSupplicant();
+ //$FALL-THROUGH$
+ case SUP_DISCONNECTION_EVENT: /* Supplicant died */
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ WifiNative.closeSupplicantConnection();
+ handleNetworkDisconnect();
+ sendSupplicantConnectionChangedBroadcast(false);
+ mSupplicantStateTracker.resetSupplicantState();
+ transitionTo(mDriverLoadedState);
+
+ /* When supplicant dies, unload driver and enter failed state */
+ //TODO: consider bringing up supplicant again
+ if (message.what == SUP_DISCONNECTION_EVENT) {
+ Log.d(TAG, "Supplicant died, unloading driver");
+ sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_STATE_UNKNOWN, 0));
+ }
+ break;
+ case CMD_START_DRIVER:
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ WifiNative.startDriverCommand();
+ transitionTo(mDriverStartingState);
+ break;
+ case SCAN_RESULTS_EVENT:
+ setScanResults(WifiNative.scanResultsCommand());
+ sendScanResultsAvailableBroadcast();
+ break;
+ case CMD_PING_SUPPLICANT:
+ syncParams = (SyncParams) message.obj;
+ syncParams.mSyncReturn.boolValue = WifiNative.pingCommand();
+ notifyOnMsgObject(message);
+ break;
+ case CMD_ADD_OR_UPDATE_NETWORK:
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ syncParams = (SyncParams) message.obj;
+ config = (WifiConfiguration) syncParams.mParameter;
+ syncParams.mSyncReturn.intValue = WifiConfigStore.addOrUpdateNetwork(config);
+ notifyOnMsgObject(message);
+ break;
+ case CMD_REMOVE_NETWORK:
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ syncParams = (SyncParams) message.obj;
+ syncParams.mSyncReturn.boolValue = WifiConfigStore.removeNetwork(
+ message.arg1);
+ notifyOnMsgObject(message);
+ break;
+ case CMD_ENABLE_NETWORK:
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ syncParams = (SyncParams) message.obj;
+ EnableNetParams enableNetParams = (EnableNetParams) syncParams.mParameter;
+ syncParams.mSyncReturn.boolValue = WifiConfigStore.enableNetwork(
+ enableNetParams.netId, enableNetParams.disableOthers);
+ notifyOnMsgObject(message);
+ break;
+ case CMD_DISABLE_NETWORK:
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ syncParams = (SyncParams) message.obj;
+ syncParams.mSyncReturn.boolValue = WifiConfigStore.disableNetwork(
+ message.arg1);
+ notifyOnMsgObject(message);
+ break;
+ case CMD_BLACKLIST_NETWORK:
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ WifiNative.addToBlacklistCommand((String)message.obj);
+ break;
+ case CMD_CLEAR_BLACKLIST:
+ WifiNative.clearBlacklistCommand();
+ break;
+ case CMD_SAVE_CONFIG:
+ syncParams = (SyncParams) message.obj;
+ syncParams.mSyncReturn.boolValue = WifiConfigStore.saveConfig();
+ notifyOnMsgObject(message);
+ // Inform the backup manager about a data change
+ IBackupManager ibm = IBackupManager.Stub.asInterface(
+ ServiceManager.getService(Context.BACKUP_SERVICE));
+ if (ibm != null) {
+ try {
+ ibm.dataChanged("com.android.providers.settings");
+ } catch (Exception e) {
+ // Try again later
+ }
+ }
+ break;
+ case CMD_GET_MAC_ADDR:
+ syncParams = (SyncParams) message.obj;
+ syncParams.mSyncReturn.stringValue = WifiNative.getMacAddressCommand();
+ notifyOnMsgObject(message);
+ break;
+ /* Cannot start soft AP while in client mode */
+ case CMD_START_AP:
+ Log.d(TAG, "Failed to start soft AP with a running supplicant");
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ setWifiApState(WIFI_AP_STATE_FAILED);
+ break;
+ case CMD_SET_SCAN_MODE:
+ mIsScanMode = (message.arg1 == SCAN_ONLY_MODE);
+ break;
+ case CMD_SAVE_NETWORK:
+ config = (WifiConfiguration) message.obj;
+ WifiConfigStore.saveNetwork(config);
+ break;
+ case CMD_FORGET_NETWORK:
+ WifiConfigStore.forgetNetwork(message.arg1);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return HANDLED;
+ }
+ }
+
+ class DriverStartingState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ switch(message.what) {
+ case DRIVER_START_EVENT:
+ transitionTo(mDriverStartedState);
+ break;
+ /* Queue driver commands & connection events */
+ case CMD_START_DRIVER:
+ case CMD_STOP_DRIVER:
+ case SUPPLICANT_STATE_CHANGE_EVENT:
+ case NETWORK_CONNECTION_EVENT:
+ case NETWORK_DISCONNECTION_EVENT:
+ case PASSWORD_MAY_BE_INCORRECT_EVENT:
+ case CMD_SET_SCAN_TYPE:
+ case CMD_SET_POWER_MODE:
+ case CMD_SET_BLUETOOTH_COEXISTENCE:
+ case CMD_SET_BLUETOOTH_SCAN_MODE:
+ case CMD_SET_NUM_ALLOWED_CHANNELS:
+ case CMD_START_PACKET_FILTERING:
+ case CMD_STOP_PACKET_FILTERING:
+ case CMD_START_SCAN:
+ case CMD_DISCONNECT:
+ case CMD_REASSOCIATE:
+ case CMD_RECONNECT:
+ deferMessage(message);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+ }
+
+ class DriverStartedState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+
+ mIsRunning = true;
+ updateBatteryWorkSource(null);
+
+ /* Initialize channel count */
+ setNumAllowedChannels();
+ /*
+ * STOPSHIP
+ * TODO: We are having 11A issues that Broadcom is looking
+ * to resolve, this is a temporary fix to allow only 11B/G
+ * and help improve GoogleGuest connectivity
+ * We also need to add the UI for band control
+ */
+ WifiNative.setBandCommand(BAND_2G);
+
+ if (mIsScanMode) {
+ WifiNative.setScanResultHandlingCommand(SCAN_ONLY_MODE);
+ WifiNative.disconnectCommand();
+ transitionTo(mScanModeState);
+ } else {
+ WifiNative.setScanResultHandlingCommand(CONNECT_MODE);
+ /* If supplicant has already connected, before we could finish establishing
+ * the control channel connection, we miss all the supplicant events.
+ * Disconnect and reconnect when driver has started to ensure we receive
+ * all supplicant events.
+ *
+ * TODO: This is a bit unclean, ideally the supplicant should never
+ * connect until told to do so by the framework
+ */
+ WifiNative.disconnectCommand();
+ WifiNative.reconnectCommand();
+ transitionTo(mConnectModeState);
+ }
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ SyncParams syncParams;
+ switch(message.what) {
+ case CMD_SET_SCAN_TYPE:
+ if (message.arg1 == SCAN_ACTIVE) {
+ WifiNative.setScanModeCommand(true);
+ } else {
+ WifiNative.setScanModeCommand(false);
+ }
+ break;
+ case CMD_SET_POWER_MODE:
+ WifiNative.setPowerModeCommand(message.arg1);
+ break;
+ case CMD_SET_BLUETOOTH_COEXISTENCE:
+ WifiNative.setBluetoothCoexistenceModeCommand(message.arg1);
+ break;
+ case CMD_SET_BLUETOOTH_SCAN_MODE:
+ WifiNative.setBluetoothCoexistenceScanModeCommand(message.arg1 == 1);
+ break;
+ case CMD_SET_NUM_ALLOWED_CHANNELS:
+ mNumAllowedChannels = message.arg1;
+ WifiNative.setNumAllowedChannelsCommand(message.arg1);
+ break;
+ case CMD_START_DRIVER:
+ /* Ignore another driver start */
+ break;
+ case CMD_STOP_DRIVER:
+ WifiNative.stopDriverCommand();
+ transitionTo(mDriverStoppingState);
+ break;
+ case CMD_REQUEST_CM_WAKELOCK:
+ if (mCm == null) {
+ mCm = (ConnectivityManager)mContext.getSystemService(
+ Context.CONNECTIVITY_SERVICE);
+ }
+ mCm.requestNetworkTransitionWakelock(TAG);
+ break;
+ case CMD_START_PACKET_FILTERING:
+ WifiNative.startPacketFiltering();
+ break;
+ case CMD_STOP_PACKET_FILTERING:
+ WifiNative.stopPacketFiltering();
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+ @Override
+ public void exit() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ mIsRunning = false;
+ updateBatteryWorkSource(null);
+ mScanResults = null;
+ }
+ }
+
+ class DriverStoppingState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ switch(message.what) {
+ case DRIVER_STOP_EVENT:
+ transitionTo(mDriverStoppedState);
+ break;
+ /* Queue driver commands */
+ case CMD_START_DRIVER:
+ case CMD_STOP_DRIVER:
+ case CMD_SET_SCAN_TYPE:
+ case CMD_SET_POWER_MODE:
+ case CMD_SET_BLUETOOTH_COEXISTENCE:
+ case CMD_SET_BLUETOOTH_SCAN_MODE:
+ case CMD_SET_NUM_ALLOWED_CHANNELS:
+ case CMD_START_PACKET_FILTERING:
+ case CMD_STOP_PACKET_FILTERING:
+ case CMD_START_SCAN:
+ case CMD_DISCONNECT:
+ case CMD_REASSOCIATE:
+ case CMD_RECONNECT:
+ deferMessage(message);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+ }
+
+ class DriverStoppedState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ return NOT_HANDLED;
+ }
+ }
+
+ class ScanModeState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ SyncParams syncParams;
+ switch(message.what) {
+ case CMD_SET_SCAN_MODE:
+ if (message.arg1 == SCAN_ONLY_MODE) {
+ /* Ignore */
+ return HANDLED;
+ } else {
+ WifiNative.setScanResultHandlingCommand(message.arg1);
+ WifiNative.reconnectCommand();
+ mIsScanMode = false;
+ transitionTo(mDisconnectedState);
+ }
+ break;
+ case CMD_START_SCAN:
+ WifiNative.scanCommand(message.arg1 == SCAN_ACTIVE);
+ break;
+ /* Ignore */
+ case CMD_DISCONNECT:
+ case CMD_RECONNECT:
+ case CMD_REASSOCIATE:
+ case SUPPLICANT_STATE_CHANGE_EVENT:
+ case NETWORK_CONNECTION_EVENT:
+ case NETWORK_DISCONNECTION_EVENT:
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+ }
+
+ class ConnectModeState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ SyncParams syncParams;
+ StateChangeResult stateChangeResult;
+ switch(message.what) {
+ case PASSWORD_MAY_BE_INCORRECT_EVENT:
+ mPasswordKeyMayBeIncorrect = true;
+ break;
+ case SUPPLICANT_STATE_CHANGE_EVENT:
+ stateChangeResult = (StateChangeResult) message.obj;
+ mSupplicantStateTracker.handleEvent(stateChangeResult);
+ break;
+ case CMD_START_SCAN:
+ /* We need to set scan type in completed state */
+ Message newMsg = obtainMessage();
+ newMsg.copyFrom(message);
+ mSupplicantStateTracker.sendMessage(newMsg);
+ break;
+ /* Do a redundant disconnect without transition */
+ case CMD_DISCONNECT:
+ WifiNative.disconnectCommand();
+ break;
+ case CMD_RECONNECT:
+ WifiNative.reconnectCommand();
+ break;
+ case CMD_REASSOCIATE:
+ WifiNative.reassociateCommand();
+ break;
+ case CMD_CONNECT_NETWORK:
+ int netId = message.arg1;
+ WifiConfiguration config = (WifiConfiguration) message.obj;
+
+ /* We connect to a specific network by issuing a select
+ * to the WifiConfigStore. This enables the network,
+ * while disabling all other networks in the supplicant.
+ * Disabling a connected network will cause a disconnection
+ * from the network. A reconnectCommand() will then initiate
+ * a connection to the enabled network.
+ */
+ if (config != null) {
+ WifiConfigStore.selectNetwork(config);
+ } else {
+ WifiConfigStore.selectNetwork(netId);
+ }
+
+ /* Save a flag to indicate that we need to enable all
+ * networks after supplicant indicates a network
+ * state change event
+ */
+ mEnableAllNetworks = true;
+
+ WifiNative.reconnectCommand();
+
+ /* Expect a disconnection from the old connection */
+ transitionTo(mDisconnectingState);
+ break;
+ case SCAN_RESULTS_EVENT:
+ /* Set the scan setting back to "connect" mode */
+ WifiNative.setScanResultHandlingCommand(CONNECT_MODE);
+ /* Handle scan results */
+ return NOT_HANDLED;
+ case NETWORK_CONNECTION_EVENT:
+ Log.d(TAG,"Network connection established");
+ stateChangeResult = (StateChangeResult) message.obj;
+
+ //TODO: make supplicant modification to push this in events
+ mWifiInfo.setSSID(fetchSSID());
+ mWifiInfo.setBSSID(mLastBssid = stateChangeResult.BSSID);
+ mWifiInfo.setNetworkId(stateChangeResult.networkId);
+ mLastNetworkId = stateChangeResult.networkId;
+ /* send event to CM & network change broadcast */
+ setDetailedState(DetailedState.OBTAINING_IPADDR);
+ sendNetworkStateChangeBroadcast(mLastBssid);
+ transitionTo(mConnectingState);
+ break;
+ case NETWORK_DISCONNECTION_EVENT:
+ Log.d(TAG,"Network connection lost");
+ handleNetworkDisconnect();
+ transitionTo(mDisconnectedState);
+ break;
+ case CMD_GET_RSSI:
+ syncParams = (SyncParams) message.obj;
+ syncParams.mSyncReturn.intValue = WifiNative.getRssiCommand();
+ notifyOnMsgObject(message);
+ break;
+ case CMD_GET_RSSI_APPROX:
+ syncParams = (SyncParams) message.obj;
+ syncParams.mSyncReturn.intValue = WifiNative.getRssiApproxCommand();
+ notifyOnMsgObject(message);
+ break;
+ case CMD_GET_LINK_SPEED:
+ syncParams = (SyncParams) message.obj;
+ syncParams.mSyncReturn.intValue = WifiNative.getLinkSpeedCommand();
+ notifyOnMsgObject(message);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+ }
+
+ class ConnectingState extends HierarchicalState {
+ boolean mModifiedBluetoothCoexistenceMode;
+ int mPowerMode;
+ boolean mUseStaticIp;
+ Thread mDhcpThread;
+
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+
+ mUseStaticIp = WifiConfigStore.isUsingStaticIp(mLastNetworkId);
+ if (!mUseStaticIp) {
+ mDhcpThread = null;
+ mModifiedBluetoothCoexistenceMode = false;
+ mPowerMode = DRIVER_POWER_MODE_AUTO;
+
+ if (shouldDisableCoexistenceMode()) {
+ /*
+ * There are problems setting the Wi-Fi driver's power
+ * mode to active when bluetooth coexistence mode is
+ * enabled or sense.
+ * <p>
+ * We set Wi-Fi to active mode when
+ * obtaining an IP address because we've found
+ * compatibility issues with some routers with low power
+ * mode.
+ * <p>
+ * In order for this active power mode to properly be set,
+ * we disable coexistence mode until we're done with
+ * obtaining an IP address. One exception is if we
+ * are currently connected to a headset, since disabling
+ * coexistence would interrupt that connection.
+ */
+ mModifiedBluetoothCoexistenceMode = true;
+
+ // Disable the coexistence mode
+ WifiNative.setBluetoothCoexistenceModeCommand(
+ WifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED);
+ }
+
+ mPowerMode = WifiNative.getPowerModeCommand();
+ if (mPowerMode < 0) {
+ // Handle the case where supplicant driver does not support
+ // getPowerModeCommand.
+ mPowerMode = DRIVER_POWER_MODE_AUTO;
+ }
+ if (mPowerMode != DRIVER_POWER_MODE_ACTIVE) {
+ WifiNative.setPowerModeCommand(DRIVER_POWER_MODE_ACTIVE);
+ }
+
+ Log.d(TAG, "DHCP request started");
+ mDhcpThread = new Thread(new Runnable() {
+ public void run() {
+ DhcpInfo dhcpInfo = new DhcpInfo();
+ if (NetworkUtils.runDhcp(mInterfaceName, dhcpInfo)) {
+ Log.d(TAG, "DHCP request succeeded");
+ synchronized (mDhcpInfo) {
+ mDhcpInfo = dhcpInfo;
+ }
+ sendMessage(CMD_IP_CONFIG_SUCCESS);
+ } else {
+ Log.d(TAG, "DHCP request failed: " +
+ NetworkUtils.getDhcpError());
+ sendMessage(CMD_IP_CONFIG_FAILURE);
+ }
+ }
+ });
+ mDhcpThread.start();
+ } else {
+ DhcpInfo dhcpInfo = WifiConfigStore.getIpConfiguration(mLastNetworkId);
+ if (NetworkUtils.configureInterface(mInterfaceName, dhcpInfo)) {
+ Log.v(TAG, "Static IP configuration succeeded");
+ synchronized (mDhcpInfo) {
+ mDhcpInfo = dhcpInfo;
+ }
+ sendMessage(CMD_IP_CONFIG_SUCCESS);
+ } else {
+ Log.v(TAG, "Static IP configuration failed");
+ sendMessage(CMD_IP_CONFIG_FAILURE);
+ }
+ }
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+
+ switch(message.what) {
+ case CMD_IP_CONFIG_SUCCESS:
+ mReconnectCount = 0;
+ mLastSignalLevel = -1; // force update of signal strength
+ synchronized (mDhcpInfo) {
+ mWifiInfo.setIpAddress(mDhcpInfo.ipAddress);
+ Log.d(TAG, "IP configuration: " + mDhcpInfo);
+ }
+ configureLinkProperties();
+ setDetailedState(DetailedState.CONNECTED);
+ sendNetworkStateChangeBroadcast(mLastBssid);
+ //TODO: The framework is not detecting a DHCP renewal and a possible
+ //IP change. we should detect this and send out a config change broadcast
+ transitionTo(mConnectedState);
+ break;
+ case CMD_IP_CONFIG_FAILURE:
+ mWifiInfo.setIpAddress(0);
+
+ Log.e(TAG, "IP configuration failed");
+ /**
+ * If we've exceeded the maximum number of retries for DHCP
+ * to a given network, disable the network
+ */
+ if (++mReconnectCount > getMaxDhcpRetries()) {
+ Log.e(TAG, "Failed " +
+ mReconnectCount + " times, Disabling " + mLastNetworkId);
+ WifiConfigStore.disableNetwork(mLastNetworkId);
+ }
+
+ /* DHCP times out after about 30 seconds, we do a
+ * disconnect and an immediate reconnect to try again
+ */
+ WifiNative.disconnectCommand();
+ WifiNative.reconnectCommand();
+ transitionTo(mDisconnectingState);
+ break;
+ case CMD_DISCONNECT:
+ WifiNative.disconnectCommand();
+ transitionTo(mDisconnectingState);
+ break;
+ /* Ignore connection to same network */
+ case CMD_CONNECT_NETWORK:
+ int netId = message.arg1;
+ if (mWifiInfo.getNetworkId() == netId) {
+ break;
+ }
+ return NOT_HANDLED;
+ /* Ignore */
+ case NETWORK_CONNECTION_EVENT:
+ break;
+ case CMD_STOP_DRIVER:
+ sendMessage(CMD_DISCONNECT);
+ deferMessage(message);
+ break;
+ case CMD_SET_SCAN_MODE:
+ if (message.arg1 == SCAN_ONLY_MODE) {
+ sendMessage(CMD_DISCONNECT);
+ deferMessage(message);
+ }
+ break;
+ case CMD_RECONFIGURE_IP:
+ deferMessage(message);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+
+ @Override
+ public void exit() {
+ /* reset power state & bluetooth coexistence if on DHCP */
+ if (!mUseStaticIp) {
+ if (mPowerMode != DRIVER_POWER_MODE_ACTIVE) {
+ WifiNative.setPowerModeCommand(mPowerMode);
+ }
+
+ if (mModifiedBluetoothCoexistenceMode) {
+ // Set the coexistence mode back to its default value
+ WifiNative.setBluetoothCoexistenceModeCommand(
+ WifiNative.BLUETOOTH_COEXISTENCE_MODE_SENSE);
+ }
+ }
+
+ }
+ }
+
+ class ConnectedState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ switch (message.what) {
+ case CMD_DISCONNECT:
+ WifiNative.disconnectCommand();
+ transitionTo(mDisconnectingState);
+ break;
+ case CMD_RECONFIGURE_IP:
+ Log.d(TAG,"Reconfiguring IP on connection");
+ NetworkUtils.resetConnections(mInterfaceName);
+ transitionTo(mConnectingState);
+ break;
+ case CMD_STOP_DRIVER:
+ sendMessage(CMD_DISCONNECT);
+ deferMessage(message);
+ break;
+ case CMD_SET_SCAN_MODE:
+ if (message.arg1 == SCAN_ONLY_MODE) {
+ sendMessage(CMD_DISCONNECT);
+ deferMessage(message);
+ }
+ break;
+ /* Ignore connection to same network */
+ case CMD_CONNECT_NETWORK:
+ int netId = message.arg1;
+ if (mWifiInfo.getNetworkId() == netId) {
+ break;
+ }
+ return NOT_HANDLED;
+ /* Ignore */
+ case NETWORK_CONNECTION_EVENT:
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+ }
+
+ class DisconnectingState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ switch (message.what) {
+ case CMD_STOP_DRIVER: /* Stop driver only after disconnect handled */
+ deferMessage(message);
+ break;
+ case CMD_SET_SCAN_MODE:
+ if (message.arg1 == SCAN_ONLY_MODE) {
+ deferMessage(message);
+ }
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+ @Override
+ public void exit() {
+ if (mEnableAllNetworks) {
+ mEnableAllNetworks = false;
+ WifiConfigStore.enableAllNetworks();
+ }
+ }
+ }
+
+ class DisconnectedState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ switch (message.what) {
+ case CMD_SET_SCAN_MODE:
+ if (message.arg1 == SCAN_ONLY_MODE) {
+ WifiNative.setScanResultHandlingCommand(message.arg1);
+ //Supplicant disconnect to prevent further connects
+ WifiNative.disconnectCommand();
+ mIsScanMode = true;
+ transitionTo(mScanModeState);
+ }
+ break;
+ /* Ignore network disconnect */
+ case NETWORK_DISCONNECTION_EVENT:
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+ }
+
+ class SoftApStartedState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ switch(message.what) {
+ case CMD_STOP_AP:
+ Log.d(TAG,"Stopping Soft AP");
+ setWifiApState(WIFI_AP_STATE_DISABLING);
+ try {
+ nwService.stopAccessPoint();
+ } catch(Exception e) {
+ Log.e(TAG, "Exception in stopAccessPoint()");
+ }
+ transitionTo(mDriverLoadedState);
+ break;
+ case CMD_START_AP:
+ Log.d(TAG,"SoftAP set on a running access point");
+ try {
+ nwService.setAccessPoint((WifiConfiguration) message.obj,
+ mInterfaceName,
+ SOFTAP_IFACE);
+ } catch(Exception e) {
+ Log.e(TAG, "Exception in nwService during soft AP set");
+ try {
+ nwService.stopAccessPoint();
+ } catch (Exception ee) {
+ Slog.e(TAG, "Could not stop AP, :" + ee);
+ }
+ sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_AP_STATE_FAILED, 0));
+ }
+ break;
+ /* Fail client mode operation when soft AP is enabled */
+ case CMD_START_SUPPLICANT:
+ Log.e(TAG,"Cannot start supplicant with a running soft AP");
+ setWifiState(WIFI_STATE_UNKNOWN);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+ }
+
+
+ class SupplicantStateTracker extends HierarchicalStateMachine {
+
+ private int mRssiPollToken = 0;
+
+ /**
+ * The max number of the WPA supplicant loop iterations before we
+ * decide that the loop should be terminated:
+ */
+ private static final int MAX_SUPPLICANT_LOOP_ITERATIONS = 4;
+ private int mLoopDetectIndex = 0;
+ private int mLoopDetectCount = 0;
+
+ /**
+ * Supplicant state change commands follow
+ * the ordinal values defined in SupplicantState.java
+ */
+ private static final int DISCONNECTED = 0;
+ private static final int INACTIVE = 1;
+ private static final int SCANNING = 2;
+ private static final int ASSOCIATING = 3;
+ private static final int ASSOCIATED = 4;
+ private static final int FOUR_WAY_HANDSHAKE = 5;
+ private static final int GROUP_HANDSHAKE = 6;
+ private static final int COMPLETED = 7;
+ private static final int DORMANT = 8;
+ private static final int UNINITIALIZED = 9;
+ private static final int INVALID = 10;
+
+ private HierarchicalState mUninitializedState = new UninitializedState();
+ private HierarchicalState mInitializedState = new InitializedState();;
+ private HierarchicalState mInactiveState = new InactiveState();
+ private HierarchicalState mDisconnectState = new DisconnectedState();
+ private HierarchicalState mScanState = new ScanState();
+ private HierarchicalState mConnectState = new ConnectState();
+ private HierarchicalState mHandshakeState = new HandshakeState();
+ private HierarchicalState mCompletedState = new CompletedState();
+ private HierarchicalState mDormantState = new DormantState();
+
+ public SupplicantStateTracker(Context context, Handler target) {
+ super(TAG, target.getLooper());
+
+ addState(mUninitializedState);
+ addState(mInitializedState);
+ addState(mInactiveState, mInitializedState);
+ addState(mDisconnectState, mInitializedState);
+ addState(mScanState, mInitializedState);
+ addState(mConnectState, mInitializedState);
+ addState(mHandshakeState, mConnectState);
+ addState(mCompletedState, mConnectState);
+ addState(mDormantState, mInitializedState);
+
+ setInitialState(mUninitializedState);
+
+ //start the state machine
+ start();
+ }
+
+ public void handleEvent(StateChangeResult stateChangeResult) {
+ SupplicantState newState = (SupplicantState) stateChangeResult.state;
+
+ // Supplicant state change
+ // [31-13] Reserved for future use
+ // [8 - 0] Supplicant state (as defined in SupplicantState.java)
+ // 50023 supplicant_state_changed (custom|1|5)
+ EventLog.writeEvent(EVENTLOG_SUPPLICANT_STATE_CHANGED, newState.ordinal());
+
+ sendMessage(obtainMessage(newState.ordinal(), stateChangeResult));
+ }
+
+ public void resetSupplicantState() {
+ transitionTo(mUninitializedState);
+ }
+
+ private void resetLoopDetection() {
+ mLoopDetectCount = 0;
+ mLoopDetectIndex = 0;
+ }
+
+ private boolean handleTransition(Message msg) {
+ if (DBG) Log.d(TAG, getName() + msg.toString() + "\n");
+ switch (msg.what) {
+ case DISCONNECTED:
+ transitionTo(mDisconnectState);
+ break;
+ case SCANNING:
+ transitionTo(mScanState);
+ break;
+ case ASSOCIATING:
+ StateChangeResult stateChangeResult = (StateChangeResult) msg.obj;
+ /* BSSID is valid only in ASSOCIATING state */
+ mWifiInfo.setBSSID(stateChangeResult.BSSID);
+ //$FALL-THROUGH$
+ case ASSOCIATED:
+ case FOUR_WAY_HANDSHAKE:
+ case GROUP_HANDSHAKE:
+ transitionTo(mHandshakeState);
+ break;
+ case COMPLETED:
+ transitionTo(mCompletedState);
+ break;
+ case DORMANT:
+ transitionTo(mDormantState);
+ break;
+ case INACTIVE:
+ transitionTo(mInactiveState);
+ break;
+ case UNINITIALIZED:
+ case INVALID:
+ transitionTo(mUninitializedState);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ StateChangeResult stateChangeResult = (StateChangeResult) msg.obj;
+ SupplicantState supState = (SupplicantState) stateChangeResult.state;
+ setDetailedState(WifiInfo.getDetailedStateOf(supState));
+ mWifiInfo.setSupplicantState(supState);
+ mWifiInfo.setNetworkId(stateChangeResult.networkId);
+ return HANDLED;
+ }
+
+ /********************************************************
+ * HSM states
+ *******************************************************/
+
+ class InitializedState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ switch (message.what) {
+ case CMD_START_SCAN:
+ WifiNative.scanCommand(message.arg1 == SCAN_ACTIVE);
+ break;
+ default:
+ if (DBG) Log.w(TAG, "Ignoring " + message);
+ break;
+ }
+ return HANDLED;
+ }
+ }
+
+ class UninitializedState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ mNetworkInfo.setIsAvailable(false);
+ resetLoopDetection();
+ mPasswordKeyMayBeIncorrect = false;
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ switch(message.what) {
+ default:
+ if (!handleTransition(message)) {
+ if (DBG) Log.w(TAG, "Ignoring " + message);
+ }
+ break;
+ }
+ return HANDLED;
+ }
+ @Override
+ public void exit() {
+ mNetworkInfo.setIsAvailable(true);
+ }
+ }
+
+ class InactiveState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ Message message = getCurrentMessage();
+ StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
+
+ mNetworkInfo.setIsAvailable(false);
+ resetLoopDetection();
+ mPasswordKeyMayBeIncorrect = false;
+
+ sendSupplicantStateChangedBroadcast(stateChangeResult, false);
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ return handleTransition(message);
+ }
+ @Override
+ public void exit() {
+ mNetworkInfo.setIsAvailable(true);
+ }
+ }
+
+
+ class DisconnectedState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ Message message = getCurrentMessage();
+ StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
+
+ resetLoopDetection();
+
+ /* If a disconnect event happens after a password key failure
+ * event, disable the network
+ */
+ if (mPasswordKeyMayBeIncorrect) {
+ Log.d(TAG, "Failed to authenticate, disabling network " +
+ mWifiInfo.getNetworkId());
+ WifiConfigStore.disableNetwork(mWifiInfo.getNetworkId());
+ mPasswordKeyMayBeIncorrect = false;
+ sendSupplicantStateChangedBroadcast(stateChangeResult, true);
+ }
+ else {
+ sendSupplicantStateChangedBroadcast(stateChangeResult, false);
+ }
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ return handleTransition(message);
+ }
+ }
+
+ class ScanState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ Message message = getCurrentMessage();
+ StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
+
+ mPasswordKeyMayBeIncorrect = false;
+ resetLoopDetection();
+ sendSupplicantStateChangedBroadcast(stateChangeResult, false);
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ return handleTransition(message);
+ }
+ }
+
+ class ConnectState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ switch (message.what) {
+ case CMD_START_SCAN:
+ WifiNative.setScanResultHandlingCommand(SCAN_ONLY_MODE);
+ WifiNative.scanCommand(message.arg1 == SCAN_ACTIVE);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return HANDLED;
+ }
+ }
+
+ class HandshakeState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ final Message message = getCurrentMessage();
+ StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
+
+ if (mLoopDetectIndex > message.what) {
+ mLoopDetectCount++;
+ }
+ if (mLoopDetectCount > MAX_SUPPLICANT_LOOP_ITERATIONS) {
+ WifiConfigStore.disableNetwork(stateChangeResult.networkId);
+ mLoopDetectCount = 0;
+ }
+
+ mLoopDetectIndex = message.what;
+
+ mPasswordKeyMayBeIncorrect = false;
+ sendSupplicantStateChangedBroadcast(stateChangeResult, false);
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ return handleTransition(message);
+ }
+ }
+
+ class CompletedState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ Message message = getCurrentMessage();
+ StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
+
+ mRssiPollToken++;
+ if (mEnableRssiPolling) {
+ sendMessageDelayed(obtainMessage(CMD_RSSI_POLL, mRssiPollToken, 0),
+ POLL_RSSI_INTERVAL_MSECS);
+ }
+
+ resetLoopDetection();
+
+ mPasswordKeyMayBeIncorrect = false;
+ sendSupplicantStateChangedBroadcast(stateChangeResult, false);
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ switch(message.what) {
+ case ASSOCIATING:
+ case ASSOCIATED:
+ case FOUR_WAY_HANDSHAKE:
+ case GROUP_HANDSHAKE:
+ case COMPLETED:
+ break;
+ case CMD_RSSI_POLL:
+ if (message.arg1 == mRssiPollToken) {
+ // Get Info and continue polling
+ requestPolledInfo();
+ sendMessageDelayed(obtainMessage(CMD_RSSI_POLL, mRssiPollToken, 0),
+ POLL_RSSI_INTERVAL_MSECS);
+ } else {
+ // Polling has completed
+ }
+ break;
+ case CMD_ENABLE_RSSI_POLL:
+ mRssiPollToken++;
+ if (mEnableRssiPolling) {
+ // first poll
+ requestPolledInfo();
+ sendMessageDelayed(obtainMessage(CMD_RSSI_POLL, mRssiPollToken, 0),
+ POLL_RSSI_INTERVAL_MSECS);
+ }
+ break;
+ default:
+ return handleTransition(message);
+ }
+ return HANDLED;
+ }
+ }
+
+ class DormantState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ Message message = getCurrentMessage();
+ StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
+
+ resetLoopDetection();
+ mPasswordKeyMayBeIncorrect = false;
+
+ sendSupplicantStateChangedBroadcast(stateChangeResult, false);
+
+ /* TODO: reconnect is now being handled at DHCP failure handling
+ * If we run into issues with staying in Dormant state, might
+ * need a reconnect here
+ */
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ return handleTransition(message);
+ }
+ }
+ }
+}
diff --git a/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java
index 5220713..f8c9bd6 100644
--- a/wifi/java/android/net/wifi/WifiStateTracker.java
+++ b/wifi/java/android/net/wifi/WifiStateTracker.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2010 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.
@@ -16,466 +16,110 @@
package android.net.wifi;
-import static android.net.wifi.WifiManager.WIFI_STATE_DISABLED;
-import static android.net.wifi.WifiManager.WIFI_STATE_DISABLING;
-import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED;
-import static android.net.wifi.WifiManager.WIFI_STATE_ENABLING;
-import static android.net.wifi.WifiManager.WIFI_STATE_UNKNOWN;
-import android.app.ActivityManagerNative;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+import android.net.LinkCapabilities;
import android.net.NetworkInfo;
+import android.net.LinkProperties;
import android.net.NetworkStateTracker;
-import android.net.DhcpInfo;
-import android.net.NetworkUtils;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo.DetailedState;
-import android.net.NetworkInfo.State;
-import android.os.Message;
-import android.os.Parcelable;
import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.SystemProperties;
-import android.os.Looper;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.WorkSource;
-import android.provider.Settings;
-import android.text.TextUtils;
-import android.util.EventLog;
-import android.util.Log;
-import android.util.Config;
-import android.app.Notification;
-import android.app.PendingIntent;
-import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothHeadset;
-import android.bluetooth.BluetoothA2dp;
-import android.content.ContentResolver;
-import android.content.Intent;
-import android.content.Context;
-import android.database.ContentObserver;
-import com.android.internal.app.IBatteryStats;
+import android.os.Message;
-import java.net.UnknownHostException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
/**
- * Track the state of Wifi connectivity. All event handling is done here,
- * and all changes in connectivity state are initiated here.
+ * Track the state of wifi for connectivity service.
*
* @hide
*/
-public class WifiStateTracker extends NetworkStateTracker {
+public class WifiStateTracker implements NetworkStateTracker {
- private static final boolean LOCAL_LOGD = Config.LOGD || false;
-
+ private static final String NETWORKTYPE = "WIFI";
private static final String TAG = "WifiStateTracker";
- // Event log tags (must be in sync with event-log-tags)
- private static final int EVENTLOG_NETWORK_STATE_CHANGED = 50021;
- private static final int EVENTLOG_SUPPLICANT_STATE_CHANGED = 50022;
- private static final int EVENTLOG_DRIVER_STATE_CHANGED = 50023;
- private static final int EVENTLOG_INTERFACE_CONFIGURATION_STATE_CHANGED = 50024;
- private static final int EVENTLOG_SUPPLICANT_CONNECTION_STATE_CHANGED = 50025;
-
- // Event codes
- private static final int EVENT_SUPPLICANT_CONNECTION = 1;
- private static final int EVENT_SUPPLICANT_DISCONNECT = 2;
- private static final int EVENT_SUPPLICANT_STATE_CHANGED = 3;
- private static final int EVENT_NETWORK_STATE_CHANGED = 4;
- private static final int EVENT_SCAN_RESULTS_AVAILABLE = 5;
- private static final int EVENT_INTERFACE_CONFIGURATION_SUCCEEDED = 6;
- private static final int EVENT_INTERFACE_CONFIGURATION_FAILED = 7;
- private static final int EVENT_POLL_INTERVAL = 8;
- private static final int EVENT_DHCP_START = 9;
- private static final int EVENT_DEFERRED_DISCONNECT = 10;
- private static final int EVENT_DEFERRED_RECONNECT = 11;
- /**
- * The driver is started or stopped. The object will be the state: true for
- * started, false for stopped.
- */
- private static final int EVENT_DRIVER_STATE_CHANGED = 12;
- private static final int EVENT_PASSWORD_KEY_MAY_BE_INCORRECT = 13;
- private static final int EVENT_MAYBE_START_SCAN_POST_DISCONNECT = 14;
-
- /**
- * The driver state indication.
- */
- private static final int DRIVER_STARTED = 0;
- private static final int DRIVER_STOPPED = 1;
- private static final int DRIVER_HUNG = 2;
-
- /**
- * Interval in milliseconds between polling for connection
- * status items that are not sent via asynchronous events.
- * An example is RSSI (signal strength).
- */
- private static final int POLL_STATUS_INTERVAL_MSECS = 3000;
-
- /**
- * The max number of the WPA supplicant loop iterations before we
- * decide that the loop should be terminated:
- */
- private static final int MAX_SUPPLICANT_LOOP_ITERATIONS = 4;
-
- /**
- * When a DISCONNECT event is received, we defer handling it to
- * allow for the possibility that the DISCONNECT is about to
- * be followed shortly by a CONNECT to the same network we were
- * just connected to. In such a case, we don't want to report
- * the network as down, nor do we want to reconfigure the network
- * interface, etc. If we get a CONNECT event for another network
- * within the delay window, we immediately handle the pending
- * disconnect before processing the CONNECT.<p/>
- * The five second delay is chosen somewhat arbitrarily, but is
- * meant to cover most of the cases where a DISCONNECT/CONNECT
- * happens to a network.
- */
- private static final int DISCONNECT_DELAY_MSECS = 5000;
- /**
- * When the supplicant goes idle after we do an explicit disconnect
- * following a DHCP failure, we need to kick the supplicant into
- * trying to associate with access points.
- */
- private static final int RECONNECT_DELAY_MSECS = 2000;
-
- /**
- * When the supplicant disconnects from an AP it sometimes forgets
- * to restart scanning. Wait this delay before asking it to start
- * scanning (in case it forgot). 15 sec is the standard delay between
- * scans.
- */
- private static final int KICKSTART_SCANNING_DELAY_MSECS = 15000;
-
- /**
- * The maximum number of times we will retry a connection to an access point
- * for which we have failed in acquiring an IP address from DHCP. A value of
- * N means that we will make N+1 connection attempts in all.
- * <p>
- * See {@link Settings.Secure#WIFI_MAX_DHCP_RETRY_COUNT}. This is the default
- * value if a Settings value is not present.
- */
- private static final int DEFAULT_MAX_DHCP_RETRIES = 9;
-
- private static final int DRIVER_POWER_MODE_AUTO = 0;
- private static final int DRIVER_POWER_MODE_ACTIVE = 1;
-
- /**
- * The current WPA supplicant loop state (used to detect looping behavior):
- */
- private SupplicantState mSupplicantLoopState = SupplicantState.DISCONNECTED;
-
- /**
- * The current number of WPA supplicant loop iterations:
- */
- private int mNumSupplicantLoopIterations = 0;
-
- /**
- * The current number of supplicant state changes. This is used to determine
- * if we've received any new info since we found out it was DISCONNECTED or
- * INACTIVE. If we haven't for X ms, we then request a scan - it should have
- * done that automatically, but sometimes some firmware does not.
- */
- private int mNumSupplicantStateChanges = 0;
-
- /**
- * True if we received an event that that a password-key may be incorrect.
- * If the next incoming supplicant state change event is DISCONNECT,
- * broadcast a message that we have a possible password error and disable
- * the network.
- */
- private boolean mPasswordKeyMayBeIncorrect = false;
-
- public static final int SUPPL_SCAN_HANDLING_NORMAL = 1;
- public static final int SUPPL_SCAN_HANDLING_LIST_ONLY = 2;
-
- private WifiMonitor mWifiMonitor;
- private WifiInfo mWifiInfo;
- private List<ScanResult> mScanResults;
- private WifiManager mWM;
- private boolean mHaveIpAddress;
- private boolean mObtainingIpAddress;
- private boolean mTornDownByConnMgr;
- /**
- * A DISCONNECT event has been received, but processing it
- * is being deferred.
- */
- private boolean mDisconnectPending;
- /**
- * An operation has been performed as a result of which we expect the next event
- * will be a DISCONNECT.
- */
- private boolean mDisconnectExpected;
- private DhcpHandler mDhcpTarget;
- private DhcpInfo mDhcpInfo;
- private int mLastSignalLevel = -1;
- private String mLastBssid;
- private String mLastSsid;
- private int mLastNetworkId = -1;
- private boolean mUseStaticIp = false;
- private int mReconnectCount;
-
- /* Tracks if any network in the configuration is disabled */
- private AtomicBoolean mIsAnyNetworkDisabled = new AtomicBoolean(false);
-
- // used to store the (non-persisted) num determined during device boot
- // (from mcc or other phone info) before the driver is started.
- private int mNumAllowedChannels = 0;
-
- // Variables relating to the 'available networks' notification
-
- /**
- * The icon to show in the 'available networks' notification. This will also
- * be the ID of the Notification given to the NotificationManager.
- */
- private static final int ICON_NETWORKS_AVAILABLE =
- com.android.internal.R.drawable.stat_notify_wifi_in_range;
- /**
- * When a notification is shown, we wait this amount before possibly showing it again.
- */
- private final long NOTIFICATION_REPEAT_DELAY_MS;
- /**
- * Whether the user has set the setting to show the 'available networks' notification.
- */
- private boolean mNotificationEnabled;
- /**
- * Observes the user setting to keep {@link #mNotificationEnabled} in sync.
- */
- private NotificationEnabledSettingObserver mNotificationEnabledSettingObserver;
- /**
- * The {@link System#currentTimeMillis()} must be at least this value for us
- * to show the notification again.
- */
- private long mNotificationRepeatTime;
- /**
- * The Notification object given to the NotificationManager.
- */
- private Notification mNotification;
- /**
- * Whether the notification is being shown, as set by us. That is, if the
- * user cancels the notification, we will not receive the callback so this
- * will still be true. We only guarantee if this is false, then the
- * notification is not showing.
- */
- private boolean mNotificationShown;
- /**
- * The number of continuous scans that must occur before consider the
- * supplicant in a scanning state. This allows supplicant to associate with
- * remembered networks that are in the scan results.
- */
- private static final int NUM_SCANS_BEFORE_ACTUALLY_SCANNING = 3;
- /**
- * The number of scans since the last network state change. When this
- * exceeds {@link #NUM_SCANS_BEFORE_ACTUALLY_SCANNING}, we consider the
- * supplicant to actually be scanning. When the network state changes to
- * something other than scanning, we reset this to 0.
- */
- private int mNumScansSinceNetworkStateChange;
- /**
- * Observes the static IP address settings.
- */
- private SettingsObserver mSettingsObserver;
-
- private boolean mIsScanModeActive;
- private boolean mEnableRssiPolling;
- private boolean mIsHighPerfEnabled;
- private int mPowerModeRefCount = 0;
- private int mOptimizationsDisabledRefCount = 0;
-
- /**
- * One of {@link WifiManager#WIFI_STATE_DISABLED},
- * {@link WifiManager#WIFI_STATE_DISABLING},
- * {@link WifiManager#WIFI_STATE_ENABLED},
- * {@link WifiManager#WIFI_STATE_ENABLING},
- * {@link WifiManager#WIFI_STATE_UNKNOWN}
- *
- * getWifiState() is not synchronized to make sure it's always fast,
- * even when the instance lock is held on other slow operations.
- * Use a atomic variable for state.
- */
- private final AtomicInteger mWifiState = new AtomicInteger(WIFI_STATE_UNKNOWN);
-
- // Wi-Fi run states:
- private static final int RUN_STATE_STARTING = 1;
- private static final int RUN_STATE_RUNNING = 2;
- private static final int RUN_STATE_STOPPING = 3;
- private static final int RUN_STATE_STOPPED = 4;
+ private AtomicBoolean mTeardownRequested = new AtomicBoolean(false);
+ private AtomicBoolean mPrivateDnsRouteSet = new AtomicBoolean(false);
+ private AtomicInteger mDefaultGatewayAddr = new AtomicInteger(0);
+ private AtomicBoolean mDefaultRouteSet = new AtomicBoolean(false);
- private static final String mRunStateNames[] = {
- "Starting",
- "Running",
- "Stopping",
- "Stopped"
- };
- private int mRunState;
+ private LinkProperties mLinkProperties;
+ private LinkCapabilities mLinkCapabilities;
+ private NetworkInfo mNetworkInfo;
- private final IBatteryStats mBatteryStats;
+ /* For sending events to connectivity service handler */
+ private Handler mCsHandler;
+ private Context mContext;
+ private BroadcastReceiver mWifiStateReceiver;
+ private WifiManager mWifiManager;
- private boolean mIsScanOnly;
+ public WifiStateTracker() {
+ mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, NETWORKTYPE, "");
+ mLinkProperties = new LinkProperties();
+ mLinkCapabilities = new LinkCapabilities();
- private BluetoothA2dp mBluetoothA2dp;
-
- private String mInterfaceName;
- private static String LS = System.getProperty("line.separator");
-
- private static String[] sDnsPropNames;
- private Runnable mReleaseWakeLockCallback;
-
- /**
- * Keep track of whether we last told the battery stats we had started.
- */
- private boolean mReportedRunning = false;
-
- /**
- * Most recently set source of starting WIFI.
- */
- private final WorkSource mRunningWifiUids = new WorkSource();
-
- /**
- * The last reported UIDs that were responsible for starting WIFI.
- */
- private final WorkSource mLastRunningWifiUids = new WorkSource();
-
- /**
- * A structure for supplying information about a supplicant state
- * change in the STATE_CHANGE event message that comes from the
- * WifiMonitor
- * thread.
- */
- private static class SupplicantStateChangeResult {
- SupplicantStateChangeResult(int networkId, String BSSID, SupplicantState state) {
- this.state = state;
- this.BSSID = BSSID;
- this.networkId = networkId;
- }
- int networkId;
- String BSSID;
- SupplicantState state;
- }
-
- /**
- * A structure for supplying information about a connection in
- * the CONNECTED event message that comes from the WifiMonitor
- * thread.
- */
- private static class NetworkStateChangeResult {
- NetworkStateChangeResult(DetailedState state, String BSSID, int networkId) {
- this.state = state;
- this.BSSID = BSSID;
- this.networkId = networkId;
- }
- DetailedState state;
- String BSSID;
- int networkId;
+ mNetworkInfo.setIsAvailable(false);
+ setTeardownRequested(false);
}
- public WifiStateTracker(Context context, Handler target) {
- super(context, target, ConnectivityManager.TYPE_WIFI, 0, "WIFI", "");
-
- mWifiInfo = new WifiInfo();
- mWifiMonitor = new WifiMonitor(this);
- mHaveIpAddress = false;
- mObtainingIpAddress = false;
- setTornDownByConnMgr(false);
- mDisconnectPending = false;
- mScanResults = new ArrayList<ScanResult>();
- // Allocate DHCP info object once, and fill it in on each request
- mDhcpInfo = new DhcpInfo();
- mRunState = RUN_STATE_STARTING;
-
- // Setting is in seconds
- NOTIFICATION_REPEAT_DELAY_MS = Settings.Secure.getInt(context.getContentResolver(),
- Settings.Secure.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY, 900) * 1000l;
- mNotificationEnabledSettingObserver = new NotificationEnabledSettingObserver(new Handler());
- mNotificationEnabledSettingObserver.register();
-
- mSettingsObserver = new SettingsObserver(new Handler());
-
- mInterfaceName = SystemProperties.get("wifi.interface", "tiwlan0");
- sDnsPropNames = new String[] {
- "dhcp." + mInterfaceName + ".dns1",
- "dhcp." + mInterfaceName + ".dns2"
- };
- mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo"));
+ public void setTeardownRequested(boolean isRequested) {
+ mTeardownRequested.set(isRequested);
}
- /**
- * Helper method: sets the supplicant state and keeps the network
- * info updated.
- * @param state the new state
- */
- private void setSupplicantState(SupplicantState state) {
- mWifiInfo.setSupplicantState(state);
- updateNetworkInfo();
- checkPollTimer();
- }
-
- public SupplicantState getSupplicantState() {
- return mWifiInfo.getSupplicantState();
+ public boolean isTeardownRequested() {
+ return mTeardownRequested.get();
}
/**
- * Helper method: sets the supplicant state and keeps the network
- * info updated (string version).
- * @param stateName the string name of the new state
+ * Begin monitoring wifi connectivity
*/
- private void setSupplicantState(String stateName) {
- mWifiInfo.setSupplicantState(stateName);
- updateNetworkInfo();
- checkPollTimer();
- }
+ public void startMonitoring(Context context, Handler target) {
+ mCsHandler = target;
+ mContext = context;
- /**
- * Helper method: sets the boolean indicating that the connection
- * manager asked the network to be torn down (and so only the connection
- * manager can set it up again).
- * network info updated.
- * @param flag {@code true} if explicitly disabled.
- */
- private void setTornDownByConnMgr(boolean flag) {
- mTornDownByConnMgr = flag;
- updateNetworkInfo();
+ mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+ filter.addAction(WifiManager.CONFIG_CHANGED_ACTION);
+
+ mWifiStateReceiver = new WifiStateReceiver();
+ mContext.registerReceiver(mWifiStateReceiver, filter);
}
/**
- * Return the IP addresses of the DNS servers available for the WLAN
- * network interface.
- * @return a list of DNS addresses, with no holes.
+ * Disable connectivity to a network
+ * TODO: do away with return value after making MobileDataStateTracker async
*/
- public String[] getNameServers() {
- return getNameServerList(sDnsPropNames);
+ public boolean teardown() {
+ mTeardownRequested.set(true);
+ mWifiManager.stopWifi();
+ return true;
}
/**
- * Return the name of our WLAN network interface.
- * @return the name of our interface.
+ * Re-enable connectivity to a network after a {@link #teardown()}.
+ * TODO: do away with return value after making MobileDataStateTracker async
*/
- public String getInterfaceName() {
- return mInterfaceName;
+ public boolean reconnect() {
+ mTeardownRequested.set(false);
+ mWifiManager.startWifi();
+ return true;
}
/**
- * Return the system properties name associated with the tcp buffer sizes
- * for this network.
+ * Turn the wireless radio off for a network.
+ * @param turnOn {@code true} to turn the radio on, {@code false}
+ * TODO: do away with return value after making MobileDataStateTracker async
*/
- public String getTcpBufferSizesPropName() {
- return "net.tcp.buffersize.wifi";
- }
-
- public void startMonitoring() {
- /*
- * Get a handle on the WifiManager. This cannot be done in our
- * constructor, because the Wifi service is not yet registered.
- */
- mWM = (WifiManager)mContext.getSystemService(Context.WIFI_SERVICE);
- }
-
- public void startEventLoop() {
- mWifiMonitor.startMonitoring();
+ public boolean setRadio(boolean turnOn) {
+ mWifiManager.setWifiEnabled(turnOn);
+ return true;
}
/**
@@ -487,2201 +131,136 @@ public class WifiStateTracker extends NetworkStateTracker {
* unavailable.
* @return {@code true} if Wi-Fi connections are possible
*/
- public synchronized boolean isAvailable() {
- /*
- * TODO: Need to also look at scan results to see whether we're
- * in range of any access points. If we have scan results that
- * are no more than N seconds old, use those, otherwise, initiate
- * a scan and wait for the results. This only matters if we
- * allow mobile to be the preferred network.
- */
- SupplicantState suppState = mWifiInfo.getSupplicantState();
- return suppState != SupplicantState.UNINITIALIZED &&
- suppState != SupplicantState.INACTIVE &&
- (mTornDownByConnMgr || !isDriverStopped());
- }
-
- /**
- * {@inheritDoc}
- * There are currently no defined Wi-Fi subtypes.
- */
- public int getNetworkSubtype() {
- return 0;
- }
-
- /**
- * Helper method: updates the network info object to keep it in sync with
- * the Wi-Fi state tracker.
- */
- private void updateNetworkInfo() {
- mNetworkInfo.setIsAvailable(isAvailable());
- }
-
- /**
- * Report whether the Wi-Fi connection is fully configured for data.
- * @return {@code true} if the {@link SupplicantState} is
- * {@link android.net.wifi.SupplicantState#COMPLETED COMPLETED}.
- */
- public boolean isConnectionCompleted() {
- return mWifiInfo.getSupplicantState() == SupplicantState.COMPLETED;
- }
-
- /**
- * Report whether the Wi-Fi connection has successfully acquired an IP address.
- * @return {@code true} if the Wi-Fi connection has been assigned an IP address.
- */
- public boolean hasIpAddress() {
- return mHaveIpAddress;
- }
-
- /**
- * Send the tracker a notification that a user-entered password key
- * may be incorrect (i.e., caused authentication to fail).
- */
- void notifyPasswordKeyMayBeIncorrect() {
- sendEmptyMessage(EVENT_PASSWORD_KEY_MAY_BE_INCORRECT);
- }
-
- /**
- * Send the tracker a notification that a connection to the supplicant
- * daemon has been established.
- */
- void notifySupplicantConnection() {
- sendEmptyMessage(EVENT_SUPPLICANT_CONNECTION);
- }
-
- /**
- * Send the tracker a notification that the state of the supplicant
- * has changed.
- * @param networkId the configured network on which the state change occurred
- * @param newState the new {@code SupplicantState}
- */
- void notifyStateChange(int networkId, String BSSID, SupplicantState newState) {
- Message msg = Message.obtain(
- this, EVENT_SUPPLICANT_STATE_CHANGED,
- new SupplicantStateChangeResult(networkId, BSSID, newState));
- msg.sendToTarget();
- }
-
- /**
- * Send the tracker a notification that the state of Wifi connectivity
- * has changed.
- * @param networkId the configured network on which the state change occurred
- * @param newState the new network state
- * @param BSSID when the new state is {@link DetailedState#CONNECTED
- * NetworkInfo.DetailedState.CONNECTED},
- * this is the MAC address of the access point. Otherwise, it
- * is {@code null}.
- */
- void notifyStateChange(DetailedState newState, String BSSID, int networkId) {
- Message msg = Message.obtain(
- this, EVENT_NETWORK_STATE_CHANGED,
- new NetworkStateChangeResult(newState, BSSID, networkId));
- msg.sendToTarget();
- }
-
- /**
- * Send the tracker a notification that a scan has completed, and results
- * are available.
- */
- void notifyScanResultsAvailable() {
- // reset the supplicant's handling of scan results to "normal" mode
- setScanResultHandling(SUPPL_SCAN_HANDLING_NORMAL);
- sendEmptyMessage(EVENT_SCAN_RESULTS_AVAILABLE);
- }
-
- /**
- * Send the tracker a notification that we can no longer communicate with
- * the supplicant daemon.
- */
- void notifySupplicantLost() {
- sendEmptyMessage(EVENT_SUPPLICANT_DISCONNECT);
- }
-
- /**
- * Send the tracker a notification that the Wi-Fi driver has been stopped.
- */
- void notifyDriverStopped() {
- mRunState = RUN_STATE_STOPPED;
-
- // Send a driver stopped message to our handler
- Message.obtain(this, EVENT_DRIVER_STATE_CHANGED, DRIVER_STOPPED, 0).sendToTarget();
- }
-
- /**
- * Send the tracker a notification that the Wi-Fi driver has been restarted after
- * having been stopped.
- */
- void notifyDriverStarted() {
- // Send a driver started message to our handler
- Message.obtain(this, EVENT_DRIVER_STATE_CHANGED, DRIVER_STARTED, 0).sendToTarget();
- }
-
- /**
- * Send the tracker a notification that the Wi-Fi driver has hung and needs restarting.
- */
- void notifyDriverHung() {
- // Send a driver hanged message to our handler
- Message.obtain(this, EVENT_DRIVER_STATE_CHANGED, DRIVER_HUNG, 0).sendToTarget();
- }
-
- /**
- * Set the interval timer for polling connection information
- * that is not delivered asynchronously.
- */
- private synchronized void checkPollTimer() {
- if (mEnableRssiPolling &&
- mWifiInfo.getSupplicantState() == SupplicantState.COMPLETED &&
- !hasMessages(EVENT_POLL_INTERVAL)) {
- sendEmptyMessageDelayed(EVENT_POLL_INTERVAL, POLL_STATUS_INTERVAL_MSECS);
- }
- }
-
- /**
- * TODO: mRunState is not synchronized in some places
- * address this as part of re-architect.
- *
- * TODO: We are exposing an additional public synchronized call
- * for a wakelock optimization in WifiService. Remove it
- * when we handle the wakelock in ConnectivityService.
- */
- public synchronized boolean isDriverStopped() {
- return mRunState == RUN_STATE_STOPPED || mRunState == RUN_STATE_STOPPING;
- }
-
- public void updateBatteryWorkSourceLocked(WorkSource newSource) {
- try {
- if (newSource != null) {
- mRunningWifiUids.set(newSource);
- }
- if (mRunState == RUN_STATE_RUNNING) {
- if (mReportedRunning) {
- // If the work source has changed since last time, need
- // to remove old work from battery stats.
- if (mLastRunningWifiUids.diff(mRunningWifiUids)) {
- mBatteryStats.noteWifiRunningChanged(mLastRunningWifiUids,
- mRunningWifiUids);
- mLastRunningWifiUids.set(mRunningWifiUids);
- }
- } else {
- // Now being started, report it.
- mBatteryStats.noteWifiRunning(mRunningWifiUids);
- mLastRunningWifiUids.set(mRunningWifiUids);
- mReportedRunning = true;
- }
- } else if (mRunState == RUN_STATE_STOPPED) {
- if (mReportedRunning) {
- // Last reported we were running, time to stop.
- mBatteryStats.noteWifiStopped(mLastRunningWifiUids);
- mLastRunningWifiUids.clear();
- mReportedRunning = false;
- }
- } else {
- // State in transition -- nothing to update yet.
- }
- } catch (RemoteException ignore) {
- }
- }
-
- /**
- * Set the run state to either "normal" or "scan-only".
- * @param scanOnlyMode true if the new mode should be scan-only.
- */
- public synchronized void setScanOnlyMode(boolean scanOnlyMode) {
- // do nothing unless scan-only mode is changing
- if (mIsScanOnly != scanOnlyMode) {
- int scanType = (scanOnlyMode ?
- SUPPL_SCAN_HANDLING_LIST_ONLY : SUPPL_SCAN_HANDLING_NORMAL);
- if (LOCAL_LOGD) Log.v(TAG, "Scan-only mode changing to " + scanOnlyMode + " scanType=" + scanType);
- if (setScanResultHandling(scanType)) {
- mIsScanOnly = scanOnlyMode;
- if (!isDriverStopped()) {
- if (scanOnlyMode) {
- disconnect();
- } else {
- reconnectCommand();
- }
- }
- }
- }
- }
-
- /**
- * Set suspend mode optimizations. These include:
- * - packet filtering
- * - turn off roaming
- * - DTIM settings
- *
- * Uses reference counting to keep the suspend optimizations disabled
- * as long as one entity wants optimizations disabled.
- *
- * For example, WifiLock can keep suspend optimizations disabled
- * or the user setting (wifi never sleeps) can keep suspend optimizations
- * disabled. As long as one entity wants it disabled, it should stay
- * that way
- *
- * @param enabled true if optimizations need enabled, false otherwise
- */
- public synchronized void setSuspendModeOptimizations(boolean enabled) {
-
- /* It is good to plumb suspend optimization enable
- * or disable even if ref count indicates already done
- * since we could have a case of previous failure.
- */
- if (!enabled) {
- mOptimizationsDisabledRefCount++;
- } else {
- mOptimizationsDisabledRefCount--;
- if (mOptimizationsDisabledRefCount > 0) {
- return;
- } else {
- /* Keep refcount from becoming negative */
- mOptimizationsDisabledRefCount = 0;
- }
- }
-
- if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
- return;
- }
-
- WifiNative.setSuspendOptimizationsCommand(enabled);
- }
-
-
- /**
- * Set high performance mode of operation. This would mean
- * use active power mode and disable suspend optimizations
- * @param enabled true if enabled, false otherwise
- */
- public synchronized void setHighPerfMode(boolean enabled) {
- if (mIsHighPerfEnabled != enabled) {
- if (enabled) {
- setPowerMode(DRIVER_POWER_MODE_ACTIVE);
- setSuspendModeOptimizations(false);
- } else {
- setPowerMode(DRIVER_POWER_MODE_AUTO);
- setSuspendModeOptimizations(true);
- }
- mIsHighPerfEnabled = enabled;
- Log.d(TAG,"high performance mode: " + enabled);
- }
- }
-
-
- private void checkIsBluetoothPlaying() {
- boolean isBluetoothPlaying = false;
- Set<BluetoothDevice> connected = mBluetoothA2dp.getConnectedSinks();
-
- for (BluetoothDevice device : connected) {
- if (mBluetoothA2dp.getSinkState(device) == BluetoothA2dp.STATE_PLAYING) {
- isBluetoothPlaying = true;
- break;
- }
- }
- setBluetoothScanMode(isBluetoothPlaying);
- }
-
- public void enableRssiPolling(boolean enable) {
- if (mEnableRssiPolling != enable) {
- mEnableRssiPolling = enable;
- checkPollTimer();
- }
- }
-
- /**
- * We release the wakelock in WifiService
- * using a timer.
- *
- * TODO:
- * Releasing wakelock using both timer and
- * a call from ConnectivityService requires
- * a rethink. We had problems where WifiService
- * could keep a wakelock forever if we delete
- * messages in the asynchronous call
- * from ConnectivityService
- */
- @Override
- public void releaseWakeLock() {
- }
-
- /**
- * Tracks the WPA supplicant states to detect "loop" situations.
- * @param newSupplicantState The new WPA supplicant state.
- * @return {@code true} if the supplicant loop should be stopped
- * and {@code false} if it should continue.
- */
- private boolean isSupplicantLooping(SupplicantState newSupplicantState) {
- if (SupplicantState.ASSOCIATING.ordinal() <= newSupplicantState.ordinal()
- && newSupplicantState.ordinal() < SupplicantState.COMPLETED.ordinal()) {
- if (mSupplicantLoopState != newSupplicantState) {
- if (newSupplicantState.ordinal() < mSupplicantLoopState.ordinal()) {
- ++mNumSupplicantLoopIterations;
- }
-
- mSupplicantLoopState = newSupplicantState;
- }
- } else if (newSupplicantState == SupplicantState.COMPLETED) {
- resetSupplicantLoopState();
- }
-
- return mNumSupplicantLoopIterations >= MAX_SUPPLICANT_LOOP_ITERATIONS;
- }
-
- /**
- * Resets the WPA supplicant loop state.
- */
- private void resetSupplicantLoopState() {
- mNumSupplicantLoopIterations = 0;
- }
-
- @Override
- public void handleMessage(Message msg) {
- Intent intent;
-
- switch (msg.what) {
- case EVENT_SUPPLICANT_CONNECTION:
- mRunState = RUN_STATE_RUNNING;
- synchronized (this) {
- updateBatteryWorkSourceLocked(null);
- }
- checkUseStaticIp();
- /* Reset notification state on new connection */
- resetNotificationTimer();
- /*
- * DHCP requests are blocking, so run them in a separate thread.
- */
- HandlerThread dhcpThread = new HandlerThread("DHCP Handler Thread");
- dhcpThread.start();
- mDhcpTarget = new DhcpHandler(dhcpThread.getLooper(), this);
- mIsScanModeActive = true;
- mIsHighPerfEnabled = false;
- mOptimizationsDisabledRefCount = 0;
- mPowerModeRefCount = 0;
- mTornDownByConnMgr = false;
- mLastBssid = null;
- mLastSsid = null;
- mIsAnyNetworkDisabled.set(false);
- requestConnectionInfo();
- SupplicantState supplState = mWifiInfo.getSupplicantState();
- /**
- * The MAC address isn't going to change, so just request it
- * once here.
- */
- String macaddr = getMacAddress();
-
- if (macaddr != null) {
- mWifiInfo.setMacAddress(macaddr);
- }
- if (LOCAL_LOGD) Log.v(TAG, "Connection to supplicant established, state=" +
- supplState);
- // Wi-Fi supplicant connection state changed:
- // [31- 2] Reserved for future use
- // [ 1- 0] Connected to supplicant (1), disconnected from supplicant (0) ,
- // or supplicant died (2)
- EventLog.writeEvent(EVENTLOG_SUPPLICANT_CONNECTION_STATE_CHANGED, 1);
- /*
- * The COMPLETED state change from the supplicant may have occurred
- * in between polling for supplicant availability, in which case
- * we didn't perform a DHCP request to get an IP address.
- */
- if (supplState == SupplicantState.COMPLETED) {
- mLastBssid = mWifiInfo.getBSSID();
- mLastSsid = mWifiInfo.getSSID();
- configureInterface();
- }
- if (ActivityManagerNative.isSystemReady()) {
- intent = new Intent(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
- intent.putExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, true);
- mContext.sendBroadcast(intent);
- }
- if (supplState == SupplicantState.COMPLETED && mHaveIpAddress) {
- setDetailedState(DetailedState.CONNECTED);
- } else {
- setDetailedState(WifiInfo.getDetailedStateOf(supplState));
- }
- /*
- * Filter out multicast packets. This saves battery power, since
- * the CPU doesn't have to spend time processing packets that
- * are going to end up being thrown away.
- */
- mWM.initializeMulticastFiltering();
-
- if (mBluetoothA2dp == null) {
- mBluetoothA2dp = new BluetoothA2dp(mContext);
- }
- checkIsBluetoothPlaying();
-
- // initialize this after the supplicant is alive
- setNumAllowedChannels();
- break;
-
- case EVENT_SUPPLICANT_DISCONNECT:
- mRunState = RUN_STATE_STOPPED;
- synchronized (this) {
- updateBatteryWorkSourceLocked(null);
- }
- boolean died = mWifiState.get() != WIFI_STATE_DISABLED &&
- mWifiState.get() != WIFI_STATE_DISABLING;
- if (died) {
- if (LOCAL_LOGD) Log.v(TAG, "Supplicant died unexpectedly");
- } else {
- if (LOCAL_LOGD) Log.v(TAG, "Connection to supplicant lost");
- }
- // Wi-Fi supplicant connection state changed:
- // [31- 2] Reserved for future use
- // [ 1- 0] Connected to supplicant (1), disconnected from supplicant (0) ,
- // or supplicant died (2)
- EventLog.writeEvent(EVENTLOG_SUPPLICANT_CONNECTION_STATE_CHANGED, died ? 2 : 0);
- closeSupplicantConnection();
-
- if (died) {
- resetConnections(true);
- }
- // When supplicant dies, kill the DHCP thread
- if (mDhcpTarget != null) {
- mDhcpTarget.getLooper().quit();
- mDhcpTarget = null;
- }
- mContext.removeStickyBroadcast(new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION));
- if (ActivityManagerNative.isSystemReady()) {
- intent = new Intent(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
- intent.putExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, false);
- mContext.sendBroadcast(intent);
- }
- setDetailedState(DetailedState.DISCONNECTED);
- setSupplicantState(SupplicantState.UNINITIALIZED);
- mHaveIpAddress = false;
- mObtainingIpAddress = false;
- if (died) {
- mWM.setWifiEnabled(false);
- }
- break;
-
- case EVENT_MAYBE_START_SCAN_POST_DISCONNECT:
- // Only do this if we haven't gotten a new supplicant status since the timer
- // started
- if (mNumSupplicantStateChanges == msg.arg1) {
- scan(false); // do a passive scan
- }
- break;
-
- case EVENT_SUPPLICANT_STATE_CHANGED:
- mNumSupplicantStateChanges++;
- SupplicantStateChangeResult supplicantStateResult =
- (SupplicantStateChangeResult) msg.obj;
- SupplicantState newState = supplicantStateResult.state;
- SupplicantState currentState = mWifiInfo.getSupplicantState();
-
- // Wi-Fi supplicant state changed:
- // [31- 6] Reserved for future use
- // [ 5- 0] Supplicant state ordinal (as defined by SupplicantState)
- int eventLogParam = (newState.ordinal() & 0x3f);
- EventLog.writeEvent(EVENTLOG_SUPPLICANT_STATE_CHANGED, eventLogParam);
-
- if (LOCAL_LOGD) Log.v(TAG, "Changing supplicant state: "
- + currentState +
- " ==> " + newState);
-
- int networkId = supplicantStateResult.networkId;
-
- /**
- * The SupplicantState BSSID value is valid in ASSOCIATING state only.
- * The NetworkState BSSID value comes upon a successful connection.
- */
- if (supplicantStateResult.state == SupplicantState.ASSOCIATING) {
- mLastBssid = supplicantStateResult.BSSID;
- }
- /*
- * If we get disconnect or inactive we need to start our
- * watchdog timer to start a scan
- */
- if (newState == SupplicantState.DISCONNECTED ||
- newState == SupplicantState.INACTIVE) {
- sendMessageDelayed(obtainMessage(EVENT_MAYBE_START_SCAN_POST_DISCONNECT,
- mNumSupplicantStateChanges, 0), KICKSTART_SCANNING_DELAY_MSECS);
- }
-
-
- /*
- * Did we get to DISCONNECTED state due to an
- * authentication (password) failure?
- */
- boolean failedToAuthenticate = false;
- if (newState == SupplicantState.DISCONNECTED) {
- failedToAuthenticate = mPasswordKeyMayBeIncorrect;
- }
- mPasswordKeyMayBeIncorrect = false;
-
- /*
- * Keep track of the supplicant state and check if we should
- * disable the network
- */
- boolean disabledNetwork = false;
- if (isSupplicantLooping(newState)) {
- if (LOCAL_LOGD) {
- Log.v(TAG,
- "Stop WPA supplicant loop and disable network");
- }
- disabledNetwork = wifiManagerDisableNetwork(networkId);
- }
-
- if (disabledNetwork) {
- /*
- * Reset the loop state if we disabled the network
- */
- resetSupplicantLoopState();
- } else if (newState != currentState ||
- (newState == SupplicantState.DISCONNECTED && isDriverStopped())) {
- setSupplicantState(newState);
- if (newState == SupplicantState.DORMANT) {
- DetailedState newDetailedState;
- Message reconnectMsg = obtainMessage(EVENT_DEFERRED_RECONNECT, mLastBssid);
- if (mIsScanOnly || mRunState == RUN_STATE_STOPPING) {
- newDetailedState = DetailedState.IDLE;
- } else {
- newDetailedState = DetailedState.FAILED;
- }
- handleDisconnectedState(newDetailedState, true);
- /**
- * If we were associated with a network (networkId != -1),
- * assume we reached this state because of a failed attempt
- * to acquire an IP address, and attempt another connection
- * and IP address acquisition in RECONNECT_DELAY_MSECS
- * milliseconds.
- */
- if (mRunState == RUN_STATE_RUNNING && !mIsScanOnly && networkId != -1) {
- sendMessageDelayed(reconnectMsg, RECONNECT_DELAY_MSECS);
- } else if (mRunState == RUN_STATE_STOPPING) {
- stopDriver();
- } else if (mRunState == RUN_STATE_STARTING && !mIsScanOnly) {
- reconnectCommand();
- }
- } else if (newState == SupplicantState.DISCONNECTED) {
- mHaveIpAddress = false;
- if (isDriverStopped() || mDisconnectExpected) {
- handleDisconnectedState(DetailedState.DISCONNECTED, true);
- } else {
- scheduleDisconnect();
- }
- } else if (newState != SupplicantState.COMPLETED && !mDisconnectPending) {
- /**
- * Ignore events that don't change the connectivity state,
- * such as WPA rekeying operations.
- */
- if (!(currentState == SupplicantState.COMPLETED &&
- (newState == SupplicantState.ASSOCIATING ||
- newState == SupplicantState.ASSOCIATED ||
- newState == SupplicantState.FOUR_WAY_HANDSHAKE ||
- newState == SupplicantState.GROUP_HANDSHAKE))) {
- setDetailedState(WifiInfo.getDetailedStateOf(newState));
- }
- }
-
- mDisconnectExpected = false;
- intent = new Intent(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
- | Intent.FLAG_RECEIVER_REPLACE_PENDING);
- intent.putExtra(WifiManager.EXTRA_NEW_STATE, (Parcelable)newState);
- if (failedToAuthenticate) {
- if (LOCAL_LOGD) Log.d(TAG, "Failed to authenticate, disabling network " + networkId);
- wifiManagerDisableNetwork(networkId);
- intent.putExtra(
- WifiManager.EXTRA_SUPPLICANT_ERROR,
- WifiManager.ERROR_AUTHENTICATING);
- }
- mContext.sendStickyBroadcast(intent);
- }
- break;
-
- case EVENT_NETWORK_STATE_CHANGED:
- /*
- * Each CONNECT or DISCONNECT generates a pair of events.
- * One is a supplicant state change event, and the other
- * is a network state change event. For connects, the
- * supplicant event always arrives first, followed by
- * the network state change event. Only the latter event
- * has the BSSID, which we are interested in capturing.
- * For disconnects, the order is the opposite -- the
- * network state change event comes first, followed by
- * the supplicant state change event.
- */
- NetworkStateChangeResult result =
- (NetworkStateChangeResult) msg.obj;
-
- // Wi-Fi network state changed:
- // [31- 6] Reserved for future use
- // [ 5- 0] Detailed state ordinal (as defined by NetworkInfo.DetailedState)
- eventLogParam = (result.state.ordinal() & 0x3f);
- EventLog.writeEvent(EVENTLOG_NETWORK_STATE_CHANGED, eventLogParam);
-
- if (LOCAL_LOGD) Log.v(TAG, "New network state is " + result.state);
- /*
- * If we're in scan-only mode, don't advance the state machine, and
- * don't report the state change to clients.
- */
- if (mIsScanOnly) {
- if (LOCAL_LOGD) Log.v(TAG, "Dropping event in scan-only mode");
- break;
- }
- if (result.state != DetailedState.SCANNING) {
- /*
- * Reset the scan count since there was a network state
- * change. This could be from supplicant trying to associate
- * with a network.
- */
- mNumScansSinceNetworkStateChange = 0;
- }
- /*
- * If the supplicant sent us a CONNECTED event, we don't
- * want to send out an indication of overall network
- * connectivity until we have our IP address. If the
- * supplicant sent us a DISCONNECTED event, we delay
- * sending a notification in case a reconnection to
- * the same access point occurs within a short time.
- */
- if (result.state == DetailedState.DISCONNECTED) {
- if (mWifiInfo.getSupplicantState() != SupplicantState.DORMANT) {
- scheduleDisconnect();
- }
- break;
- }
- requestConnectionStatus(mWifiInfo);
- if (!(result.state == DetailedState.CONNECTED &&
- (!mHaveIpAddress || mDisconnectPending))) {
- setDetailedState(result.state);
- }
-
- if (result.state == DetailedState.CONNECTED) {
- /*
- * Remove the 'available networks' notification when we
- * successfully connect to a network.
- */
- setNotificationVisible(false, 0, false, 0);
- boolean wasDisconnectPending = mDisconnectPending;
- cancelDisconnect();
- /*
- * The connection is fully configured as far as link-level
- * connectivity is concerned, but we may still need to obtain
- * an IP address.
- */
- if (wasDisconnectPending) {
- DetailedState saveState = getNetworkInfo().getDetailedState();
- handleDisconnectedState(DetailedState.DISCONNECTED, false);
- setDetailedStateInternal(saveState);
- }
-
- configureInterface();
- mLastBssid = result.BSSID;
- mLastSsid = mWifiInfo.getSSID();
- mLastNetworkId = result.networkId;
- if (mHaveIpAddress) {
- setDetailedState(DetailedState.CONNECTED);
- } else {
- setDetailedState(DetailedState.OBTAINING_IPADDR);
- }
- }
- sendNetworkStateChangeBroadcast(mWifiInfo.getBSSID());
- break;
-
- case EVENT_SCAN_RESULTS_AVAILABLE:
- if (ActivityManagerNative.isSystemReady()) {
- mContext.sendBroadcast(new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
- }
- sendScanResultsAvailable();
- /**
- * On receiving the first scan results after connecting to
- * the supplicant, switch scan mode over to passive.
- */
- setScanMode(false);
- break;
-
- case EVENT_POLL_INTERVAL:
- if (mWifiInfo.getSupplicantState() != SupplicantState.UNINITIALIZED) {
- requestPolledInfo(mWifiInfo, true);
- checkPollTimer();
- }
- break;
-
- case EVENT_DEFERRED_DISCONNECT:
- if (mWifiInfo.getSupplicantState() != SupplicantState.UNINITIALIZED) {
- handleDisconnectedState(DetailedState.DISCONNECTED, true);
- }
- break;
-
- case EVENT_DEFERRED_RECONNECT:
- /**
- * mLastBssid can be null when there is a reconnect
- * request on the first BSSID we connect to
- */
- String BSSID = (msg.obj != null) ? msg.obj.toString() : null;
- /**
- * If we've exceeded the maximum number of retries for reconnecting
- * to a given network, disable the network
- */
- if (mWifiInfo.getSupplicantState() != SupplicantState.UNINITIALIZED) {
- if (++mReconnectCount > getMaxDhcpRetries()) {
- if (LOCAL_LOGD) {
- Log.d(TAG, "Failed reconnect count: " +
- mReconnectCount + " Disabling " + BSSID);
- }
- mWM.disableNetwork(mLastNetworkId);
- }
- reconnectCommand();
- }
- break;
-
- case EVENT_INTERFACE_CONFIGURATION_SUCCEEDED:
- /**
- * Since this event is sent from another thread, it might have been
- * sent after we closed our connection to the supplicant in the course
- * of disabling Wi-Fi. In that case, we should just ignore the event.
- */
- if (mWifiInfo.getSupplicantState() == SupplicantState.UNINITIALIZED) {
- break;
- }
- mReconnectCount = 0;
- mHaveIpAddress = true;
- mObtainingIpAddress = false;
- mWifiInfo.setIpAddress(mDhcpInfo.ipAddress);
- mLastSignalLevel = -1; // force update of signal strength
- if (mNetworkInfo.getDetailedState() != DetailedState.CONNECTED) {
- setDetailedState(DetailedState.CONNECTED);
- sendNetworkStateChangeBroadcast(mWifiInfo.getBSSID());
- } else {
- msg = mTarget.obtainMessage(EVENT_CONFIGURATION_CHANGED, mNetworkInfo);
- msg.sendToTarget();
- }
- if (LOCAL_LOGD) Log.v(TAG, "IP configuration: " + mDhcpInfo);
- // Wi-Fi interface configuration state changed:
- // [31- 1] Reserved for future use
- // [ 0- 0] Interface configuration succeeded (1) or failed (0)
- EventLog.writeEvent(EVENTLOG_INTERFACE_CONFIGURATION_STATE_CHANGED, 1);
-
- // We've connected successfully, so allow the notification again in the future
- resetNotificationTimer();
- break;
-
- case EVENT_INTERFACE_CONFIGURATION_FAILED:
- if (mWifiInfo.getSupplicantState() != SupplicantState.UNINITIALIZED) {
- // Wi-Fi interface configuration state changed:
- // [31- 1] Reserved for future use
- // [ 0- 0] Interface configuration succeeded (1) or failed (0)
- EventLog.writeEvent(EVENTLOG_INTERFACE_CONFIGURATION_STATE_CHANGED, 0);
- mHaveIpAddress = false;
- mWifiInfo.setIpAddress(0);
- mObtainingIpAddress = false;
- disconnect();
- }
- break;
-
- case EVENT_DRIVER_STATE_CHANGED:
- // Wi-Fi driver state changed:
- // 0 STARTED
- // 1 STOPPED
- // 2 HUNG
- EventLog.writeEvent(EVENTLOG_DRIVER_STATE_CHANGED, msg.arg1);
-
- switch (msg.arg1) {
- case DRIVER_STARTED:
- /**
- * Set the number of allowed radio channels according
- * to the system setting, since it gets reset by the
- * driver upon changing to the STARTED state.
- */
- setNumAllowedChannels();
- synchronized (this) {
- if (mRunState == RUN_STATE_STARTING) {
- mRunState = RUN_STATE_RUNNING;
- if (!mIsScanOnly) {
- reconnectCommand();
- } else {
- // In some situations, supplicant needs to be kickstarted to
- // start the background scanning
- scan(true);
- }
- }
- }
- break;
- case DRIVER_HUNG:
- Log.e(TAG, "Wifi Driver reports HUNG - reloading.");
- /**
- * restart the driver - toggle off and on
- */
- mWM.setWifiEnabled(false);
- mWM.setWifiEnabled(true);
- break;
- }
- synchronized (this) {
- updateBatteryWorkSourceLocked(null);
- }
- break;
-
- case EVENT_PASSWORD_KEY_MAY_BE_INCORRECT:
- mPasswordKeyMayBeIncorrect = true;
- break;
- }
- }
-
- private boolean wifiManagerDisableNetwork(int networkId) {
- boolean disabledNetwork = false;
- if (0 <= networkId) {
- disabledNetwork = mWM.disableNetwork(networkId);
- if (LOCAL_LOGD) {
- if (disabledNetwork) {
- Log.v(TAG, "Disabled network: " + networkId);
- }
- }
- }
- if (LOCAL_LOGD) {
- if (!disabledNetwork) {
- Log.e(TAG, "Failed to disable network:" +
- " invalid network id: " + networkId);
- }
- }
- return disabledNetwork;
- }
-
- private void configureInterface() {
- checkPollTimer();
- mLastSignalLevel = -1;
- if (!mUseStaticIp) {
- if (!mHaveIpAddress && !mObtainingIpAddress) {
- mObtainingIpAddress = true;
- mDhcpTarget.sendEmptyMessage(EVENT_DHCP_START);
- }
- } else {
- int event;
- if (NetworkUtils.configureInterface(mInterfaceName, mDhcpInfo)) {
- mHaveIpAddress = true;
- event = EVENT_INTERFACE_CONFIGURATION_SUCCEEDED;
- if (LOCAL_LOGD) Log.v(TAG, "Static IP configuration succeeded");
- } else {
- mHaveIpAddress = false;
- event = EVENT_INTERFACE_CONFIGURATION_FAILED;
- if (LOCAL_LOGD) Log.v(TAG, "Static IP configuration failed");
- }
- sendEmptyMessage(event);
- }
- }
-
- /**
- * Reset our IP state and send out broadcasts following a disconnect.
- * @param newState the {@code DetailedState} to set. Should be either
- * {@code DISCONNECTED} or {@code FAILED}.
- * @param disableInterface indicates whether the interface should
- * be disabled
- */
- private void handleDisconnectedState(DetailedState newState, boolean disableInterface) {
- if (mDisconnectPending) {
- cancelDisconnect();
- }
- mDisconnectExpected = false;
- resetConnections(disableInterface);
- setDetailedState(newState);
- sendNetworkStateChangeBroadcast(mLastBssid);
- mWifiInfo.setBSSID(null);
- mLastBssid = null;
- mLastSsid = null;
- mDisconnectPending = false;
- }
-
- /**
- * Resets the Wi-Fi Connections by clearing any state, resetting any sockets
- * using the interface, stopping DHCP, and disabling the interface.
- */
- public void resetConnections(boolean disableInterface) {
- if (LOCAL_LOGD) Log.d(TAG, "Reset connections and stopping DHCP");
- mHaveIpAddress = false;
- mObtainingIpAddress = false;
- mWifiInfo.setIpAddress(0);
-
- /*
- * Reset connection depends on both the interface and the IP assigned,
- * so it should be done before any chance of the IP being lost.
- */
- NetworkUtils.resetConnections(mInterfaceName);
-
- // Stop DHCP
- if (mDhcpTarget != null) {
- mDhcpTarget.setCancelCallback(true);
- mDhcpTarget.removeMessages(EVENT_DHCP_START);
- }
- if (!NetworkUtils.stopDhcp(mInterfaceName)) {
- Log.e(TAG, "Could not stop DHCP");
- }
-
- /**
- * Interface is re-enabled in the supplicant
- * when moving out of ASSOCIATING state
- */
- if(disableInterface) {
- if (LOCAL_LOGD) Log.d(TAG, "Disabling interface");
- NetworkUtils.disableInterface(mInterfaceName);
- }
- }
-
- /**
- * The supplicant is reporting that we are disconnected from the current
- * access point. Often, however, a disconnect will be followed very shortly
- * by a reconnect to the same access point. Therefore, we delay resetting
- * the connection's IP state for a bit.
- */
- private void scheduleDisconnect() {
- mDisconnectPending = true;
- if (!hasMessages(EVENT_DEFERRED_DISCONNECT)) {
- sendEmptyMessageDelayed(EVENT_DEFERRED_DISCONNECT, DISCONNECT_DELAY_MSECS);
- }
- }
-
- private void cancelDisconnect() {
- mDisconnectPending = false;
- removeMessages(EVENT_DEFERRED_DISCONNECT);
- }
-
- public DhcpInfo getDhcpInfo() {
- return mDhcpInfo;
- }
-
- public synchronized List<ScanResult> getScanResultsList() {
- return mScanResults;
- }
-
- public synchronized void setScanResultsList(List<ScanResult> scanList) {
- mScanResults = scanList;
- }
-
- /**
- * Get status information for the current connection, if any.
- * @return a {@link WifiInfo} object containing information about the current connection
- */
- public WifiInfo requestConnectionInfo() {
- requestConnectionStatus(mWifiInfo);
- requestPolledInfo(mWifiInfo, false);
- return mWifiInfo;
- }
-
- private void requestConnectionStatus(WifiInfo info) {
- String reply = status();
- if (reply == null) {
- return;
- }
- /*
- * Parse the reply from the supplicant to the status command, and update
- * local state accordingly. The reply is a series of lines of the form
- * "name=value".
- */
- String SSID = null;
- String BSSID = null;
- String suppState = null;
- int netId = -1;
- String[] lines = reply.split("\n");
- for (String line : lines) {
- String[] prop = line.split(" *= *", 2);
- if (prop.length < 2)
- continue;
- String name = prop[0];
- String value = prop[1];
- if (name.equalsIgnoreCase("id"))
- netId = Integer.parseInt(value);
- else if (name.equalsIgnoreCase("ssid"))
- SSID = value;
- else if (name.equalsIgnoreCase("bssid"))
- BSSID = value;
- else if (name.equalsIgnoreCase("wpa_state"))
- suppState = value;
- }
- info.setNetworkId(netId);
- info.setSSID(SSID);
- info.setBSSID(BSSID);
- /*
- * We only set the supplicant state if the previous state was
- * UNINITIALIZED. This should only happen when we first connect to
- * the supplicant. Once we're connected, we should always receive
- * an event upon any state change, but in this case, we want to
- * make sure any listeners are made aware of the state change.
- */
- if (mWifiInfo.getSupplicantState() == SupplicantState.UNINITIALIZED && suppState != null)
- setSupplicantState(suppState);
- }
-
- /**
- * Get the dynamic information that is not reported via events.
- * @param info the object into which the information should be captured.
- */
- private synchronized void requestPolledInfo(WifiInfo info, boolean polling)
- {
- int newRssi = (polling ? getRssiApprox() : getRssi());
- if (newRssi != -1 && -200 < newRssi && newRssi < 256) { // screen out invalid values
- /* some implementations avoid negative values by adding 256
- * so we need to adjust for that here.
- */
- if (newRssi > 0) newRssi -= 256;
- info.setRssi(newRssi);
- /*
- * Rather then sending the raw RSSI out every time it
- * changes, we precalculate the signal level that would
- * be displayed in the status bar, and only send the
- * broadcast if that much more coarse-grained number
- * changes. This cuts down greatly on the number of
- * broadcasts, at the cost of not informing others
- * interested in RSSI of all the changes in signal
- * level.
- */
- // TODO: The second arg to the call below needs to be a symbol somewhere, but
- // it's actually the size of an array of icons that's private
- // to StatusBar Policy.
- int newSignalLevel = WifiManager.calculateSignalLevel(newRssi, 4);
- if (newSignalLevel != mLastSignalLevel) {
- sendRssiChangeBroadcast(newRssi);
- }
- mLastSignalLevel = newSignalLevel;
- } else {
- info.setRssi(-200);
- }
- int newLinkSpeed = getLinkSpeed();
- if (newLinkSpeed != -1) {
- info.setLinkSpeed(newLinkSpeed);
- }
- }
-
- private void sendRssiChangeBroadcast(final int newRssi) {
- if (ActivityManagerNative.isSystemReady()) {
- Intent intent = new Intent(WifiManager.RSSI_CHANGED_ACTION);
- intent.putExtra(WifiManager.EXTRA_NEW_RSSI, newRssi);
- mContext.sendBroadcast(intent);
- }
- }
-
- private void sendNetworkStateChangeBroadcast(String bssid) {
- Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
- | Intent.FLAG_RECEIVER_REPLACE_PENDING);
- intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, mNetworkInfo);
- if (bssid != null)
- intent.putExtra(WifiManager.EXTRA_BSSID, bssid);
- mContext.sendStickyBroadcast(intent);
- }
-
- /**
- * Disable Wi-Fi connectivity by stopping the driver.
- */
- public boolean teardown() {
- if (!mTornDownByConnMgr) {
- if (disconnectAndStop()) {
- setTornDownByConnMgr(true);
- return true;
- } else {
- return false;
- }
- } else {
- return true;
- }
- }
-
- /**
- * Reenable Wi-Fi connectivity by restarting the driver.
- */
- public boolean reconnect() {
- if (mTornDownByConnMgr) {
- if (restart()) {
- setTornDownByConnMgr(false);
- return true;
- } else {
- return false;
- }
- } else {
- return true;
- }
- }
-
- /**
- * We want to stop the driver, but if we're connected to a network,
- * we first want to disconnect, so that the supplicant is always in
- * a known state (DISCONNECTED) when the driver is stopped.
- * @return {@code true} if the operation succeeds, which means that the
- * disconnect or stop command was initiated.
- */
- public synchronized boolean disconnectAndStop() {
- boolean ret = true;;
- if (mRunState != RUN_STATE_STOPPING && mRunState != RUN_STATE_STOPPED) {
- // Take down any open network notifications
- setNotificationVisible(false, 0, false, 0);
-
- if (mWifiInfo.getSupplicantState() == SupplicantState.DORMANT) {
- ret = stopDriver();
- } else {
- ret = disconnect();
- }
- mRunState = RUN_STATE_STOPPING;
- }
- return ret;
- }
-
- public synchronized boolean restart() {
- if (mRunState == RUN_STATE_STOPPED) {
- mRunState = RUN_STATE_STARTING;
- resetConnections(true);
- return startDriver();
- } else if (mRunState == RUN_STATE_STOPPING) {
- mRunState = RUN_STATE_STARTING;
- }
- return true;
- }
-
- public int getWifiState() {
- return mWifiState.get();
- }
-
- public void setWifiState(int wifiState) {
- mWifiState.set(wifiState);
- }
-
- public boolean isAnyNetworkDisabled() {
- return mIsAnyNetworkDisabled.get();
- }
-
- /**
- * The WifiNative interface functions are listed below.
- * The only native call that is not synchronized on
- * WifiStateTracker is waitForEvent() which waits on a
- * seperate monitor channel.
- *
- * All supplicant commands need the wifi to be in an
- * enabled state. This can be done by checking the
- * mWifiState to be WIFI_STATE_ENABLED.
- *
- * All commands that can cause commands to driver
- * initiated need the driver state to be started.
- * This is done by checking isDriverStopped() to
- * be false.
- */
-
- /**
- * Load the driver and firmware
- *
- * @return {@code true} if the operation succeeds, {@code false} otherwise
- */
- public synchronized boolean loadDriver() {
- return WifiNative.loadDriver();
- }
-
- /**
- * Unload the driver and firmware
- *
- * @return {@code true} if the operation succeeds, {@code false} otherwise
- */
- public synchronized boolean unloadDriver() {
- return WifiNative.unloadDriver();
- }
-
- /**
- * Check the supplicant config and
- * start the supplicant daemon
- *
- * @return {@code true} if the operation succeeds, {@code false} otherwise
- */
- public synchronized boolean startSupplicant() {
- return WifiNative.startSupplicant();
- }
-
- /**
- * Stop the supplicant daemon
- *
- * @return {@code true} if the operation succeeds, {@code false} otherwise
- */
- public synchronized boolean stopSupplicant() {
- return WifiNative.stopSupplicant();
- }
-
- /**
- * Establishes two channels - control channel for commands
- * and monitor channel for notifying WifiMonitor
- *
- * @return {@code true} if the operation succeeds, {@code false} otherwise
- */
- public synchronized boolean connectToSupplicant() {
- return WifiNative.connectToSupplicant();
- }
-
- /**
- * Close the control/monitor channels to supplicant
- */
- public synchronized void closeSupplicantConnection() {
- WifiNative.closeSupplicantConnection();
- }
-
- /**
- * Check if the supplicant is alive
- *
- * @return {@code true} if the operation succeeds, {@code false} otherwise
- */
- public synchronized boolean ping() {
- if (mWifiState.get() != WIFI_STATE_ENABLED) {
- return false;
- }
- return WifiNative.pingCommand();
- }
-
- /**
- * initiate an active or passive scan
- *
- * @param forceActive true if it is a active scan
- * @return {@code true} if the operation succeeds, {@code false} otherwise
- */
- public synchronized boolean scan(boolean forceActive) {
- if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
- return false;
- }
- return WifiNative.scanCommand(forceActive);
- }
-
- /**
- * Specifies whether the supplicant or driver
- * take care of initiating scan and doing AP selection
- *
- * @param mode
- * SUPPL_SCAN_HANDLING_NORMAL
- * SUPPL_SCAN_HANDLING_LIST_ONLY
- * @return {@code true} if the operation succeeds, {@code false} otherwise
- */
- public synchronized boolean setScanResultHandling(int mode) {
- if (mWifiState.get() != WIFI_STATE_ENABLED) {
- return false;
- }
- return WifiNative.setScanResultHandlingCommand(mode);
- }
-
- /**
- * Fetch the scan results from the supplicant
- *
- * @return example result string
- * 00:bb:cc:dd:cc:ee 2427 166 [WPA-EAP-TKIP][WPA2-EAP-CCMP] Net1
- * 00:bb:cc:dd:cc:ff 2412 165 [WPA-EAP-TKIP][WPA2-EAP-CCMP] Net2
- */
- public synchronized String scanResults() {
- if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
- return null;
- }
- return WifiNative.scanResultsCommand();
- }
-
- /**
- * Set the scan mode - active or passive
- *
- * @return {@code true} if the operation succeeds, {@code false} otherwise
- */
- public synchronized boolean setScanMode(boolean isScanModeActive) {
- if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
- return false;
- }
- if (mIsScanModeActive != isScanModeActive) {
- return WifiNative.setScanModeCommand(mIsScanModeActive = isScanModeActive);
- }
- return true;
- }
-
- /**
- * Disconnect from Access Point
- *
- * @return {@code true} if the operation succeeds, {@code false} otherwise
- */
- public synchronized boolean disconnect() {
- if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
- return false;
- }
- return WifiNative.disconnectCommand();
- }
-
- /**
- * Initiate a reconnection to AP
- *
- * @return {@code true} if the operation succeeds, {@code false} otherwise
- */
- public synchronized boolean reconnectCommand() {
- if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
- return false;
- }
- return WifiNative.reconnectCommand();
- }
-
- /**
- * Add a network
- *
- * @return network id of the new network
- */
- public synchronized int addNetwork() {
- if (mWifiState.get() != WIFI_STATE_ENABLED) {
- return -1;
- }
- return WifiNative.addNetworkCommand();
- }
-
- /**
- * Delete a network
- *
- * @param networkId id of the network to be removed
- * @return {@code true} if the operation succeeds, {@code false} otherwise
- */
- public synchronized boolean removeNetwork(int networkId) {
- if (mWifiState.get() != WIFI_STATE_ENABLED) {
- return false;
- }
- return mDisconnectExpected = WifiNative.removeNetworkCommand(networkId);
- }
-
- /**
- * Enable a network
- *
- * @param netId network id of the network
- * @param disableOthers true, if all other networks have to be disabled
- * @return {@code true} if the operation succeeds, {@code false} otherwise
- */
- public synchronized boolean enableNetwork(int netId, boolean disableOthers) {
- if (mWifiState.get() != WIFI_STATE_ENABLED) {
- return false;
- }
- if (disableOthers) mIsAnyNetworkDisabled.set(true);
- return WifiNative.enableNetworkCommand(netId, disableOthers);
- }
-
- /**
- * Enable all networks
- *
- * @param networks list of configured networks
- */
- public synchronized void enableAllNetworks(List<WifiConfiguration> networks) {
- if (mWifiState.get() != WIFI_STATE_ENABLED) {
- return;
- }
- mIsAnyNetworkDisabled.set(false);
- for (WifiConfiguration config : networks) {
- if (config.status == WifiConfiguration.Status.DISABLED) {
- WifiNative.enableNetworkCommand(config.networkId, false);
- }
- }
- }
-
- /**
- * Disable a network
- *
- * @param netId network id of the network
- * @return {@code true} if the operation succeeds, {@code false} otherwise
- */
- public synchronized boolean disableNetwork(int netId) {
- if (mWifiState.get() != WIFI_STATE_ENABLED) {
- return false;
- }
- mIsAnyNetworkDisabled.set(true);
- return WifiNative.disableNetworkCommand(netId);
- }
-
- /**
- * Initiate a re-association in supplicant
- *
- * @return {@code true} if the operation succeeds, {@code false} otherwise
- */
- public synchronized boolean reassociate() {
- if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
- return false;
- }
- return WifiNative.reassociateCommand();
- }
-
- /**
- * Blacklist a BSSID. This will avoid the AP if there are
- * alternate APs to connect
- *
- * @param bssid BSSID of the network
- * @return {@code true} if the operation succeeds, {@code false} otherwise
- */
- public synchronized boolean addToBlacklist(String bssid) {
- if (mWifiState.get() != WIFI_STATE_ENABLED) {
- return false;
- }
- return WifiNative.addToBlacklistCommand(bssid);
- }
-
- /**
- * Clear the blacklist list
- *
- * @return {@code true} if the operation succeeds, {@code false} otherwise
- */
- public synchronized boolean clearBlacklist() {
- if (mWifiState.get() != WIFI_STATE_ENABLED) {
- return false;
- }
- return WifiNative.clearBlacklistCommand();
- }
-
- /**
- * List all configured networks
- *
- * @return list of networks or null on failure
- */
- public synchronized String listNetworks() {
- if (mWifiState.get() != WIFI_STATE_ENABLED) {
- return null;
- }
- return WifiNative.listNetworksCommand();
- }
-
- /**
- * Get network setting by name
- *
- * @param netId network id of the network
- * @param name network variable key
- * @return value corresponding to key
- */
- public synchronized String getNetworkVariable(int netId, String name) {
- if (mWifiState.get() != WIFI_STATE_ENABLED) {
- return null;
- }
- return WifiNative.getNetworkVariableCommand(netId, name);
- }
-
- /**
- * Set network setting by name
- *
- * @param netId network id of the network
- * @param name network variable key
- * @param value network variable value
- * @return {@code true} if the operation succeeds, {@code false} otherwise
- */
- public synchronized boolean setNetworkVariable(int netId, String name, String value) {
- if (mWifiState.get() != WIFI_STATE_ENABLED) {
- return false;
- }
- return WifiNative.setNetworkVariableCommand(netId, name, value);
- }
-
- /**
- * Get detailed status of the connection
- *
- * @return Example status result
- * bssid=aa:bb:cc:dd:ee:ff
- * ssid=TestNet
- * id=3
- * pairwise_cipher=NONE
- * group_cipher=NONE
- * key_mgmt=NONE
- * wpa_state=COMPLETED
- * ip_address=X.X.X.X
- */
- public synchronized String status() {
- if (mWifiState.get() != WIFI_STATE_ENABLED) {
- return null;
- }
- return WifiNative.statusCommand();
- }
-
- /**
- * Get RSSI to currently connected network
- *
- * @return RSSI value, -1 on failure
- */
- public synchronized int getRssi() {
- if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
- return -1;
- }
- return WifiNative.getRssiApproxCommand();
- }
-
- /**
- * Get approx RSSI to currently connected network
- *
- * @return RSSI value, -1 on failure
- */
- public synchronized int getRssiApprox() {
- if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
- return -1;
- }
- return WifiNative.getRssiApproxCommand();
- }
-
- /**
- * Get link speed to currently connected network
- *
- * @return link speed, -1 on failure
- */
- public synchronized int getLinkSpeed() {
- if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
- return -1;
- }
- return WifiNative.getLinkSpeedCommand();
- }
-
- /**
- * Get MAC address of radio
- *
- * @return MAC address, null on failure
- */
- public synchronized String getMacAddress() {
- if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
- return null;
- }
- return WifiNative.getMacAddressCommand();
- }
-
- /**
- * Start driver
- *
- * @return {@code true} if the operation succeeds, {@code false} otherwise
- */
- public synchronized boolean startDriver() {
- if (mWifiState.get() != WIFI_STATE_ENABLED) {
- return false;
- }
- return WifiNative.startDriverCommand();
- }
-
- /**
- * Stop driver
- *
- * @return {@code true} if the operation succeeds, {@code false} otherwise
- */
- public synchronized boolean stopDriver() {
- /* Driver stop should not happen only when supplicant event
- * DRIVER_STOPPED has already been handled */
- if (mWifiState.get() != WIFI_STATE_ENABLED || mRunState == RUN_STATE_STOPPED) {
- return false;
- }
- return WifiNative.stopDriverCommand();
- }
-
- /**
- * Start packet filtering
- *
- * @return {@code true} if the operation succeeds, {@code false} otherwise
- */
- public synchronized boolean startPacketFiltering() {
- if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
- return false;
- }
- return WifiNative.startPacketFiltering();
- }
-
- /**
- * Stop packet filtering
- *
- * @return {@code true} if the operation succeeds, {@code false} otherwise
- */
- public synchronized boolean stopPacketFiltering() {
- if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
- return false;
- }
- return WifiNative.stopPacketFiltering();
+ public boolean isAvailable() {
+ return mNetworkInfo.isAvailable();
}
/**
- * Get power mode
- * @return power mode
+ * Tells the underlying networking system that the caller wants to
+ * begin using the named feature. The interpretation of {@code feature}
+ * is completely up to each networking implementation.
+ * @param feature the name of the feature to be used
+ * @param callingPid the process ID of the process that is issuing this request
+ * @param callingUid the user ID of the process that is issuing this request
+ * @return an integer value representing the outcome of the request.
+ * The interpretation of this value is specific to each networking
+ * implementation+feature combination, except that the value {@code -1}
+ * always indicates failure.
+ * TODO: needs to go away
*/
- public synchronized int getPowerMode() {
- if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) {
- return -1;
- }
- return WifiNative.getPowerModeCommand();
+ public int startUsingNetworkFeature(String feature, int callingPid, int callingUid) {
+ return -1;
}
/**
- * Set power mode
- * @param mode
- * DRIVER_POWER_MODE_AUTO
- * DRIVER_POWER_MODE_ACTIVE
- *
- * Uses reference counting to keep power mode active
- * as long as one entity wants power mode to be active.
- *
- * For example, WifiLock high perf mode can keep power mode active
- * or a DHCP session can keep it active. As long as one entity wants
- * it enabled, it should stay that way
- *
+ * Tells the underlying networking system that the caller is finished
+ * using the named feature. The interpretation of {@code feature}
+ * is completely up to each networking implementation.
+ * @param feature the name of the feature that is no longer needed.
+ * @param callingPid the process ID of the process that is issuing this request
+ * @param callingUid the user ID of the process that is issuing this request
+ * @return an integer value representing the outcome of the request.
+ * The interpretation of this value is specific to each networking
+ * implementation+feature combination, except that the value {@code -1}
+ * always indicates failure.
+ * TODO: needs to go away
*/
- private synchronized void setPowerMode(int mode) {
-
- /* It is good to plumb power mode change
- * even if ref count indicates already done
- * since we could have a case of previous failure.
- */
- switch(mode) {
- case DRIVER_POWER_MODE_ACTIVE:
- mPowerModeRefCount++;
- break;
- case DRIVER_POWER_MODE_AUTO:
- mPowerModeRefCount--;
- if (mPowerModeRefCount > 0) {
- return;
- } else {
- /* Keep refcount from becoming negative */
- mPowerModeRefCount = 0;
- }
- break;
- }
-
- if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
- return;
- }
-
- WifiNative.setPowerModeCommand(mode);
+ public int stopUsingNetworkFeature(String feature, int callingPid, int callingUid) {
+ return -1;
}
/**
- * Set the number of allowed radio frequency channels from the system
- * setting value, if any.
- * @return {@code true} if the operation succeeds, {@code false} otherwise, e.g.,
- * the number of channels is invalid.
+ * Check if private DNS route is set for the network
*/
- public synchronized boolean setNumAllowedChannels() {
- if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
- return false;
- }
- try {
- return setNumAllowedChannels(
- Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS));
- } catch (Settings.SettingNotFoundException e) {
- if (mNumAllowedChannels != 0) {
- WifiNative.setNumAllowedChannelsCommand(mNumAllowedChannels);
- }
- // otherwise, use the driver default
- }
- return true;
+ public boolean isPrivateDnsRouteSet() {
+ return mPrivateDnsRouteSet.get();
}
/**
- * Set the number of radio frequency channels that are allowed to be used
- * in the current regulatory domain.
- * @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 outside the valid range.
+ * Set a flag indicating private DNS route is set
*/
- public synchronized boolean setNumAllowedChannels(int numChannels) {
- if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
- return false;
- }
- mNumAllowedChannels = numChannels;
- return WifiNative.setNumAllowedChannelsCommand(numChannels);
+ public void privateDnsRouteSet(boolean enabled) {
+ mPrivateDnsRouteSet.set(enabled);
}
/**
- * Get number of allowed channels
- *
- * @return channel count, -1 on failure
+ * Fetch NetworkInfo for the network
*/
- public synchronized int getNumAllowedChannels() {
- if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
- return -1;
- }
- return WifiNative.getNumAllowedChannelsCommand();
+ public NetworkInfo getNetworkInfo() {
+ return mNetworkInfo;
}
/**
- * Set bluetooth coex mode:
- *
- * @param mode
- * BLUETOOTH_COEXISTENCE_MODE_ENABLED
- * BLUETOOTH_COEXISTENCE_MODE_DISABLED
- * BLUETOOTH_COEXISTENCE_MODE_SENSE
- * @return {@code true} if the operation succeeds, {@code false} otherwise
+ * Fetch LinkProperties for the network
*/
- public synchronized boolean setBluetoothCoexistenceMode(int mode) {
- if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
- return false;
- }
- return WifiNative.setBluetoothCoexistenceModeCommand(mode);
+ public LinkProperties getLinkProperties() {
+ return new LinkProperties(mLinkProperties);
}
/**
- * Enable or disable Bluetooth coexistence scan mode. When this mode is on,
- * some of the low-level scan parameters used by the driver are changed to
- * reduce interference with A2DP streaming.
+ * A capability is an Integer/String pair, the capabilities
+ * are defined in the class LinkSocket#Key.
*
- * @param isBluetoothPlaying whether to enable or disable this mode
+ * @return a copy of this connections capabilities, may be empty but never null.
*/
- public synchronized void setBluetoothScanMode(boolean isBluetoothPlaying) {
- if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
- return;
- }
- WifiNative.setBluetoothCoexistenceScanModeCommand(isBluetoothPlaying);
+ public LinkCapabilities getLinkCapabilities() {
+ return new LinkCapabilities(mLinkCapabilities);
}
/**
- * Save configuration on supplicant
- *
- * @return {@code true} if the operation succeeds, {@code false} otherwise
+ * Fetch default gateway address for the network
*/
- public synchronized boolean saveConfig() {
- if (mWifiState.get() != WIFI_STATE_ENABLED) {
- return false;
- }
- return WifiNative.saveConfigCommand();
+ public int getDefaultGatewayAddr() {
+ return mDefaultGatewayAddr.get();
}
/**
- * Reload the configuration from file
- *
- * @return {@code true} if the operation succeeds, {@code false} otherwise
+ * Check if default route is set
*/
- public synchronized boolean reloadConfig() {
- if (mWifiState.get() != WIFI_STATE_ENABLED) {
- return false;
- }
- return WifiNative.reloadConfigCommand();
- }
-
- public boolean setRadio(boolean turnOn) {
- return mWM.setWifiEnabled(turnOn);
+ public boolean isDefaultRouteSet() {
+ return mDefaultRouteSet.get();
}
/**
- * {@inheritDoc}
- * There are currently no Wi-Fi-specific features supported.
- * @param feature the name of the feature
- * @return {@code -1} indicating failure, always
+ * Set a flag indicating default route is set for the network
*/
- public int startUsingNetworkFeature(String feature, int callingPid, int callingUid) {
- return -1;
+ public void defaultRouteSet(boolean enabled) {
+ mDefaultRouteSet.set(enabled);
}
/**
- * {@inheritDoc}
- * There are currently no Wi-Fi-specific features supported.
- * @param feature the name of the feature
- * @return {@code -1} indicating failure, always
+ * Return the system properties name associated with the tcp buffer sizes
+ * for this network.
*/
- public int stopUsingNetworkFeature(String feature, int callingPid, int callingUid) {
- return -1;
+ public String getTcpBufferSizesPropName() {
+ return "net.tcp.buffersize.wifi";
}
- @Override
- public void interpretScanResultsAvailable() {
-
- // If we shouldn't place a notification on available networks, then
- // don't bother doing any of the following
- if (!mNotificationEnabled) return;
-
- NetworkInfo networkInfo = getNetworkInfo();
-
- State state = networkInfo.getState();
- if ((state == NetworkInfo.State.DISCONNECTED)
- || (state == NetworkInfo.State.UNKNOWN)) {
-
- // Look for an open network
- List<ScanResult> scanResults = getScanResultsList();
- if (scanResults != null) {
- int numOpenNetworks = 0;
- for (int i = scanResults.size() - 1; i >= 0; i--) {
- ScanResult scanResult = scanResults.get(i);
-
- if (TextUtils.isEmpty(scanResult.capabilities)) {
- numOpenNetworks++;
- }
+ private class WifiStateReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction().equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
+ mNetworkInfo = (NetworkInfo) intent.getParcelableExtra(
+ WifiManager.EXTRA_NETWORK_INFO);
+ mLinkProperties = intent.getParcelableExtra(
+ WifiManager.EXTRA_LINK_PROPERTIES);
+ if (mLinkProperties == null) {
+ mLinkProperties = new LinkProperties();
}
-
- if (numOpenNetworks > 0) {
- if (++mNumScansSinceNetworkStateChange >= NUM_SCANS_BEFORE_ACTUALLY_SCANNING) {
- /*
- * We've scanned continuously at least
- * NUM_SCANS_BEFORE_NOTIFICATION times. The user
- * probably does not have a remembered network in range,
- * since otherwise supplicant would have tried to
- * associate and thus resetting this counter.
- */
- setNotificationVisible(true, numOpenNetworks, false, 0);
- }
- return;
+ mLinkCapabilities = intent.getParcelableExtra(
+ WifiManager.EXTRA_LINK_CAPABILITIES);
+ if (mLinkCapabilities == null) {
+ mLinkCapabilities = new LinkCapabilities();
}
+ Message msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo);
+ msg.sendToTarget();
+ } else if (intent.getAction().equals(WifiManager.CONFIG_CHANGED_ACTION)) {
+ mLinkProperties = (LinkProperties) intent.getParcelableExtra(
+ WifiManager.EXTRA_LINK_PROPERTIES);
+ Message msg = mCsHandler.obtainMessage(EVENT_CONFIGURATION_CHANGED, mNetworkInfo);
+ msg.sendToTarget();
}
}
-
- // No open networks in range, remove the notification
- setNotificationVisible(false, 0, false, 0);
- }
-
- /**
- * Display or don't display a notification that there are open Wi-Fi networks.
- * @param visible {@code true} if notification should be visible, {@code false} otherwise
- * @param numNetworks the number networks seen
- * @param force {@code true} to force notification to be shown/not-shown,
- * even if it is already shown/not-shown.
- * @param delay time in milliseconds after which the notification should be made
- * visible or invisible.
- */
- public void setNotificationVisible(boolean visible, int numNetworks, boolean force, int delay) {
-
- // Since we use auto cancel on the notification, when the
- // mNetworksAvailableNotificationShown is true, the notification may
- // have actually been canceled. However, when it is false we know
- // for sure that it is not being shown (it will not be shown any other
- // place than here)
-
- // If it should be hidden and it is already hidden, then noop
- if (!visible && !mNotificationShown && !force) {
- return;
- }
-
- Message message;
- if (visible) {
-
- // Not enough time has passed to show the notification again
- if (System.currentTimeMillis() < mNotificationRepeatTime) {
- return;
- }
-
- if (mNotification == null) {
- // Cache the Notification mainly so we can remove the
- // EVENT_NOTIFICATION_CHANGED message with this Notification from
- // the queue later
- mNotification = new Notification();
- mNotification.when = 0;
- mNotification.icon = ICON_NETWORKS_AVAILABLE;
- mNotification.flags = Notification.FLAG_AUTO_CANCEL;
- mNotification.contentIntent = PendingIntent.getActivity(mContext, 0,
- new Intent(WifiManager.ACTION_PICK_WIFI_NETWORK), 0);
- }
-
- CharSequence title = mContext.getResources().getQuantityText(
- com.android.internal.R.plurals.wifi_available, numNetworks);
- CharSequence details = mContext.getResources().getQuantityText(
- com.android.internal.R.plurals.wifi_available_detailed, numNetworks);
- mNotification.tickerText = title;
- mNotification.setLatestEventInfo(mContext, title, details, mNotification.contentIntent);
-
- mNotificationRepeatTime = System.currentTimeMillis() + NOTIFICATION_REPEAT_DELAY_MS;
-
- message = mTarget.obtainMessage(EVENT_NOTIFICATION_CHANGED, 1,
- ICON_NETWORKS_AVAILABLE, mNotification);
-
- } else {
-
- // Remove any pending messages to show the notification
- mTarget.removeMessages(EVENT_NOTIFICATION_CHANGED, mNotification);
-
- message = mTarget.obtainMessage(EVENT_NOTIFICATION_CHANGED, 0, ICON_NETWORKS_AVAILABLE);
- }
-
- mTarget.sendMessageDelayed(message, delay);
-
- mNotificationShown = visible;
- }
-
- /**
- * Clears variables related to tracking whether a notification has been
- * shown recently.
- * <p>
- * After calling this method, the timer that prevents notifications from
- * being shown too often will be cleared.
- */
- private void resetNotificationTimer() {
- mNotificationRepeatTime = 0;
- mNumScansSinceNetworkStateChange = 0;
- }
-
- @Override
- public String toString() {
- StringBuffer sb = new StringBuffer();
- sb.append("interface ").append(mInterfaceName);
- sb.append(" runState=");
- if (mRunState >= 1 && mRunState <= mRunStateNames.length) {
- sb.append(mRunStateNames[mRunState-1]);
- } else {
- sb.append(mRunState);
- }
- sb.append(LS).append(mWifiInfo).append(LS);
- sb.append(mDhcpInfo).append(LS);
- sb.append("haveIpAddress=").append(mHaveIpAddress).
- append(", obtainingIpAddress=").append(mObtainingIpAddress).
- append(", scanModeActive=").append(mIsScanModeActive).append(LS).
- append("lastSignalLevel=").append(mLastSignalLevel).
- append(", explicitlyDisabled=").append(mTornDownByConnMgr);
- return sb.toString();
}
- private class DhcpHandler extends Handler {
-
- private Handler mTarget;
-
- /**
- * Whether to skip the DHCP result callback to the target. For example,
- * this could be set if the network we were requesting an IP for has
- * since been disconnected.
- * <p>
- * Note: There is still a chance where the client's intended DHCP
- * request not being canceled. For example, we are request for IP on
- * A, and he queues request for IP on B, and then cancels the request on
- * B while we're still requesting from A.
- */
- private boolean mCancelCallback;
-
- /**
- * Instance of the bluetooth headset helper. This needs to be created
- * early because there is a delay before it actually 'connects', as
- * noted by its javadoc. If we check before it is connected, it will be
- * in an error state and we will not disable coexistence.
- */
- private BluetoothHeadset mBluetoothHeadset;
-
- public DhcpHandler(Looper looper, Handler target) {
- super(looper);
- mTarget = target;
-
- mBluetoothHeadset = new BluetoothHeadset(mContext, null);
- }
-
- public void handleMessage(Message msg) {
- int event;
-
- switch (msg.what) {
- case EVENT_DHCP_START:
-
- boolean modifiedBluetoothCoexistenceMode = false;
- int powerMode = DRIVER_POWER_MODE_AUTO;
-
- if (shouldDisableCoexistenceMode()) {
- /*
- * There are problems setting the Wi-Fi driver's power
- * mode to active when bluetooth coexistence mode is
- * enabled or sense.
- * <p>
- * We set Wi-Fi to active mode when
- * obtaining an IP address because we've found
- * compatibility issues with some routers with low power
- * mode.
- * <p>
- * In order for this active power mode to properly be set,
- * we disable coexistence mode until we're done with
- * obtaining an IP address. One exception is if we
- * are currently connected to a headset, since disabling
- * coexistence would interrupt that connection.
- */
- modifiedBluetoothCoexistenceMode = true;
-
- // Disable the coexistence mode
- setBluetoothCoexistenceMode(
- WifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED);
- }
-
- powerMode = getPowerMode();
- if (powerMode < 0) {
- // Handle the case where supplicant driver does not support
- // getPowerModeCommand.
- powerMode = DRIVER_POWER_MODE_AUTO;
- }
- if (powerMode != DRIVER_POWER_MODE_ACTIVE) {
- setPowerMode(DRIVER_POWER_MODE_ACTIVE);
- }
-
- synchronized (this) {
- // A new request is being made, so assume we will callback
- mCancelCallback = false;
- }
- Log.d(TAG, "DhcpHandler: DHCP request started");
- if (NetworkUtils.runDhcp(mInterfaceName, mDhcpInfo)) {
- event = EVENT_INTERFACE_CONFIGURATION_SUCCEEDED;
- if (LOCAL_LOGD) Log.v(TAG, "DhcpHandler: DHCP request succeeded");
- } else {
- event = EVENT_INTERFACE_CONFIGURATION_FAILED;
- Log.i(TAG, "DhcpHandler: DHCP request failed: " +
- NetworkUtils.getDhcpError());
- }
-
- if (powerMode != DRIVER_POWER_MODE_ACTIVE) {
- setPowerMode(powerMode);
- }
-
- if (modifiedBluetoothCoexistenceMode) {
- // Set the coexistence mode back to its default value
- setBluetoothCoexistenceMode(
- WifiNative.BLUETOOTH_COEXISTENCE_MODE_SENSE);
- }
-
- synchronized (this) {
- if (!mCancelCallback) {
- mTarget.sendEmptyMessage(event);
- }
- }
- break;
- }
- }
-
- public synchronized void setCancelCallback(boolean cancelCallback) {
- mCancelCallback = cancelCallback;
- }
-
- /**
- * Whether to disable coexistence mode while obtaining IP address. This
- * logic will return true only if the current bluetooth
- * headset/handsfree state is disconnected. This means if it is in an
- * error state, we will NOT disable coexistence mode to err on the side
- * of safety.
- *
- * @return Whether to disable coexistence mode.
- */
- private boolean shouldDisableCoexistenceMode() {
- int state = mBluetoothHeadset.getState(mBluetoothHeadset.getCurrentHeadset());
- return state == BluetoothHeadset.STATE_DISCONNECTED;
- }
- }
-
- private void checkUseStaticIp() {
- mUseStaticIp = false;
- final ContentResolver cr = mContext.getContentResolver();
- try {
- if (Settings.System.getInt(cr, Settings.System.WIFI_USE_STATIC_IP) == 0) {
- return;
- }
- } catch (Settings.SettingNotFoundException e) {
- return;
- }
-
- try {
- String addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_IP);
- if (addr != null) {
- mDhcpInfo.ipAddress = stringToIpAddr(addr);
- } else {
- return;
- }
- addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_GATEWAY);
- if (addr != null) {
- mDhcpInfo.gateway = stringToIpAddr(addr);
- } else {
- return;
- }
- addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_NETMASK);
- if (addr != null) {
- mDhcpInfo.netmask = stringToIpAddr(addr);
- } else {
- return;
- }
- addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_DNS1);
- if (addr != null) {
- mDhcpInfo.dns1 = stringToIpAddr(addr);
- } else {
- return;
- }
- addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_DNS2);
- if (addr != null) {
- mDhcpInfo.dns2 = stringToIpAddr(addr);
- } else {
- mDhcpInfo.dns2 = 0;
- }
- } catch (UnknownHostException e) {
- return;
- }
- mUseStaticIp = true;
- }
-
- private static int stringToIpAddr(String addrString) throws UnknownHostException {
- try {
- String[] parts = addrString.split("\\.");
- if (parts.length != 4) {
- throw new UnknownHostException(addrString);
- }
-
- int a = Integer.parseInt(parts[0]) ;
- int b = Integer.parseInt(parts[1]) << 8;
- int c = Integer.parseInt(parts[2]) << 16;
- int d = Integer.parseInt(parts[3]) << 24;
-
- return a | b | c | d;
- } catch (NumberFormatException ex) {
- throw new UnknownHostException(addrString);
- }
- }
-
- private int getMaxDhcpRetries() {
- return Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.WIFI_MAX_DHCP_RETRY_COUNT,
- DEFAULT_MAX_DHCP_RETRIES);
- }
-
- private class SettingsObserver extends ContentObserver {
- public SettingsObserver(Handler handler) {
- super(handler);
- ContentResolver cr = mContext.getContentResolver();
- cr.registerContentObserver(Settings.System.getUriFor(
- Settings.System.WIFI_USE_STATIC_IP), false, this);
- cr.registerContentObserver(Settings.System.getUriFor(
- Settings.System.WIFI_STATIC_IP), false, this);
- cr.registerContentObserver(Settings.System.getUriFor(
- Settings.System.WIFI_STATIC_GATEWAY), false, this);
- cr.registerContentObserver(Settings.System.getUriFor(
- Settings.System.WIFI_STATIC_NETMASK), false, this);
- cr.registerContentObserver(Settings.System.getUriFor(
- Settings.System.WIFI_STATIC_DNS1), false, this);
- cr.registerContentObserver(Settings.System.getUriFor(
- Settings.System.WIFI_STATIC_DNS2), false, this);
- }
-
- public void onChange(boolean selfChange) {
- super.onChange(selfChange);
-
- boolean wasStaticIp = mUseStaticIp;
- int oIp, oGw, oMsk, oDns1, oDns2;
- oIp = oGw = oMsk = oDns1 = oDns2 = 0;
- if (wasStaticIp) {
- oIp = mDhcpInfo.ipAddress;
- oGw = mDhcpInfo.gateway;
- oMsk = mDhcpInfo.netmask;
- oDns1 = mDhcpInfo.dns1;
- oDns2 = mDhcpInfo.dns2;
- }
- checkUseStaticIp();
-
- if (mWifiInfo.getSupplicantState() == SupplicantState.UNINITIALIZED) {
- return;
- }
-
- boolean changed =
- (wasStaticIp != mUseStaticIp) ||
- (wasStaticIp && (
- oIp != mDhcpInfo.ipAddress ||
- oGw != mDhcpInfo.gateway ||
- oMsk != mDhcpInfo.netmask ||
- oDns1 != mDhcpInfo.dns1 ||
- oDns2 != mDhcpInfo.dns2));
-
- if (changed) {
- resetConnections(true);
- configureInterface();
- if (mUseStaticIp) {
- Message msg = mTarget.obtainMessage(EVENT_CONFIGURATION_CHANGED, mNetworkInfo);
- msg.sendToTarget();
- }
- }
- }
- }
-
- private class NotificationEnabledSettingObserver extends ContentObserver {
-
- public NotificationEnabledSettingObserver(Handler handler) {
- super(handler);
- }
-
- public void register() {
- ContentResolver cr = mContext.getContentResolver();
- cr.registerContentObserver(Settings.Secure.getUriFor(
- Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON), true, this);
- mNotificationEnabled = getValue();
- }
-
- @Override
- public void onChange(boolean selfChange) {
- super.onChange(selfChange);
-
- mNotificationEnabled = getValue();
- if (!mNotificationEnabled) {
- // Remove any notification that may be showing
- setNotificationVisible(false, 0, true, 0);
- }
-
- resetNotificationTimer();
- }
-
- private boolean getValue() {
- return Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 1) == 1;
- }
- }
}