diff options
Diffstat (limited to 'wifi')
-rw-r--r-- | wifi/java/android/net/wifi/IWifiManager.aidl | 24 | ||||
-rw-r--r-- | wifi/java/android/net/wifi/WifiConfigStore.java | 682 | ||||
-rw-r--r-- | wifi/java/android/net/wifi/WifiConfiguration.java | 36 | ||||
-rw-r--r-- | wifi/java/android/net/wifi/WifiManager.java | 231 | ||||
-rw-r--r-- | wifi/java/android/net/wifi/WifiMonitor.java | 37 | ||||
-rw-r--r-- | wifi/java/android/net/wifi/WifiNative.java | 8 | ||||
-rw-r--r-- | wifi/java/android/net/wifi/WifiStateMachine.java | 3169 | ||||
-rw-r--r-- | wifi/java/android/net/wifi/WifiStateTracker.java | 2658 |
8 files changed, 4262 insertions, 2583 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..78d5b7e --- /dev/null +++ b/wifi/java/android/net/wifi/WifiConfigStore.java @@ -0,0 +1,682 @@ +/* + * 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.wifi.WifiConfiguration.Status; +import android.text.TextUtils; +import android.util.Log; + +import java.util.ArrayList; +import java.util.BitSet; +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 + * - Maintain a list of configured networks for quick access + * TODO: + * - handle static IP per configuration + * - handle proxy per configuration + */ +class WifiConfigStore { + + private static Context sContext; + private static final String TAG = "WifiConfigStore"; + + private static List<WifiConfiguration> sConfiguredNetworks = new ArrayList<WifiConfiguration>(); + /* Tracks the highest priority of configured networks */ + private static int sLastPriority = -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) { + 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() { + for (WifiConfiguration config : sConfiguredNetworks) { + if(config != null && config.status == Status.DISABLED) { + WifiNative.enableNetworkCommand(config.networkId, false); + } + } + + WifiNative.saveConfigCommand(); + updateConfigAndSendChangeBroadcast(); + } + + /** + * 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); + selectNetwork(netId); + } + } + + /** + * 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) { + for (WifiConfiguration conf : sConfiguredNetworks) { + if (conf.networkId != -1) { + conf.priority = 0; + addOrUpdateNetworkNative(conf); + } + } + 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 */ + WifiNative.enableNetworkCommand(netId, true); + + /* update the configured networks list but not send a + * broadcast to avoid a fetch from settings + * during this temporary disabling of networks + */ + updateConfiguredNetworks(); + } + + /** + * Add/update the specified configuration and save config + * + * @param config WifiConfiguration to be saved + */ + static void saveNetwork(WifiConfiguration config) { + int netId = addOrUpdateNetworkNative(config); + /* enable a new network */ + if (config.networkId < 0) { + WifiNative.enableNetworkCommand(netId, false); + } + WifiNative.saveConfigCommand(); + updateConfigAndSendChangeBroadcast(); + } + + /** + * Forget the specified network and save config + * + * @param netId network to forget + */ + static void forgetNetwork(int netId) { + WifiNative.removeNetworkCommand(netId); + WifiNative.saveConfigCommand(); + updateConfigAndSendChangeBroadcast(); + } + + /** + * 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); + updateConfigAndSendChangeBroadcast(); + 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); + updateConfigAndSendChangeBroadcast(); + 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 = WifiNative.enableNetworkCommand(netId, disableOthers); + updateConfigAndSendChangeBroadcast(); + 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); + updateConfigAndSendChangeBroadcast(); + return ret; + } + + /** + * Save the configured networks in supplicant to disk + */ + static boolean saveConfig() { + return WifiNative.saveConfigCommand(); + } + + private static void updateConfigAndSendChangeBroadcast() { + updateConfiguredNetworks(); + 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(); + + 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.add(config); + } + } + } + + 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 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; + } + } + } + return netId; + } + + if (newNetwork) { + WifiNative.removeNetworkCommand(netId); + Log.d(TAG, + "Failed to set a network variable, removed network: " + + netId); + } + + return -1; + } + + /** + * 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; + } + + 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..ca4f29f 100644 --- a/wifi/java/android/net/wifi/WifiConfiguration.java +++ b/wifi/java/android/net/wifi/WifiConfiguration.java @@ -216,7 +216,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 "MyNetwork"}, 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 +239,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 "abcdef"} 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 @@ -432,6 +432,38 @@ 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()); + } + return config; + } + /** Implement the Parcelable interface {@hide} */ public void writeToParcel(Parcel dest, int flags) { dest.writeInt(networkId); diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index dd162f2..26ed878 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,26 @@ 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 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 +330,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 +525,8 @@ public class WifiManager { */ public boolean disconnect() { try { - return mService.disconnect(); + mService.disconnect(); + return true; } catch (RemoteException e) { return false; } @@ -527,7 +540,8 @@ public class WifiManager { */ public boolean reconnect() { try { - return mService.reconnect(); + mService.reconnect(); + return true; } catch (RemoteException e) { return false; } @@ -541,7 +555,8 @@ public class WifiManager { */ public boolean reassociate() { try { - return mService.reassociate(); + mService.reassociate(); + return true; } catch (RemoteException e) { return false; } @@ -570,7 +585,8 @@ public class WifiManager { */ public boolean startScan() { try { - return mService.startScan(false); + mService.startScan(false); + return true; } catch (RemoteException e) { return false; } @@ -588,7 +604,8 @@ public class WifiManager { */ public boolean startScanActive() { try { - return mService.startScan(true); + mService.startScan(true); + return true; } catch (RemoteException e) { return false; } @@ -754,8 +771,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 +867,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 +1209,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..e1cbf5b --- /dev/null +++ b/wifi/java/android/net/wifi/WifiStateMachine.java @@ -0,0 +1,3169 @@ +/* + * 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.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 boolean mUseStaticIp = false; + private int mReconnectCount = 0; + private boolean mIsScanMode = false; + private boolean mConfigChanged = 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; + + /** + * Observes the static IP address settings. + */ + private SettingsObserver mSettingsObserver; + 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()); + + 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(); + } + }; + + mSettingsObserver = new SettingsObserver(new Handler()); + + 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() { + return 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); + } + + @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("mUseStaticIp ").append(mUseStaticIp).append(LS); + sb.append("mReconnectCount ").append(mReconnectCount).append(LS); + sb.append("mIsScanMode ").append(mIsScanMode).append(LS); + sb.append("mConfigChanged ").append(mConfigChanged).append(LS).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(mLastEnableUid.get()); + } else if (wifiState == WIFI_STATE_DISABLED) { + mBatteryStats.noteWifiOff(mLastEnableUid.get()); + } + } 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(mLastApEnableUid.get()); + } else if (wifiApState == WIFI_AP_STATE_DISABLED) { + mBatteryStats.noteWifiOff(mLastApEnableUid.get()); + } + } 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 + 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 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); + } + + @Override + 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) { + sendMessage(CMD_RECONFIGURE_IP); + mConfigChanged = true; + } + } + } + + /** + * 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); + } + + 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(); + + checkUseStaticIp(); + 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()); + + try { + mBatteryStats.noteWifiRunning(); + } catch (RemoteException ignore) {} + + /* 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"); + try { + mBatteryStats.noteWifiStopped(); + } catch (RemoteException ignore) { } + 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 modifiedBluetoothCoexistenceMode; + int powerMode; + Thread mDhcpThread; + + @Override + public void enter() { + if (DBG) Log.d(TAG, getName() + "\n"); + EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName()); + + if (!mUseStaticIp) { + + mDhcpThread = null; + modifiedBluetoothCoexistenceMode = false; + 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 + WifiNative.setBluetoothCoexistenceModeCommand( + WifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED); + } + + powerMode = WifiNative.getPowerModeCommand(); + if (powerMode < 0) { + // Handle the case where supplicant driver does not support + // getPowerModeCommand. + powerMode = DRIVER_POWER_MODE_AUTO; + } + if (powerMode != DRIVER_POWER_MODE_ACTIVE) { + WifiNative.setPowerModeCommand(DRIVER_POWER_MODE_ACTIVE); + } + + Log.d(TAG, "DHCP request started"); + mDhcpThread = new Thread(new Runnable() { + public void run() { + if (NetworkUtils.runDhcp(mInterfaceName, mDhcpInfo)) { + Log.d(TAG, "DHCP request succeeded"); + sendMessage(CMD_IP_CONFIG_SUCCESS); + } else { + Log.d(TAG, "DHCP request failed: " + + NetworkUtils.getDhcpError()); + sendMessage(CMD_IP_CONFIG_FAILURE); + } + } + }); + mDhcpThread.start(); + } else { + if (NetworkUtils.configureInterface(mInterfaceName, mDhcpInfo)) { + Log.v(TAG, "Static IP configuration succeeded"); + 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 + mWifiInfo.setIpAddress(mDhcpInfo.ipAddress); + Log.d(TAG, "IP configuration: " + mDhcpInfo); + configureLinkProperties(); + setDetailedState(DetailedState.CONNECTED); + sendNetworkStateChangeBroadcast(mLastBssid); + //TODO: we could also detect an IP config change + // from a DHCP renewal and send out a config change + // broadcast + if (mConfigChanged) { + sendConfigChangeBroadcast(); + mConfigChanged = false; + } + 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 (powerMode != DRIVER_POWER_MODE_ACTIVE) { + WifiNative.setPowerModeCommand(powerMode); + } + + if (modifiedBluetoothCoexistenceMode) { + // 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 59f2312..9cc44b5 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,450 +16,108 @@ 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.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.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 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(); - private BluetoothA2dp mBluetoothA2dp; - - private String mInterfaceName; - private static String LS = System.getProperty("line.separator"); - - private static String[] sDnsPropNames; - private Runnable mReleaseWakeLockCallback; - - /** - * 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); + mLinkProperties.clear(); + 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")); - - } - /** - * 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 void setTeardownRequested(boolean isRequested) { + mTeardownRequested.set(isRequested); } - 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; } /** @@ -471,2172 +129,118 @@ 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; - } - - private void noteRunState() { - try { - if (mRunState == RUN_STATE_RUNNING) { - mBatteryStats.noteWifiRunning(); - } else if (mRunState == RUN_STATE_STOPPED) { - mBatteryStats.noteWifiStopped(); - } - } 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; - noteRunState(); - 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; - noteRunState(); - 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; - } - noteRunState(); - 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(); - } - - /** - * Get power mode - * @return power mode - */ - public synchronized int getPowerMode() { - if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) { - return -1; - } - return WifiNative.getPowerModeCommand(); - } - - /** - * 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 - * - */ - 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); - } - - /** - * 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. - */ - 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 isAvailable() { + return mNetworkInfo.isAvailable(); } /** - * 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. + * 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 boolean setNumAllowedChannels(int numChannels) { - if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) { - return false; - } - mNumAllowedChannels = numChannels; - return WifiNative.setNumAllowedChannelsCommand(numChannels); + public int startUsingNetworkFeature(String feature, int callingPid, int callingUid) { + return -1; } /** - * Get number of allowed channels - * - * @return channel count, -1 on failure + * 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 */ - public synchronized int getNumAllowedChannels() { - if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) { - return -1; - } - return WifiNative.getNumAllowedChannelsCommand(); + public int stopUsingNetworkFeature(String feature, int callingPid, int callingUid) { + return -1; } /** - * 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 + * Check if private DNS route is set for the network */ - public synchronized boolean setBluetoothCoexistenceMode(int mode) { - if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) { - return false; - } - return WifiNative.setBluetoothCoexistenceModeCommand(mode); + public boolean isPrivateDnsRouteSet() { + return mPrivateDnsRouteSet.get(); } /** - * 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 + * Set a flag indicating private DNS route is set */ - public synchronized void setBluetoothScanMode(boolean isBluetoothPlaying) { - if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) { - return; - } - WifiNative.setBluetoothCoexistenceScanModeCommand(isBluetoothPlaying); + public void privateDnsRouteSet(boolean enabled) { + mPrivateDnsRouteSet.set(enabled); } /** - * Save configuration on supplicant - * - * @return {@code true} if the operation succeeds, {@code false} otherwise + * Fetch NetworkInfo for the network */ - public synchronized boolean saveConfig() { - if (mWifiState.get() != WIFI_STATE_ENABLED) { - return false; - } - return WifiNative.saveConfigCommand(); + public NetworkInfo getNetworkInfo() { + return mNetworkInfo; } /** - * Reload the configuration from file - * - * @return {@code true} if the operation succeeds, {@code false} otherwise + * Fetch LinkProperties for the network */ - 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 LinkProperties getLinkProperties() { + return new LinkProperties(mLinkProperties); } /** - * {@inheritDoc} - * There are currently no Wi-Fi-specific features supported. - * @param feature the name of the feature - * @return {@code -1} indicating failure, always + * Fetch default gateway address for the network */ - public int startUsingNetworkFeature(String feature, int callingPid, int callingUid) { - return -1; + public int getDefaultGatewayAddr() { + return mDefaultGatewayAddr.get(); } /** - * {@inheritDoc} - * There are currently no Wi-Fi-specific features supported. - * @param feature the name of the feature - * @return {@code -1} indicating failure, always + * Check if default route is set */ - public int stopUsingNetworkFeature(String feature, int callingPid, int callingUid) { - return -1; - } - - @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++; - } - } - - 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; - } - } - } - - // No open networks in range, remove the notification - setNotificationVisible(false, 0, false, 0); + public boolean isDefaultRouteSet() { + return mDefaultRouteSet.get(); } /** - * 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. + * Set a flag indicating default route is set for the network */ - 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; + public void defaultRouteSet(boolean enabled) { + mDefaultRouteSet.set(enabled); } /** - * 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. + * Return the system properties name associated with the tcp buffer sizes + * for this network. */ - 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(); - } - } - } + public String getTcpBufferSizesPropName() { + return "net.tcp.buffersize.wifi"; } - 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(); - } - + private class WifiStateReceiver extends BroadcastReceiver { @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); + 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 = (LinkProperties) intent.getParcelableExtra( + WifiManager.EXTRA_LINK_PROPERTIES); + 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(); } - - resetNotificationTimer(); - } - - private boolean getValue() { - return Settings.Secure.getInt(mContext.getContentResolver(), - Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 1) == 1; } } + } |