blob: 97a610a82c77a89e8bd289b722523dd02a967d2f [file] [log] [blame]
/*
* 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 com.android.server.wifi;
import android.content.Context;
import android.content.Intent;
import android.net.IpConfiguration;
import android.net.IpConfiguration.IpAssignment;
import android.net.IpConfiguration.ProxySettings;
import android.net.LinkAddress;
import android.net.NetworkInfo.DetailedState;
import android.net.ProxyInfo;
import android.net.RouteInfo;
import android.net.StaticIpConfiguration;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiConfiguration.KeyMgmt;
import android.net.wifi.WifiConfiguration.Status;
import static android.net.wifi.WifiConfiguration.INVALID_NETWORK_ID;
import android.net.wifi.WifiEnterpriseConfig;
import android.net.wifi.WifiManager;
import android.net.wifi.WifiSsid;
import android.net.wifi.WpsInfo;
import android.net.wifi.WpsResult;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiInfo;
import android.os.Environment;
import android.os.FileObserver;
import android.os.Process;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
import android.security.Credentials;
import android.security.KeyChain;
import android.security.KeyStore;
import android.text.TextUtils;
import android.util.LocalLog;
import android.util.Log;
import android.util.SparseArray;
import com.android.server.net.DelayedDiskWrite;
import com.android.server.net.IpConfigStore;
import java.io.BufferedReader;
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.math.BigInteger;
import java.net.InetAddress;
import java.nio.charset.Charset;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.text.SimpleDateFormat;
import java.text.DateFormat;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.*;
/**
* This class provides the API to manage configured
* wifi networks. The API is not thread safe is being
* used only from WifiStateMachine.
*
* It deals with the following
* - Add/update/remove a WifiConfiguration
* The configuration contains two types of information.
* = IP and proxy configuration that is handled by WifiConfigStore and
* is saved to disk on any change.
*
* The format of configuration file is as follows:
* <version>
* <netA_key1><netA_value1><netA_key2><netA_value2>...<EOS>
* <netB_key1><netB_value1><netB_key2><netB_value2>...<EOS>
* ..
*
* (key, value) pairs for a given network are grouped together and can
* be in any order. A EOS at the end of a set of (key, value) pairs
* indicates that the next set of (key, value) pairs are for a new
* network. A network is identified by a unique ID_KEY. If there is no
* ID_KEY in the (key, value) pairs, the data is discarded.
*
* An invalid version on read would result in discarding the contents of
* the file. On the next write, the latest version is written to file.
*
* Any failures during read or write to the configuration file are ignored
* without reporting to the user since the likelihood of these errors are
* low and the impact on connectivity is low.
*
* = SSID & security details that is pushed to the supplicant.
* supplicant saves these details to the disk on calling
* saveConfigCommand().
*
* We have two kinds of APIs exposed:
* > public API calls that provide fine grained control
* - enableNetwork, disableNetwork, addOrUpdateNetwork(),
* removeNetwork(). For these calls, the config is not persisted
* to the disk. (TODO: deprecate these calls in WifiManager)
* > The new API calls - selectNetwork(), saveNetwork() & forgetNetwork().
* These calls persist the supplicant config to disk.
*
* - Maintain a list of configured networks for quick access
*
*/
public class WifiConfigStore extends IpConfigStore {
private Context mContext;
private static final String TAG = "WifiConfigStore";
private static final boolean DBG = true;
private static boolean VDBG = false;
private static final String SUPPLICANT_CONFIG_FILE = "/data/misc/wifi/wpa_supplicant.conf";
/* configured networks with network id as the key */
private HashMap<Integer, WifiConfiguration> mConfiguredNetworks =
new HashMap<Integer, WifiConfiguration>();
/* A network id is a unique identifier for a network configured in the
* supplicant. Network ids are generated when the supplicant reads
* the configuration file at start and can thus change for networks.
* We store the IP configuration for networks along with a unique id
* that is generated from SSID and security type of the network. A mapping
* from the generated unique id to network id of the network is needed to
* map supplicant config to IP configuration. */
private HashMap<Integer, Integer> mNetworkIds =
new HashMap<Integer, Integer>();
/* Tracks the highest priority of configured networks */
private int mLastPriority = -1;
private static final String ipConfigFile = Environment.getDataDirectory() +
"/misc/wifi/ipconfig.txt";
private static final String networkHistoryConfigFile = Environment.getDataDirectory() +
"/misc/wifi/networkHistory.txt";
private static final String autoJoinConfigFile = Environment.getDataDirectory() +
"/misc/wifi/autojoinconfig.txt";
/* Network History Keys */
private static final String SSID_KEY = "SSID: ";
private static final String CONFIG_KEY = "CONFIG: ";
private static final String CHOICE_KEY = "CHOICE: ";
private static final String LINK_KEY = "LINK: ";
private static final String BSSID_KEY = "BSSID: ";
private static final String BSSID_KEY_END = "/BSSID: ";
private static final String RSSI_KEY = "RSSI: ";
private static final String FREQ_KEY = "FREQ: ";
private static final String DATE_KEY = "DATE: ";
private static final String MILLI_KEY = "MILLI: ";
private static final String BLACKLIST_MILLI_KEY = "BLACKLIST_MILLI: ";
private static final String NETWORK_ID_KEY = "ID: ";
private static final String PRIORITY_KEY = "PRIORITY: ";
private static final String DEFAULT_GW_KEY = "DEFAULT_GW: ";
private static final String AUTH_KEY = "AUTH: ";
private static final String SEPARATOR_KEY = "\n";
private static final String STATUS_KEY = "AUTO_JOIN_STATUS: ";
private static final String BSSID_STATUS_KEY = "BSSID_STATUS: ";
private static final String SELF_ADDED_KEY = "SELF_ADDED: ";
private static final String FAILURE_KEY = "FAILURE: ";
private static final String DID_SELF_ADD_KEY = "DID_SELF_ADD: ";
private static final String PEER_CONFIGURATION_KEY = "PEER_CONFIGURATION: ";
private static final String CREATOR_UID_KEY = "CREATOR_UID_KEY: ";
private static final String CONNECT_UID_KEY = "CONNECT_UID_KEY: ";
private static final String UPDATE_UID_KEY = "UPDATE_UID: ";
private static final String SUPPLICANT_STATUS_KEY = "SUP_STATUS: ";
private static final String SUPPLICANT_DISABLE_REASON_KEY = "SUP_DIS_REASON: ";
private static final String FQDN_KEY = "FQDN: ";
private static final String NUM_CONNECTION_FAILURES_KEY = "CONNECT_FAILURES: ";
private static final String SCORER_OVERRIDE_KEY = "SCORER_OVERRIDE: ";
private static final String SCORER_OVERRIDE_AND_SWITCH_KEY = "SCORER_OVERRIDE_AND_SWITCH: ";
private static final String NUM_ASSOCIATION_KEY = "NUM_ASSOCIATION: ";
private static final String THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_5G_KEY
= "THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_5G: ";
private static final String THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_24G_KEY
= "THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_24G: ";
private static final String THRESHOLD_UNBLACKLIST_HARD_5G_KEY
= "THRESHOLD_UNBLACKLIST_HARD_5G: ";
private static final String THRESHOLD_UNBLACKLIST_SOFT_5G_KEY
= "THRESHOLD_UNBLACKLIST_SOFT_5G: ";
private static final String THRESHOLD_UNBLACKLIST_HARD_24G_KEY
= "THRESHOLD_UNBLACKLIST_HARD_24G: ";
private static final String THRESHOLD_UNBLACKLIST_SOFT_24G_KEY
= "THRESHOLD_UNBLACKLIST_SOFT_24G: ";
private static final String THRESHOLD_GOOD_RSSI_5_KEY
= "THRESHOLD_GOOD_RSSI_5: ";
private static final String THRESHOLD_LOW_RSSI_5_KEY
= "THRESHOLD_LOW_RSSI_5: ";
private static final String THRESHOLD_BAD_RSSI_5_KEY
= "THRESHOLD_BAD_RSSI_5: ";
private static final String THRESHOLD_GOOD_RSSI_24_KEY
= "THRESHOLD_GOOD_RSSI_24: ";
private static final String THRESHOLD_LOW_RSSI_24_KEY
= "THRESHOLD_LOW_RSSI_24: ";
private static final String THRESHOLD_BAD_RSSI_24_KEY
= "THRESHOLD_BAD_RSSI_24: ";
private static final String THRESHOLD_MAX_TX_PACKETS_FOR_NETWORK_SWITCHING_KEY
= "THRESHOLD_MAX_TX_PACKETS_FOR_NETWORK_SWITCHING: ";
private static final String THRESHOLD_MAX_RX_PACKETS_FOR_NETWORK_SWITCHING_KEY
= "THRESHOLD_MAX_RX_PACKETS_FOR_NETWORK_SWITCHING: ";
private static final String A_BAND_PREFERENCE_RSSI_THRESHOLD_LOW_KEY =
"A_BAND_PREFERENCE_RSSI_THRESHOLD_LOW: ";
private static final String A_BAND_PREFERENCE_RSSI_THRESHOLD_KEY =
"A_BAND_PREFERENCE_RSSI_THRESHOLD: ";
private static final String G_BAND_PREFERENCE_RSSI_THRESHOLD_KEY =
"G_BAND_PREFERENCE_RSSI_THRESHOLD: ";
private static final String ENABLE_AUTOJOIN_WHILE_ASSOCIATED_KEY
= "ENABLE_AUTOJOIN_WHILE_ASSOCIATED: ";
public boolean enableAutoJoinWhileAssociated = true;
public int maxTxPacketForNetworkSwitching = 40;
public int maxRxPacketForNetworkSwitching = 80;
public int thresholdInitialAutoJoinAttemptMin5RSSI
= WifiConfiguration.INITIAL_AUTO_JOIN_ATTEMPT_MIN_5;
public int thresholdInitialAutoJoinAttemptMin24RSSI
= WifiConfiguration.INITIAL_AUTO_JOIN_ATTEMPT_MIN_24;
public int thresholdBadRssi5 = WifiConfiguration.BAD_RSSI_5;
public int thresholdLowRssi5 = WifiConfiguration.LOW_RSSI_5;
public int thresholdGoodRssi5 = WifiConfiguration.GOOD_RSSI_5;
public int thresholdBadRssi24 = WifiConfiguration.BAD_RSSI_24;
public int thresholdLowRssi24 = WifiConfiguration.LOW_RSSI_24;
public int thresholdGoodRssi24 = WifiConfiguration.GOOD_RSSI_24;
public int thresholdBandPreferenceRssi24
= WifiConfiguration.G_BAND_PREFERENCE_RSSI_THRESHOLD;
public int thresholdBandPreferenceRssi5
= WifiConfiguration.A_BAND_PREFERENCE_RSSI_THRESHOLD;
public int thresholdBandPreferenceLowRssi5
= WifiConfiguration.A_BAND_PREFERENCE_RSSI_THRESHOLD_LOW;
public int thresholdUnblacklistThreshold5Hard
= WifiConfiguration.UNBLACKLIST_THRESHOLD_5_HARD;
public int thresholdUnblacklistThreshold5Soft
= WifiConfiguration.UNBLACKLIST_THRESHOLD_5_SOFT;
public int thresholdUnblacklistThreshold24Hard
= WifiConfiguration.UNBLACKLIST_THRESHOLD_24_HARD;
public int thresholdUnblacklistThreshold24Soft
= WifiConfiguration.UNBLACKLIST_THRESHOLD_24_SOFT;
/**
* Regex pattern for extracting a connect choice.
* Matches a strings like the following:
* <configKey>=([0:9]+)
*/
private static Pattern mConnectChoice =
Pattern.compile("(.*)=([0-9]+)");
/* Enterprise configuration keys */
/**
* In old configurations, the "private_key" field was used. However, newer
* configurations use the key_id field with the engine_id set to "keystore".
* If this field is found in the configuration, the migration code is
* triggered.
*/
public static final String OLD_PRIVATE_KEY_NAME = "private_key";
/**
* This represents an empty value of an enterprise field.
* NULL is used at wpa_supplicant to indicate an empty value
*/
static final String EMPTY_VALUE = "NULL";
// Internal use only
private static final String[] ENTERPRISE_CONFIG_SUPPLICANT_KEYS = new String[] {
WifiEnterpriseConfig.EAP_KEY, WifiEnterpriseConfig.PHASE2_KEY,
WifiEnterpriseConfig.IDENTITY_KEY, WifiEnterpriseConfig.ANON_IDENTITY_KEY,
WifiEnterpriseConfig.PASSWORD_KEY, WifiEnterpriseConfig.CLIENT_CERT_KEY,
WifiEnterpriseConfig.CA_CERT_KEY, WifiEnterpriseConfig.SUBJECT_MATCH_KEY,
WifiEnterpriseConfig.ENGINE_KEY, WifiEnterpriseConfig.ENGINE_ID_KEY,
WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY };
/**
* 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 final LocalLog mLocalLog;
private final WpaConfigFileObserver mFileObserver;
private WifiNative mWifiNative;
private final KeyStore mKeyStore = KeyStore.getInstance();
/**
* The lastSelectedConfiguration is used to remember which network
* was selected last by the user.
* The connection to this network may not be successful, as well
* the selection (i.e. network priority) might not be persisted.
* WiFi state machine is the only object that sets this variable.
*/
private String lastSelectedConfiguration = null;
WifiConfigStore(Context c, WifiNative wn) {
mContext = c;
mWifiNative = wn;
if (VDBG) {
mLocalLog = mWifiNative.getLocalLog();
mFileObserver = new WpaConfigFileObserver();
mFileObserver.startWatching();
} else {
mLocalLog = null;
mFileObserver = null;
}
}
void enableVerboseLogging(int verbose) {
if (verbose > 0) {
VDBG = true;
} else {
VDBG = false;
}
}
class WpaConfigFileObserver extends FileObserver {
public WpaConfigFileObserver() {
super(SUPPLICANT_CONFIG_FILE, CLOSE_WRITE);
}
@Override
public void onEvent(int event, String path) {
if (event == CLOSE_WRITE) {
File file = new File(SUPPLICANT_CONFIG_FILE);
if (VDBG) localLog("wpa_supplicant.conf changed; new size = " + file.length());
}
}
}
/**
* Fetch the list of configured networks
* and enable all stored networks in supplicant.
*/
void loadAndEnableAllNetworks() {
if (DBG) log("Loading config and enabling all networks ");
loadConfiguredNetworks();
enableAllNetworks();
}
int getConfiguredNetworksSize() {
return mConfiguredNetworks.size();
}
private List<WifiConfiguration> getConfiguredNetworks(Map<String, String> pskMap) {
List<WifiConfiguration> networks = new ArrayList<>();
for(WifiConfiguration config : mConfiguredNetworks.values()) {
WifiConfiguration newConfig = new WifiConfiguration(config);
if (config.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DELETED) {
//do not enumerate and return this configuration to any one,
//for instance WiFi Picker.
//instead treat it as unknown. the configuration can still be retrieved
//directly by the key or networkId
continue;
}
if (pskMap != null && config.allowedKeyManagement != null
&& config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)
&& pskMap.containsKey(config.SSID)) {
newConfig.preSharedKey = pskMap.get(config.SSID);
}
networks.add(newConfig);
}
return networks;
}
/**
* Fetch the list of currently configured networks
* @return List of networks
*/
List<WifiConfiguration> getConfiguredNetworks() {
return getConfiguredNetworks(null);
}
/**
* Fetch the list of currently configured networks, filled with real preSharedKeys
* @return List of networks
*/
List<WifiConfiguration> getPrivilegedConfiguredNetworks() {
Map<String, String> pskMap = getCredentialsBySsidMap();
return getConfiguredNetworks(pskMap);
}
/**
* Fetch the preSharedKeys for all networks.
* @return a map from Ssid to preSharedKey.
*/
private Map<String, String> getCredentialsBySsidMap() {
return readNetworkVariablesFromSupplicantFile("psk");
}
/**
* Fetch the list of currently configured networks that were recently seen
*
* @return List of networks
*/
List<WifiConfiguration> getRecentConfiguredNetworks(int milli, boolean copy) {
List<WifiConfiguration> networks = null;
for (WifiConfiguration config : mConfiguredNetworks.values()) {
if (config.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DELETED) {
// Do not enumerate and return this configuration to any one,
// instead treat it as unknown. the configuration can still be retrieved
// directly by the key or networkId
continue;
}
// Calculate the RSSI for scan results that are more recent than milli
config.setVisibility(milli);
if (config.visibility == null) {
continue;
}
if (config.visibility.rssi5 == WifiConfiguration.INVALID_RSSI &&
config.visibility.rssi24 == WifiConfiguration.INVALID_RSSI) {
continue;
}
if (networks == null)
networks = new ArrayList<WifiConfiguration>();
if (copy) {
networks.add(new WifiConfiguration(config));
} else {
networks.add(config);
}
}
return networks;
}
/**
* Update the configuration and BSSID with latest RSSI value.
*/
void updateConfiguration(WifiInfo info) {
WifiConfiguration config = getWifiConfiguration(info.getNetworkId());
if (config != null && config.scanResultCache != null) {
ScanResult result = config.scanResultCache.get(info.getBSSID());
if (result != null) {
long previousSeen = result.seen;
int previousRssi = result.level;
// Update the scan result
result.seen = System.currentTimeMillis();
result.level = info.getRssi();
// Average the RSSI value
result.averageRssi(previousRssi, previousSeen,
WifiAutoJoinController.mScanResultMaximumAge);
if (VDBG) {
loge("updateConfiguration freq=" + result.frequency
+ " BSSID=" + result.BSSID
+ " RSSI=" + result.level
+ " " + config.configKey());
}
}
}
}
/**
* get the Wificonfiguration for this netId
*
* @return Wificonfiguration
*/
WifiConfiguration getWifiConfiguration(int netId) {
if (mConfiguredNetworks == null)
return null;
return mConfiguredNetworks.get(netId);
}
/**
* Get the Wificonfiguration for this key
* @return Wificonfiguration
*/
WifiConfiguration getWifiConfiguration(String key) {
if (key == null)
return null;
int hash = key.hashCode();
if (mNetworkIds == null)
return null;
Integer n = mNetworkIds.get(hash);
if (n == null)
return null;
int netId = n.intValue();
return getWifiConfiguration(netId);
}
/**
* Enable all networks and save config. This will be a no-op if the list
* of configured networks indicates all networks as being enabled
*/
void enableAllNetworks() {
boolean networkEnabledStateChanged = false;
for(WifiConfiguration config : mConfiguredNetworks.values()) {
if(config != null && config.status == Status.DISABLED
&& (config.autoJoinStatus <= WifiConfiguration.AUTO_JOIN_TEMPORARY_DISABLED)) {
if(mWifiNative.enableNetwork(config.networkId, false)) {
networkEnabledStateChanged = true;
config.status = Status.ENABLED;
} else {
loge("Enable network failed on " + config.networkId);
}
}
}
if (networkEnabledStateChanged) {
mWifiNative.saveConfig();
sendConfiguredNetworksChangedBroadcast();
}
}
/**
* 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
* @return false if the network id is invalid
*/
boolean selectNetwork(int netId) {
if (VDBG) localLog("selectNetwork", netId);
if (netId == INVALID_NETWORK_ID) return false;
// Reset the priority of each network at start or if it goes too high.
if (mLastPriority == -1 || mLastPriority > 1000000) {
for(WifiConfiguration config : mConfiguredNetworks.values()) {
if (config.networkId != INVALID_NETWORK_ID) {
config.priority = 0;
addOrUpdateNetworkNative(config);
}
}
mLastPriority = 0;
}
// Set to the highest priority and save the configuration.
WifiConfiguration config = new WifiConfiguration();
config.networkId = netId;
config.priority = ++mLastPriority;
addOrUpdateNetworkNative(config);
mWifiNative.saveConfig();
/* Enable the given network while disabling all other networks */
enableNetworkWithoutBroadcast(netId, true);
/* Avoid saving the config & sending a broadcast to prevent settings
* from displaying a disabled list of networks */
return true;
}
/**
* Add/update the specified configuration and save config
*
* @param config WifiConfiguration to be saved
* @return network update result
*/
NetworkUpdateResult saveNetwork(WifiConfiguration config) {
WifiConfiguration conf;
// A new network cannot have null SSID
if (config == null || (config.networkId == INVALID_NETWORK_ID &&
config.SSID == null)) {
return new NetworkUpdateResult(INVALID_NETWORK_ID);
}
if (VDBG) localLog("WifiConfigStore: saveNetwork netId", config.networkId);
if (VDBG) {
loge("WifiConfigStore saveNetwork, size=" + mConfiguredNetworks.size()
+ " SSID=" + config.SSID
+ " Uid=" + Integer.toString(config.creatorUid)
+ "/" + Integer.toString(config.lastUpdateUid));
}
boolean newNetwork = (config.networkId == INVALID_NETWORK_ID);
NetworkUpdateResult result = addOrUpdateNetworkNative(config);
int netId = result.getNetworkId();
if (VDBG) localLog("WifiConfigStore: saveNetwork got it back netId=", netId);
/* enable a new network */
if (newNetwork && netId != INVALID_NETWORK_ID) {
if (VDBG) localLog("WifiConfigStore: will enable netId=", netId);
mWifiNative.enableNetwork(netId, false);
conf = mConfiguredNetworks.get(netId);
if (conf != null)
conf.status = Status.ENABLED;
}
conf = mConfiguredNetworks.get(netId);
if (conf != null) {
if (conf.autoJoinStatus != WifiConfiguration.AUTO_JOIN_ENABLED) {
if (VDBG) localLog("WifiConfigStore: re-enabling: " + conf.SSID);
// reenable autojoin, since new information has been provided
conf.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED);
enableNetworkWithoutBroadcast(conf.networkId, false);
}
if (VDBG) loge("WifiConfigStore: saveNetwork got config back netId="
+ Integer.toString(netId)
+ " uid=" + Integer.toString(config.creatorUid));
}
mWifiNative.saveConfig();
sendConfiguredNetworksChangedBroadcast(config, result.isNewNetwork() ?
WifiManager.CHANGE_REASON_ADDED : WifiManager.CHANGE_REASON_CONFIG_CHANGE);
return result;
}
void updateStatus(int netId, DetailedState state) {
if (netId != INVALID_NETWORK_ID) {
WifiConfiguration config = mConfiguredNetworks.get(netId);
if (config == null) return;
switch (state) {
case CONNECTED:
config.status = Status.CURRENT;
//we successfully connected, hence remove the blacklist
config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED);
break;
case DISCONNECTED:
//If network is already disabled, keep the status
if (config.status == Status.CURRENT) {
config.status = Status.ENABLED;
}
break;
default:
//do nothing, retain the existing state
break;
}
}
}
/**
* Forget the specified network and save config
*
* @param netId network to forget
* @return {@code true} if it succeeds, {@code false} otherwise
*/
boolean forgetNetwork(int netId) {
if (VDBG) localLog("forgetNetwork", netId);
boolean remove = removeConfigAndSendBroadcastIfNeeded(netId);
if (!remove) {
//success but we dont want to remove the network from supplicant conf file
return true;
}
if (mWifiNative.removeNetwork(netId)) {
mWifiNative.saveConfig();
return true;
} else {
loge("Failed to remove network " + netId);
return false;
}
}
/**
* 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
* @return network Id
*/
int addOrUpdateNetwork(WifiConfiguration config) {
if (VDBG) localLog("addOrUpdateNetwork id=", config.networkId);
//adding unconditional message to chase b/15111865
Log.e(TAG, " key=" + config.configKey() + " netId=" + Integer.toString(config.networkId)
+ " uid=" + Integer.toString(config.creatorUid)
+ "/" + Integer.toString(config.lastUpdateUid));
NetworkUpdateResult result = addOrUpdateNetworkNative(config);
if (result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID) {
WifiConfiguration conf = mConfiguredNetworks.get(result.getNetworkId());
if (conf != null) {
sendConfiguredNetworksChangedBroadcast(conf,
result.isNewNetwork ? WifiManager.CHANGE_REASON_ADDED :
WifiManager.CHANGE_REASON_CONFIG_CHANGE);
}
}
return result.getNetworkId();
}
/**
* 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
* @return {@code true} if it succeeds, {@code false} otherwise
*/
boolean removeNetwork(int netId) {
if (VDBG) localLog("removeNetwork", netId);
boolean ret = mWifiNative.removeNetwork(netId);
if (ret) {
removeConfigAndSendBroadcastIfNeeded(netId);
}
return ret;
}
private boolean removeConfigAndSendBroadcastIfNeeded(int netId) {
boolean remove = true;
WifiConfiguration config = mConfiguredNetworks.get(netId);
if (config != null) {
if (VDBG) {
loge("removeNetwork " + Integer.toString(netId) + " key=" +
config.configKey() + " config.id=" + Integer.toString(config.networkId));
}
// cancel the last user choice
if (config.configKey().equals(lastSelectedConfiguration)) {
lastSelectedConfiguration = null;
}
// Remove any associated keys
if (config.enterpriseConfig != null) {
removeKeys(config.enterpriseConfig);
}
if (config.didSelfAdd) {
if (config.peerWifiConfiguration != null) {
for (WifiConfiguration peer : mConfiguredNetworks.values()) {
if (config.peerWifiConfiguration.equals(peer.configKey())) {
/* the configuration that trigger the add is still there */
remove = false;
}
}
} else {
loge("removeNetwork " + Integer.toString(netId)
+ " key=" + config.configKey()
+ " config.id=" + Integer.toString(config.networkId)
+ " didSelfAdd and null peerWifiConfiguration"
+ " -> dont remove");
remove = false;
}
}
if (remove) {
mConfiguredNetworks.remove(netId);
mNetworkIds.remove(configKey(config));
} else {
/* we can't directly remove the configuration since we added it ourselves, because
* that could cause the system to re-add it right away.
* Instead black list it. It will be unblacklisted only thru a new add.
*/
config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_DELETED);
mWifiNative.disableNetwork(config.networkId);
}
writeIpAndProxyConfigurations();
sendConfiguredNetworksChangedBroadcast(config, WifiManager.CHANGE_REASON_REMOVED);
writeKnownNetworkHistory();
}
return remove;
}
/**
* 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 enabled
* @return {@code true} if it succeeds, {@code false} otherwise
*/
boolean enableNetwork(int netId, boolean disableOthers) {
boolean ret = enableNetworkWithoutBroadcast(netId, disableOthers);
if (disableOthers) {
if (VDBG) localLog("enableNetwork(disableOthers=true) ", netId);
sendConfiguredNetworksChangedBroadcast();
} else {
if (VDBG) localLog("enableNetwork(disableOthers=false) ", netId);
WifiConfiguration enabledNetwork = null;
synchronized(mConfiguredNetworks) {
enabledNetwork = mConfiguredNetworks.get(netId);
}
// check just in case the network was removed by someone else.
if (enabledNetwork != null) {
sendConfiguredNetworksChangedBroadcast(enabledNetwork,
WifiManager.CHANGE_REASON_CONFIG_CHANGE);
}
}
return ret;
}
boolean enableNetworkWithoutBroadcast(int netId, boolean disableOthers) {
boolean ret = mWifiNative.enableNetwork(netId, disableOthers);
WifiConfiguration config = mConfiguredNetworks.get(netId);
if (config != null) config.status = Status.ENABLED;
if (disableOthers) {
markAllNetworksDisabledExcept(netId);
}
return ret;
}
void disableAllNetworks() {
if (VDBG) localLog("disableAllNetworks");
boolean networkDisabled = false;
for(WifiConfiguration config : mConfiguredNetworks.values()) {
if(config != null && config.status != Status.DISABLED) {
if(mWifiNative.disableNetwork(config.networkId)) {
networkDisabled = true;
config.status = Status.DISABLED;
} else {
loge("Disable network failed on " + config.networkId);
}
}
}
if (networkDisabled) {
sendConfiguredNetworksChangedBroadcast();
}
}
/**
* Disable a network. Note that there is no saveConfig operation.
* @param netId network to be disabled
* @return {@code true} if it succeeds, {@code false} otherwise
*/
boolean disableNetwork(int netId) {
return disableNetwork(netId, WifiConfiguration.DISABLED_UNKNOWN_REASON);
}
/**
* Disable a network. Note that there is no saveConfig operation.
* @param netId network to be disabled
* @param reason reason code network was disabled
* @return {@code true} if it succeeds, {@code false} otherwise
*/
boolean disableNetwork(int netId, int reason) {
if (VDBG) localLog("disableNetwork", netId);
boolean ret = mWifiNative.disableNetwork(netId);
WifiConfiguration network = null;
WifiConfiguration config = mConfiguredNetworks.get(netId);
if (VDBG) {
if (config != null) {
loge("disableNetwork netId=" + Integer.toString(netId)
+ " SSID=" + config.SSID
+ " disabled=" + (config.status == Status.DISABLED)
+ " reason=" + Integer.toString(config.disableReason));
}
}
/* Only change the reason if the network was not previously disabled */
if (config != null && config.status != Status.DISABLED) {
config.status = Status.DISABLED;
config.disableReason = reason;
network = config;
}
if (network != null) {
sendConfiguredNetworksChangedBroadcast(network,
WifiManager.CHANGE_REASON_CONFIG_CHANGE);
}
return ret;
}
/**
* Save the configured networks in supplicant to disk
* @return {@code true} if it succeeds, {@code false} otherwise
*/
boolean saveConfig() {
return mWifiNative.saveConfig();
}
/**
* Start WPS pin method configuration with pin obtained
* from the access point
* @param config WPS configuration
* @return Wps result containing status and pin
*/
WpsResult startWpsWithPinFromAccessPoint(WpsInfo config) {
WpsResult result = new WpsResult();
if (mWifiNative.startWpsRegistrar(config.BSSID, config.pin)) {
/* WPS leaves all networks disabled */
markAllNetworksDisabled();
result.status = WpsResult.Status.SUCCESS;
} else {
loge("Failed to start WPS pin method configuration");
result.status = WpsResult.Status.FAILURE;
}
return result;
}
/**
* Start WPS pin method configuration with pin obtained
* from the device
* @return WpsResult indicating status and pin
*/
WpsResult startWpsWithPinFromDevice(WpsInfo config) {
WpsResult result = new WpsResult();
result.pin = mWifiNative.startWpsPinDisplay(config.BSSID);
/* WPS leaves all networks disabled */
if (!TextUtils.isEmpty(result.pin)) {
markAllNetworksDisabled();
result.status = WpsResult.Status.SUCCESS;
} else {
loge("Failed to start WPS pin method configuration");
result.status = WpsResult.Status.FAILURE;
}
return result;
}
/**
* Start WPS push button configuration
* @param config WPS configuration
* @return WpsResult indicating status and pin
*/
WpsResult startWpsPbc(WpsInfo config) {
WpsResult result = new WpsResult();
if (mWifiNative.startWpsPbc(config.BSSID)) {
/* WPS leaves all networks disabled */
markAllNetworksDisabled();
result.status = WpsResult.Status.SUCCESS;
} else {
loge("Failed to start WPS push button configuration");
result.status = WpsResult.Status.FAILURE;
}
return result;
}
/**
* Fetch the static IP configuration for a given network id
*/
StaticIpConfiguration getStaticIpConfiguration(int netId) {
WifiConfiguration config = mConfiguredNetworks.get(netId);
if (config != null) {
return config.getStaticIpConfiguration();
}
return null;
}
/**
* Set the static IP configuration for a given network id
*/
void setStaticIpConfiguration(int netId, StaticIpConfiguration staticIpConfiguration) {
WifiConfiguration config = mConfiguredNetworks.get(netId);
if (config != null) {
config.setStaticIpConfiguration(staticIpConfiguration);
}
}
/**
* set default GW MAC address
*/
void setDefaultGwMacAddress(int netId, String macAddress) {
WifiConfiguration config = mConfiguredNetworks.get(netId);
if (config != null) {
//update defaultGwMacAddress
config.defaultGwMacAddress = macAddress;
}
}
/**
* Fetch the proxy properties for a given network id
* @param network id
* @return ProxyInfo for the network id
*/
ProxyInfo getProxyProperties(int netId) {
WifiConfiguration config = mConfiguredNetworks.get(netId);
if (config != null) {
return config.getHttpProxy();
}
return null;
}
/**
* Return if the specified network is using static IP
* @param network id
* @return {@code true} if using static ip for netId
*/
boolean isUsingStaticIp(int netId) {
WifiConfiguration config = mConfiguredNetworks.get(netId);
if (config != null && config.getIpAssignment() == IpAssignment.STATIC) {
return true;
}
return false;
}
/**
* Should be called when a single network configuration is made.
* @param network The network configuration that changed.
* @param reason The reason for the change, should be one of WifiManager.CHANGE_REASON_ADDED,
* WifiManager.CHANGE_REASON_REMOVED, or WifiManager.CHANGE_REASON_CHANGE.
*/
private void sendConfiguredNetworksChangedBroadcast(WifiConfiguration network,
int reason) {
Intent intent = new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
intent.putExtra(WifiManager.EXTRA_MULTIPLE_NETWORKS_CHANGED, false);
intent.putExtra(WifiManager.EXTRA_WIFI_CONFIGURATION, network);
intent.putExtra(WifiManager.EXTRA_CHANGE_REASON, reason);
mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
}
/**
* Should be called when multiple network configuration changes are made.
*/
private void sendConfiguredNetworksChangedBroadcast() {
Intent intent = new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
intent.putExtra(WifiManager.EXTRA_MULTIPLE_NETWORKS_CHANGED, true);
mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
}
void loadConfiguredNetworks() {
String listStr = mWifiNative.listNetworks();
mLastPriority = 0;
mConfiguredNetworks.clear();
mNetworkIds.clear();
if (listStr == null)
return;
String[] lines = listStr.split("\n");
if (VDBG) {
loge("loadConfiguredNetworks: found " + Integer.toString(lines.length)
+ " networks", true);
}
// 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) {
loge("Failed to read network-id '" + result[0] + "'");
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 > mLastPriority) {
mLastPriority = config.priority;
}
config.setIpAssignment(IpAssignment.DHCP);
config.setProxySettings(ProxySettings.NONE);
if (mNetworkIds.containsKey(configKey(config))) {
// That SSID is already known, just ignore this duplicate entry
if (VDBG) localLog("discarded duplicate network ", config.networkId);
} else if(config.isValid()){
mConfiguredNetworks.put(config.networkId, config);
mNetworkIds.put(configKey(config), config.networkId);
if (VDBG) localLog("loaded configured network", config.networkId);
} else {
if (DBG) log("Ignoring loaded configured for network " + config.networkId
+ " because config are not valid");
}
}
readIpAndProxyConfigurations();
readNetworkHistory();
readAutoJoinConfig();
sendConfiguredNetworksChangedBroadcast();
if (VDBG) localLog("loadConfiguredNetworks loaded " + mNetworkIds.size() + " networks");
if (mNetworkIds.size() == 0) {
// no networks? Lets log if the wpa_supplicant.conf file contents
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader(SUPPLICANT_CONFIG_FILE));
if (VDBG) localLog("--- Begin wpa_supplicant.conf Contents ---");
for (String line = reader.readLine(); line != null; line = reader.readLine()) {
if (VDBG) localLog(line);
}
if (VDBG) localLog("--- End wpa_supplicant.conf Contents ---");
} catch (FileNotFoundException e) {
if (VDBG) localLog("Could not open " + SUPPLICANT_CONFIG_FILE + ", " + e);
} catch (IOException e) {
if (VDBG) localLog("Could not read " + SUPPLICANT_CONFIG_FILE + ", " + e);
} finally {
try {
if (reader != null) {
reader.close();
}
} catch (IOException e) {
// Just ignore the fact that we couldn't close
}
}
}
}
private Map<String, String> readNetworkVariablesFromSupplicantFile(String key) {
Map<String, String> result = new HashMap<>();
BufferedReader reader = null;
if (VDBG) loge("readNetworkVariablesFromSupplicantFile key=" + key);
try {
reader = new BufferedReader(new FileReader(SUPPLICANT_CONFIG_FILE));
boolean found = false;
String networkSsid = null;
String value = null;
for (String line = reader.readLine(); line != null; line = reader.readLine()) {
if (VDBG) loge(line);
if (line.matches("[ \\t]*network=\\{")) {
found = true;
networkSsid = null;
value = null;
} else if (line.matches("[ \\t]*\\{")) {
found = false;
networkSsid = null;
value = null;
}
if (found) {
int index;
if ((index = line.indexOf("ssid=")) >= 0) {
networkSsid = line.substring(index + 5);
} else if ((index = line.indexOf(key + "=")) >= 0) {
value = line.substring(index + key.length() + 1);
}
if (networkSsid != null && value != null) {
result.put(networkSsid, value);
}
}
}
} catch (FileNotFoundException e) {
if (VDBG) loge("Could not open " + SUPPLICANT_CONFIG_FILE + ", " + e);
} catch (IOException e) {
if (VDBG) loge("Could not read " + SUPPLICANT_CONFIG_FILE + ", " + e);
} finally {
try {
if (reader != null) {
reader.close();
}
} catch (IOException e) {
// Just ignore the fact that we couldn't close
}
}
return result;
}
private String readNetworkVariableFromSupplicantFile(String ssid, String key) {
Map<String, String> data = readNetworkVariablesFromSupplicantFile(key);
if (VDBG) loge("readNetworkVariableFromSupplicantFile ssid=[" + ssid + "] key=" + key);
return data.get(ssid);
}
/* Mark all networks except specified netId as disabled */
private void markAllNetworksDisabledExcept(int netId) {
for(WifiConfiguration config : mConfiguredNetworks.values()) {
if(config != null && config.networkId != netId) {
if (config.status != Status.DISABLED) {
config.status = Status.DISABLED;
config.disableReason = WifiConfiguration.DISABLED_UNKNOWN_REASON;
}
}
}
}
private void markAllNetworksDisabled() {
markAllNetworksDisabledExcept(INVALID_NETWORK_ID);
}
boolean needsUnlockedKeyStore() {
// Any network using certificates to authenticate access requires
// unlocked key store; unless the certificates can be stored with
// hardware encryption
for(WifiConfiguration config : mConfiguredNetworks.values()) {
if (config.allowedKeyManagement.get(KeyMgmt.WPA_EAP)
&& config.allowedKeyManagement.get(KeyMgmt.IEEE8021X)) {
if (needsSoftwareBackedKeyStore(config.enterpriseConfig)) {
return true;
}
}
}
return false;
}
public void writeKnownNetworkHistory() {
if (VDBG) {
loge(" writeKnownNetworkHistory() num networks:" +
Integer.toString(mConfiguredNetworks.size()), true);
}
/* Make a copy */
final List<WifiConfiguration> networks = new ArrayList<WifiConfiguration>();
for (WifiConfiguration config : mConfiguredNetworks.values()) {
networks.add(new WifiConfiguration(config));
}
mWriter.write(networkHistoryConfigFile, new DelayedDiskWrite.Writer() {
public void onWriteCalled(DataOutputStream out) throws IOException {
for (WifiConfiguration config : networks) {
//loge("onWriteCalled write SSID: " + config.SSID);
/* if (config.getLinkProperties() != null)
loge(" lp " + config.getLinkProperties().toString());
else
loge("attempt config w/o lp");
*/
if (VDBG) {
int num = 0;
if (config.connectChoices != null) {
num = config.connectChoices.size();
}
loge("saving network history: " + config.configKey() + " gw: " +
config.defaultGwMacAddress + " autojoin status: " +
config.autoJoinStatus + " ephemeral=" + config.ephemeral
+ " choices:" + Integer.toString(num));
}
if (config.ephemeral == true)
continue;
if (config.isValid() == false)
continue;
if (config.SSID == null) {
if (VDBG) {
loge("writeKnownNetworkHistory trying to write config with null SSID");
}
continue;
}
out.writeUTF(CONFIG_KEY + config.configKey() + SEPARATOR_KEY);
out.writeUTF(SSID_KEY + config.SSID + SEPARATOR_KEY);
out.writeUTF(FQDN_KEY + config.FQDN + SEPARATOR_KEY);
out.writeUTF(PRIORITY_KEY + Integer.toString(config.priority) + SEPARATOR_KEY);
out.writeUTF(STATUS_KEY + Integer.toString(config.autoJoinStatus)
+ SEPARATOR_KEY);
out.writeUTF(SUPPLICANT_STATUS_KEY + Integer.toString(config.status)
+ SEPARATOR_KEY);
out.writeUTF(SUPPLICANT_DISABLE_REASON_KEY
+ Integer.toString(config.disableReason)
+ SEPARATOR_KEY);
out.writeUTF(NETWORK_ID_KEY + Integer.toString(config.networkId)
+ SEPARATOR_KEY);
out.writeUTF(SELF_ADDED_KEY + Boolean.toString(config.selfAdded)
+ SEPARATOR_KEY);
out.writeUTF(DID_SELF_ADD_KEY + Boolean.toString(config.didSelfAdd)
+ SEPARATOR_KEY);
if (config.peerWifiConfiguration != null) {
out.writeUTF(PEER_CONFIGURATION_KEY + config.peerWifiConfiguration
+ SEPARATOR_KEY);
}
out.writeUTF(NUM_CONNECTION_FAILURES_KEY
+ Integer.toString(config.numConnectionFailures)
+ SEPARATOR_KEY);
out.writeUTF(SCORER_OVERRIDE_KEY + Integer.toString(config.numScorerOverride)
+ SEPARATOR_KEY);
out.writeUTF(SCORER_OVERRIDE_AND_SWITCH_KEY
+ Integer.toString(config.numScorerOverrideAndSwitchedNetwork)
+ SEPARATOR_KEY);
out.writeUTF(NUM_ASSOCIATION_KEY
+ Integer.toString(config.numAssociation)
+ SEPARATOR_KEY);
out.writeUTF(BLACKLIST_MILLI_KEY + Long.toString(config.blackListTimestamp)
+ SEPARATOR_KEY);
out.writeUTF(CREATOR_UID_KEY + Integer.toString(config.creatorUid)
+ SEPARATOR_KEY);
out.writeUTF(CONNECT_UID_KEY + Integer.toString(config.lastConnectUid)
+ SEPARATOR_KEY);
out.writeUTF(UPDATE_UID_KEY + Integer.toString(config.lastUpdateUid)
+ SEPARATOR_KEY);
String allowedKeyManagementString =
makeString(config.allowedKeyManagement,
WifiConfiguration.KeyMgmt.strings);
out.writeUTF(AUTH_KEY + allowedKeyManagementString + SEPARATOR_KEY);
if (config.connectChoices != null) {
for (String key : config.connectChoices.keySet()) {
Integer choice = config.connectChoices.get(key);
out.writeUTF(CHOICE_KEY + key + "="
+ choice.toString() + SEPARATOR_KEY);
}
}
if (config.linkedConfigurations != null) {
for (String key : config.linkedConfigurations.keySet()) {
out.writeUTF(LINK_KEY + key + SEPARATOR_KEY);
}
}
String macAddress = config.defaultGwMacAddress;
if (macAddress != null) {
out.writeUTF(DEFAULT_GW_KEY + macAddress + SEPARATOR_KEY);
}
if (config.scanResultCache != null) {
for (ScanResult result : config.scanResultCache.values()) {
out.writeUTF(BSSID_KEY + result.BSSID + SEPARATOR_KEY);
out.writeUTF(FREQ_KEY + Integer.toString(result.frequency)
+ SEPARATOR_KEY);
out.writeUTF(RSSI_KEY + Integer.toString(result.level)
+ SEPARATOR_KEY);
out.writeUTF(BSSID_STATUS_KEY
+ Integer.toString(result.autoJoinStatus)
+ SEPARATOR_KEY);
if (result.seen != 0) {
out.writeUTF(MILLI_KEY + Long.toString(result.seen)
+ SEPARATOR_KEY);
}
out.writeUTF(BSSID_KEY_END + SEPARATOR_KEY);
}
}
if (config.lastFailure != null) {
out.writeUTF(FAILURE_KEY + config.lastFailure + SEPARATOR_KEY);
}
out.writeUTF(SEPARATOR_KEY);
}
}
});
}
public void setLastSelectedConfiguration(int netId) {
if (DBG) {
loge("setLastSelectedConfiguration " + Integer.toString(netId));
}
if (netId == WifiConfiguration.INVALID_NETWORK_ID) {
lastSelectedConfiguration = null;
} else {
WifiConfiguration selected = getWifiConfiguration(netId);
if (selected == null) {
lastSelectedConfiguration = null;
} else {
lastSelectedConfiguration = selected.configKey();
if (VDBG) {
loge("setLastSelectedConfiguration now: " + lastSelectedConfiguration);
}
}
}
}
public String getLastSelectedConfiguration() {
return lastSelectedConfiguration;
}
private void readNetworkHistory() {
if (VDBG) {
loge("will readNetworkHistory path:" + networkHistoryConfigFile, true);
}
DataInputStream in = null;
try {
in = new DataInputStream(new BufferedInputStream(new FileInputStream(
networkHistoryConfigFile)));
WifiConfiguration config = null;
while (true) {
int id = -1;
String key = in.readUTF();
String bssid = null;
String ssid = null;
int freq = 0;
int status = 0;
long seen = 0;
int rssi = WifiConfiguration.INVALID_RSSI;
String caps = null;
if (key.startsWith(CONFIG_KEY)) {
if (config != null) {
config = null;
}
String configKey = key.replace(CONFIG_KEY, "");
configKey = configKey.replace(SEPARATOR_KEY, "");
// get the networkId for that config Key
Integer n = mNetworkIds.get(configKey.hashCode());
// skip reading that configuration data
// since we don't have a corresponding network ID
if (n == null) {
loge("readNetworkHistory didnt find netid for hash="
+ Integer.toString(configKey.hashCode())
+ " key: " + configKey);
continue;
}
config = mConfiguredNetworks.get(n);
if (config == null) {
loge("readNetworkHistory didnt find config for netid="
+ n.toString()
+ " key: " + configKey);
}
status = 0;
ssid = null;
bssid = null;
freq = 0;
seen = 0;
rssi = WifiConfiguration.INVALID_RSSI;
caps = null;
} else if (config != null) {
if (key.startsWith(SSID_KEY)) {
ssid = key.replace(SSID_KEY, "");
ssid = ssid.replace(SEPARATOR_KEY, "");
if (config.SSID != null && !config.SSID.equals(ssid)) {
loge("Error parsing network history file, mismatched SSIDs");
config = null; //error
ssid = null;
} else {
config.SSID = ssid;
}
}
if (key.startsWith(FQDN_KEY)) {
String fqdn = key.replace(FQDN_KEY, "");
fqdn = fqdn.replace(SEPARATOR_KEY, "");
config.FQDN = fqdn;
}
if (key.startsWith(DEFAULT_GW_KEY)) {
String gateway = key.replace(DEFAULT_GW_KEY, "");
gateway = gateway.replace(SEPARATOR_KEY, "");
config.defaultGwMacAddress = gateway;
}
if (key.startsWith(STATUS_KEY)) {
String st = key.replace(STATUS_KEY, "");
st = st.replace(SEPARATOR_KEY, "");
config.autoJoinStatus = Integer.parseInt(st);
}
/*
if (key.startsWith(SUPPLICANT_STATUS_KEY)) {
String status = key.replace(SUPPLICANT_STATUS_KEY, "");
status = status.replace(SEPARATOR_KEY, "");
config.status = Integer.parseInt(status);
}
if (key.startsWith(SUPPLICANT_DISABLE_REASON_KEY)) {
String reason = key.replace(SUPPLICANT_DISABLE_REASON_KEY, "");
reason = reason.replace(SEPARATOR_KEY, "");
config.disableReason = Integer.parseInt(reason);
}*/
if (key.startsWith(SELF_ADDED_KEY)) {
String selfAdded = key.replace(SELF_ADDED_KEY, "");
selfAdded = selfAdded.replace(SEPARATOR_KEY, "");
config.selfAdded = Boolean.parseBoolean(selfAdded);
}
if (key.startsWith(DID_SELF_ADD_KEY)) {
String didSelfAdd = key.replace(DID_SELF_ADD_KEY, "");
didSelfAdd = didSelfAdd.replace(SEPARATOR_KEY, "");
config.didSelfAdd = Boolean.parseBoolean(didSelfAdd);
}
if (key.startsWith(CREATOR_UID_KEY)) {
String uid = key.replace(CREATOR_UID_KEY, "");
uid = uid.replace(SEPARATOR_KEY, "");
config.creatorUid = Integer.parseInt(uid);
}
if (key.startsWith(BLACKLIST_MILLI_KEY)) {
String milli = key.replace(BLACKLIST_MILLI_KEY, "");
milli = milli.replace(SEPARATOR_KEY, "");
config.blackListTimestamp = Long.parseLong(milli);
}
if (key.startsWith(NUM_CONNECTION_FAILURES_KEY)) {
String num = key.replace(NUM_CONNECTION_FAILURES_KEY, "");
num = num.replace(SEPARATOR_KEY, "");
config.numConnectionFailures = Integer.parseInt(num);
}
if (key.startsWith(SCORER_OVERRIDE_KEY)) {
String num = key.replace(SCORER_OVERRIDE_KEY, "");
num = num.replace(SEPARATOR_KEY, "");
config.numScorerOverride = Integer.parseInt(num);
}
if (key.startsWith(SCORER_OVERRIDE_AND_SWITCH_KEY)) {
String num = key.replace(SCORER_OVERRIDE_AND_SWITCH_KEY, "");
num = num.replace(SEPARATOR_KEY, "");
config.numScorerOverrideAndSwitchedNetwork = Integer.parseInt(num);
}
if (key.startsWith(NUM_ASSOCIATION_KEY)) {
String num = key.replace(NUM_ASSOCIATION_KEY, "");
num = num.replace(SEPARATOR_KEY, "");
config.numAssociation = Integer.parseInt(num);
}
if (key.startsWith(CONNECT_UID_KEY)) {
String uid = key.replace(CONNECT_UID_KEY, "");
uid = uid.replace(SEPARATOR_KEY, "");
config.lastConnectUid = Integer.parseInt(uid);
}
if (key.startsWith(UPDATE_UID_KEY)) {
String uid = key.replace(UPDATE_UID_KEY, "");
uid = uid.replace(SEPARATOR_KEY, "");
config.lastUpdateUid = Integer.parseInt(uid);
}
if (key.startsWith(FAILURE_KEY)) {
config.lastFailure = key.replace(FAILURE_KEY, "");
config.lastFailure = config.lastFailure.replace(SEPARATOR_KEY, "");
}
if (key.startsWith(PEER_CONFIGURATION_KEY)) {
config.peerWifiConfiguration = key.replace(PEER_CONFIGURATION_KEY, "");
config.peerWifiConfiguration =
config.peerWifiConfiguration.replace(SEPARATOR_KEY, "");
}
if (key.startsWith(CHOICE_KEY)) {
String choiceStr = key.replace(CHOICE_KEY, "");
choiceStr = choiceStr.replace(SEPARATOR_KEY, "");
String configKey = "";
int choice = 0;
Matcher match = mConnectChoice.matcher(choiceStr);
if (!match.find()) {
if (DBG) Log.d(TAG, "WifiConfigStore: connectChoice: " +
" Couldnt match pattern : " + choiceStr);
} else {
configKey = match.group(1);
try {
choice = Integer.parseInt(match.group(2));
} catch (NumberFormatException e) {
choice = 0;
}
if (choice > 0) {
if (config.connectChoices == null) {
config.connectChoices = new HashMap<String, Integer>();
}
config.connectChoices.put(configKey, choice);
}
}
}
if (key.startsWith(LINK_KEY)) {
String configKey = key.replace(LINK_KEY, "");
configKey = configKey.replace(SEPARATOR_KEY, "");
if (config.linkedConfigurations == null) {
config.linkedConfigurations = new HashMap<String, Integer>();
}
if (config.linkedConfigurations != null) {
config.linkedConfigurations.put(configKey, -1);
}
}
if (key.startsWith(BSSID_KEY)) {
if (key.startsWith(BSSID_KEY)) {
bssid = key.replace(BSSID_KEY, "");
bssid = bssid.replace(SEPARATOR_KEY, "");
freq = 0;
seen = 0;
rssi = WifiConfiguration.INVALID_RSSI;
caps = "";
status = 0;
}
if (key.startsWith(RSSI_KEY)) {
String lvl = key.replace(RSSI_KEY, "");
lvl = lvl.replace(SEPARATOR_KEY, "");
rssi = Integer.parseInt(lvl);
}
if (key.startsWith(BSSID_STATUS_KEY)) {
String st = key.replace(BSSID_STATUS_KEY, "");
st = st.replace(SEPARATOR_KEY, "");
status = Integer.parseInt(st);
}
if (key.startsWith(FREQ_KEY)) {
String channel = key.replace(FREQ_KEY, "");
channel = channel.replace(SEPARATOR_KEY, "");
freq = Integer.parseInt(channel);
}
if (key.startsWith(DATE_KEY)) {
/*
* when reading the configuration from file we don't update the date
* so as to avoid reading back stale or non-sensical data that would
* depend on network time.
* The date of a WifiConfiguration should only come from actual scan result.
*
String s = key.replace(FREQ_KEY, "");
seen = Integer.getInteger(s);
*/
}
if (key.startsWith(BSSID_KEY_END)) {
if ((bssid != null) && (ssid != null)) {
if (config.scanResultCache == null) {
config.scanResultCache = new HashMap<String, ScanResult>();
}
WifiSsid wssid = WifiSsid.createFromAsciiEncoded(ssid);
ScanResult result = new ScanResult(wssid, bssid,
caps, rssi, freq, (long) 0);
result.seen = seen;
config.scanResultCache.put(bssid, result);
result.autoJoinStatus = status;
}
}
}
}
}
} catch (EOFException ignore) {
if (in != null) {
try {
in.close();
} catch (Exception e) {
loge("readNetworkHistory: Error reading file" + e);
}
}
} catch (IOException e) {
loge("readNetworkHistory: No config file, revert to default" + e);
}
if(in!=null) {
try {
in.close();
} catch (Exception e) {
loge("readNetworkHistory: Error closing file" + e);
}
}
}
private void readAutoJoinConfig() {
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader(autoJoinConfigFile));
for (String key = reader.readLine(); key != null; key = reader.readLine()) {
if (key != null) {
Log.d(TAG, "readAutoJoinConfig line: " + key);
}
if (key.startsWith(ENABLE_AUTOJOIN_WHILE_ASSOCIATED_KEY)) {
String st = key.replace(ENABLE_AUTOJOIN_WHILE_ASSOCIATED_KEY, "");
st = st.replace(SEPARATOR_KEY, "");
try {
enableAutoJoinWhileAssociated = Integer.parseInt(st) != 0;
Log.d(TAG,"readAutoJoinConfig: enabled = " + enableAutoJoinWhileAssociated);
} catch (NumberFormatException e) {
Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
}
}
if (key.startsWith(THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_5G_KEY)) {
String st =
key.replace(THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_5G_KEY, "");
st = st.replace(SEPARATOR_KEY, "");
try {
thresholdInitialAutoJoinAttemptMin5RSSI = Integer.parseInt(st);
Log.d(TAG,"readAutoJoinConfig: thresholdInitialAutoJoinAttemptMin5RSSI = "
+ Integer.toString(thresholdInitialAutoJoinAttemptMin5RSSI));
} catch (NumberFormatException e) {
Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
}
}
if (key.startsWith(THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_24G_KEY)) {
String st =
key.replace(THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_24G_KEY, "");
st = st.replace(SEPARATOR_KEY, "");
try {
thresholdInitialAutoJoinAttemptMin24RSSI = Integer.parseInt(st);
Log.d(TAG,"readAutoJoinConfig: thresholdInitialAutoJoinAttemptMin24RSSI = "
+ Integer.toString(thresholdInitialAutoJoinAttemptMin24RSSI));
} catch (NumberFormatException e) {
Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
}
}
if (key.startsWith(THRESHOLD_UNBLACKLIST_HARD_5G_KEY)) {
String st = key.replace(THRESHOLD_UNBLACKLIST_HARD_5G_KEY, "");
st = st.replace(SEPARATOR_KEY, "");
try {
thresholdUnblacklistThreshold5Hard = Integer.parseInt(st);
Log.d(TAG,"readAutoJoinConfig: thresholdUnblacklistThreshold5Hard = "
+ Integer.toString(thresholdUnblacklistThreshold5Hard));
} catch (NumberFormatException e) {
Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
}
}
if (key.startsWith(THRESHOLD_UNBLACKLIST_SOFT_5G_KEY)) {
String st = key.replace(THRESHOLD_UNBLACKLIST_SOFT_5G_KEY, "");
st = st.replace(SEPARATOR_KEY, "");
try {
thresholdUnblacklistThreshold5Soft = Integer.parseInt(st);
Log.d(TAG,"readAutoJoinConfig: thresholdUnblacklistThreshold5Soft = "
+ Integer.toString(thresholdUnblacklistThreshold5Soft));
} catch (NumberFormatException e) {
Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
}
}
if (key.startsWith(THRESHOLD_UNBLACKLIST_HARD_24G_KEY)) {
String st = key.replace(THRESHOLD_UNBLACKLIST_HARD_24G_KEY, "");
st = st.replace(SEPARATOR_KEY, "");
try {
thresholdUnblacklistThreshold24Hard = Integer.parseInt(st);
Log.d(TAG,"readAutoJoinConfig: thresholdUnblacklistThreshold24Hard = "
+ Integer.toString(thresholdUnblacklistThreshold24Hard));
} catch (NumberFormatException e) {
Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
}
}
if (key.startsWith(THRESHOLD_UNBLACKLIST_SOFT_24G_KEY)) {
String st = key.replace(THRESHOLD_UNBLACKLIST_SOFT_24G_KEY, "");
st = st.replace(SEPARATOR_KEY, "");
try {
thresholdUnblacklistThreshold24Soft = Integer.parseInt(st);
Log.d(TAG,"readAutoJoinConfig: thresholdUnblacklistThreshold24Soft = "
+ Integer.toString(thresholdUnblacklistThreshold24Soft));
} catch (NumberFormatException e) {
Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
}
}
if (key.startsWith(THRESHOLD_GOOD_RSSI_5_KEY)) {
String st = key.replace(THRESHOLD_GOOD_RSSI_5_KEY, "");
st = st.replace(SEPARATOR_KEY, "");
try {
thresholdGoodRssi5 = Integer.parseInt(st);
Log.d(TAG,"readAutoJoinConfig: thresholdGoodRssi5 = "
+ Integer.toString(thresholdGoodRssi5));
} catch (NumberFormatException e) {
Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
}
}
if (key.startsWith(THRESHOLD_LOW_RSSI_5_KEY)) {
String st = key.replace(THRESHOLD_LOW_RSSI_5_KEY, "");
st = st.replace(SEPARATOR_KEY, "");
try {
thresholdLowRssi5 = Integer.parseInt(st);
Log.d(TAG,"readAutoJoinConfig: thresholdLowRssi5 = "
+ Integer.toString(thresholdLowRssi5));
} catch (NumberFormatException e) {
Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
}
}
if (key.startsWith(THRESHOLD_BAD_RSSI_5_KEY)) {
String st = key.replace(THRESHOLD_BAD_RSSI_5_KEY, "");
st = st.replace(SEPARATOR_KEY, "");
try {
thresholdBadRssi5 = Integer.parseInt(st);
Log.d(TAG,"readAutoJoinConfig: thresholdBadRssi5 = "
+ Integer.toString(thresholdBadRssi5));
} catch (NumberFormatException e) {
Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
}
}
if (key.startsWith(THRESHOLD_GOOD_RSSI_24_KEY)) {
String st = key.replace(THRESHOLD_GOOD_RSSI_24_KEY, "");
st = st.replace(SEPARATOR_KEY, "");
try {
thresholdGoodRssi24 = Integer.parseInt(st);
Log.d(TAG,"readAutoJoinConfig: thresholdGoodRssi24 = "
+ Integer.toString(thresholdGoodRssi24));
} catch (NumberFormatException e) {
Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
}
}
if (key.startsWith(THRESHOLD_LOW_RSSI_24_KEY)) {
String st = key.replace(THRESHOLD_LOW_RSSI_24_KEY, "");
st = st.replace(SEPARATOR_KEY, "");
try {
thresholdLowRssi24 = Integer.parseInt(st);
Log.d(TAG,"readAutoJoinConfig: thresholdLowRssi24 = "
+ Integer.toString(thresholdLowRssi24));
} catch (NumberFormatException e) {
Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
}
}
if (key.startsWith(THRESHOLD_BAD_RSSI_24_KEY)) {
String st = key.replace(THRESHOLD_BAD_RSSI_24_KEY, "");
st = st.replace(SEPARATOR_KEY, "");
try {
thresholdBadRssi24 = Integer.parseInt(st);
Log.d(TAG,"readAutoJoinConfig: thresholdBadRssi24 = "
+ Integer.toString(thresholdBadRssi24));
} catch (NumberFormatException e) {
Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
}
}
if (key.startsWith(THRESHOLD_MAX_TX_PACKETS_FOR_NETWORK_SWITCHING_KEY)) {
String st = key.replace(THRESHOLD_MAX_TX_PACKETS_FOR_NETWORK_SWITCHING_KEY, "");
st = st.replace(SEPARATOR_KEY, "");
try {
maxTxPacketForNetworkSwitching = Integer.parseInt(st);
Log.d(TAG,"readAutoJoinConfig: maxTxPacketForNetworkSwitching = "
+ Integer.toString(maxTxPacketForNetworkSwitching));
} catch (NumberFormatException e) {
Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
}
}
if (key.startsWith(THRESHOLD_MAX_RX_PACKETS_FOR_NETWORK_SWITCHING_KEY)) {
String st = key.replace(THRESHOLD_MAX_RX_PACKETS_FOR_NETWORK_SWITCHING_KEY, "");
st = st.replace(SEPARATOR_KEY, "");
try {
maxRxPacketForNetworkSwitching = Integer.parseInt(st);
Log.d(TAG,"readAutoJoinConfig: maxRxPacketForNetworkSwitching = "
+ Integer.toString(maxRxPacketForNetworkSwitching));
} catch (NumberFormatException e) {
Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
}
}
if (key.startsWith(A_BAND_PREFERENCE_RSSI_THRESHOLD_LOW_KEY)) {
String st = key.replace(A_BAND_PREFERENCE_RSSI_THRESHOLD_LOW_KEY, "");
st = st.replace(SEPARATOR_KEY, "");
try {
thresholdBandPreferenceLowRssi5 = Integer.parseInt(st);
Log.d(TAG,"readAutoJoinConfig: thresholdBandPreferenceLowRssi5 = "
+ Integer.toString(thresholdBandPreferenceLowRssi5));
} catch (NumberFormatException e) {
Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
}
}
if (key.startsWith(A_BAND_PREFERENCE_RSSI_THRESHOLD_KEY)) {
String st = key.replace(A_BAND_PREFERENCE_RSSI_THRESHOLD_KEY, "");
st = st.replace(SEPARATOR_KEY, "");
try {
thresholdBandPreferenceRssi5 = Integer.parseInt(st);
Log.d(TAG,"readAutoJoinConfig: thresholdBandPreferenceRssi5 = "
+ Integer.toString(thresholdBandPreferenceRssi5));
} catch (NumberFormatException e) {
Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
}
}
if (key.startsWith(G_BAND_PREFERENCE_RSSI_THRESHOLD_KEY)) {
String st = key.replace(G_BAND_PREFERENCE_RSSI_THRESHOLD_KEY, "");
st = st.replace(SEPARATOR_KEY, "");
try {
thresholdBandPreferenceRssi24 = Integer.parseInt(st);
Log.d(TAG,"readAutoJoinConfig: thresholdBandPreferenceRssi24 = "
+ Integer.toString(thresholdBandPreferenceRssi24));
} catch (NumberFormatException e) {
Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
}
}
}
} catch (EOFException ignore) {
if (reader != null) {
try {
reader.close();
reader = null;
} catch (Exception e) {
loge("readAutoJoinStatus: Error closing file" + e);
}
}
} catch (IOException e) {
loge("readAutoJoinStatus: Error parsing configuration" + e);
}
if (reader!=null) {
try {
reader.close();
} catch (Exception e) {
loge("readAutoJoinStatus: Error closing file" + e);
}
}
}
private void writeIpAndProxyConfigurations() {
final SparseArray<IpConfiguration> networks = new SparseArray<IpConfiguration>();
for(WifiConfiguration config : mConfiguredNetworks.values()) {
if (!config.ephemeral && config.autoJoinStatus != WifiConfiguration.AUTO_JOIN_DELETED) {
networks.put(configKey(config), config.getIpConfiguration());
}
}
super.writeIpAndProxyConfigurations(ipConfigFile, networks);
}
private void readIpAndProxyConfigurations() {
SparseArray<IpConfiguration> networks = super.readIpAndProxyConfigurations(ipConfigFile);
if (networks.size() == 0) {
// IpConfigStore.readIpAndProxyConfigurations has already logged an error.
return;
}
for (int i = 0; i < networks.size(); i++) {
int id = networks.keyAt(i);
WifiConfiguration config = mConfiguredNetworks.get(mNetworkIds.get(id));
if (config == null || config.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DELETED) {
loge("configuration found for missing network, nid=" + id
+", ignored, networks.size=" + Integer.toString(networks.size()));
} else {
config.setIpConfiguration(networks.valueAt(i));
}
}
}
/*
* Convert string to Hexadecimal before passing to wifi native layer
* In native function "doCommand()" have trouble in converting Unicode character string to UTF8
* conversion to hex is required because SSIDs can have space characters in them;
* and that can confuses the supplicant because it uses space charaters as delimiters
*/
private String encodeSSID(String str){
String tmp = removeDoubleQuotes(str);
return String.format("%x", new BigInteger(1, tmp.getBytes(Charset.forName("UTF-8"))));
}
private NetworkUpdateResult addOrUpdateNetworkNative(WifiConfiguration config) {
/*
* If the supplied networkId is INVALID_NETWORK_ID, we create a new empty
* network configuration. Otherwise, the networkId should
* refer to an existing configuration.
*/
if (VDBG) localLog("addOrUpdateNetworkNative " + config.getPrintableSsid());
int netId = config.networkId;
boolean newNetwork = false;
// networkId of INVALID_NETWORK_ID means we want to create a new network
if (netId == INVALID_NETWORK_ID) {
Integer savedNetId = mNetworkIds.get(configKey(config));
//paranoia: check if either we have a network Id or a WifiConfiguration
//matching the one we are trying to add.
if (savedNetId == null) {
for (WifiConfiguration test : mConfiguredNetworks.values()) {
if (test.configKey().equals(config.configKey())) {
savedNetId = test.networkId;
loge("addOrUpdateNetworkNative " + config.configKey()
+ " was found, but no network Id");
break;
}
}
}
if (savedNetId != null) {
netId = savedNetId;
} else {
newNetwork = true;
netId = mWifiNative.addNetwork();
if (netId < 0) {
loge("Failed to add a network!");
return new NetworkUpdateResult(INVALID_NETWORK_ID);
} else {
loge("addOrUpdateNetworkNative created netId=" + netId);
}
}
}
boolean updateFailed = true;
setVariables: {
if (config.SSID != null &&
!mWifiNative.setNetworkVariable(
netId,
WifiConfiguration.ssidVarName,
encodeSSID(config.SSID))) {
loge("failed to set SSID: "+config.SSID);
break setVariables;
}
if (config.BSSID != null) {
loge("Setting BSSID for " + config.configKey() + " to " + config.BSSID);
if (!mWifiNative.setNetworkVariable(
netId,
WifiConfiguration.bssidVarName,
config.BSSID)) {
loge("failed to set BSSID: " + config.BSSID);
break setVariables;
}
}
String allowedKeyManagementString =
makeString(config.allowedKeyManagement, WifiConfiguration.KeyMgmt.strings);
if (config.allowedKeyManagement.cardinality() != 0 &&
!mWifiNative.setNetworkVariable(
netId,
WifiConfiguration.KeyMgmt.varName,
allowedKeyManagementString)) {
loge("failed to set key_mgmt: "+
allowedKeyManagementString);
break setVariables;
}
String allowedProtocolsString =
makeString(config.allowedProtocols, WifiConfiguration.Protocol.strings);
if (config.allowedProtocols.cardinality() != 0 &&
!mWifiNative.setNetworkVariable(
netId,
WifiConfiguration.Protocol.varName,
allowedProtocolsString)) {
loge("failed to set proto: "+
allowedProtocolsString);
break setVariables;
}
String allowedAuthAlgorithmsString =
makeString(config.allowedAuthAlgorithms, WifiConfiguration.AuthAlgorithm.strings);
if (config.allowedAuthAlgorithms.cardinality() != 0 &&
!mWifiNative.setNetworkVariable(
netId,
WifiConfiguration.AuthAlgorithm.varName,
allowedAuthAlgorithmsString)) {
loge("failed to set auth_alg: "+
allowedAuthAlgorithmsString);
break setVariables;
}
String allowedPairwiseCiphersString =
makeString(config.allowedPairwiseCiphers,
WifiConfiguration.PairwiseCipher.strings);
if (config.allowedPairwiseCiphers.cardinality() != 0 &&
!mWifiNative.setNetworkVariable(
netId,
WifiConfiguration.PairwiseCipher.varName,
allowedPairwiseCiphersString)) {
loge("failed to set pairwise: "+
allowedPairwiseCiphersString);
break setVariables;
}
String allowedGroupCiphersString =
makeString(config.allowedGroupCiphers, WifiConfiguration.GroupCipher.strings);
if (config.allowedGroupCiphers.cardinality() != 0 &&
!mWifiNative.setNetworkVariable(
netId,
WifiConfiguration.GroupCipher.varName,
allowedGroupCiphersString)) {
loge("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("*") &&
!mWifiNative.setNetworkVariable(
netId,
WifiConfiguration.pskVarName,
config.preSharedKey)) {
loge("failed to set psk");
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 (!mWifiNative.setNetworkVariable(
netId,
WifiConfiguration.wepKeyVarNames[i],
config.wepKeys[i])) {
loge("failed to set wep_key" + i + ": " + config.wepKeys[i]);
break setVariables;
}
hasSetKey = true;
}
}
}
if (hasSetKey) {
if (!mWifiNative.setNetworkVariable(
netId,
WifiConfiguration.wepTxKeyIdxVarName,
Integer.toString(config.wepTxKeyIndex))) {
loge("failed to set wep_tx_keyidx: " + config.wepTxKeyIndex);
break setVariables;
}
}
if (!mWifiNative.setNetworkVariable(
netId,
WifiConfiguration.priorityVarName,
Integer.toString(config.priority))) {
loge(config.SSID + ": failed to set priority: "
+config.priority);
break setVariables;
}
if (config.hiddenSSID && !mWifiNative.setNetworkVariable(
netId,
WifiConfiguration.hiddenSSIDVarName,
Integer.toString(config.hiddenSSID ? 1 : 0))) {
loge(config.SSID + ": failed to set hiddenSSID: "+
config.hiddenSSID);
break setVariables;
}
if (config.requirePMF && !mWifiNative.setNetworkVariable(
netId,
WifiConfiguration.pmfVarName,
"2")) {
loge(config.SSID + ": failed to set requirePMF: "+
config.requirePMF);
break setVariables;
}
if (config.updateIdentifier != null && !mWifiNative.setNetworkVariable(
netId,
WifiConfiguration.updateIdentiferVarName,
config.updateIdentifier)) {
loge(config.SSID + ": failed to set updateIdentifier: "+
config.updateIdentifier);
break setVariables;
}
if (config.enterpriseConfig != null &&
config.enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE) {
WifiEnterpriseConfig enterpriseConfig = config.enterpriseConfig;
if (needsKeyStore(enterpriseConfig)) {
/**
* Keyguard settings may eventually be controlled by device policy.
* We check here if keystore is unlocked before installing
* credentials.
* TODO: Do we need a dialog here ?
*/
if (mKeyStore.state() != KeyStore.State.UNLOCKED) {
loge(config.SSID + ": key store is locked");
break setVariables;
}
try {
/* config passed may include only fields being updated.
* In order to generate the key id, fetch uninitialized
* fields from the currently tracked configuration
*/
WifiConfiguration currentConfig = mConfiguredNetworks.get(netId);
String keyId = config.getKeyIdForCredentials(currentConfig);
if (!installKeys(enterpriseConfig, keyId)) {
loge(config.SSID + ": failed to install keys");
break setVariables;
}
} catch (IllegalStateException e) {
loge(config.SSID + " invalid config for key installation");
break setVariables;
}
}
HashMap<String, String> enterpriseFields = enterpriseConfig.getFields();
for (String key : enterpriseFields.keySet()) {
String value = enterpriseFields.get(key);
if (key.equals("password") && value != null && value.equals("*")) {
//no need to try to set an obfuscated password, which will fail
continue;
}
if (!mWifiNative.setNetworkVariable(
netId,
key,
value)) {
removeKeys(enterpriseConfig);
loge(config.SSID + ": failed to set " + key +
": " + value);
break setVariables;
}
}
}
updateFailed = false;
} //end of setVariables
if (updateFailed) {
if (newNetwork) {
mWifiNative.removeNetwork(netId);
loge("Failed to set a network variable, removed network: " + netId);
}
return new NetworkUpdateResult(INVALID_NETWORK_ID);
}
/* An update of the network variables requires reading them
* back from the supplicant to update mConfiguredNetworks.
* This is because some of the variables (SSID, wep keys &
* passphrases) reflect different values when read back than
* when written. For example, wep key is stored as * irrespective
* of the value sent to the supplicant
*/
WifiConfiguration currentConfig = mConfiguredNetworks.get(netId);
if (currentConfig == null) {
currentConfig = new WifiConfiguration();
currentConfig.setIpAssignment(IpAssignment.DHCP);
currentConfig.setProxySettings(ProxySettings.NONE);
currentConfig.networkId = netId;
if (config != null) {
//carry over the creation parameters
currentConfig.selfAdded = config.selfAdded;
currentConfig.didSelfAdd = config.didSelfAdd;
currentConfig.lastConnectUid = config.lastConnectUid;
currentConfig.lastUpdateUid = config.lastUpdateUid;
currentConfig.creatorUid = config.creatorUid;
currentConfig.peerWifiConfiguration = config.peerWifiConfiguration;
}
if (DBG) {
loge("created new config netId=" + Integer.toString(netId)
+ " uid=" + Integer.toString(currentConfig.creatorUid));
}
}
if (currentConfig.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DELETED) {
//make sure the configuration is not deleted anymore since we just
//added or modified it.
currentConfig.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED);
currentConfig.selfAdded = false;
currentConfig.didSelfAdd = false;
}
if (DBG) loge("will read network variables netId=" + Integer.toString(netId));
readNetworkVariables(currentConfig);
mConfiguredNetworks.put(netId, currentConfig);
mNetworkIds.put(configKey(currentConfig), netId);
NetworkUpdateResult result = writeIpAndProxyConfigurationsOnChange(currentConfig, config);
result.setIsNewNetwork(newNetwork);
result.setNetworkId(netId);
return result;
}
public void linkConfiguration(WifiConfiguration config) {
if (config.scanResultCache != null && config.scanResultCache.size() > 6) {
// Ignore configurations with large number of BSSIDs
return;
}
if (!config.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
// Only link WPA_PSK config
return;
}
for (WifiConfiguration link : mConfiguredNetworks.values()) {
boolean doLink = false;
if (link.configKey().equals(config.configKey())) {
continue;
}
if (link.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DELETED) {
continue;
}
// Autojoin will be allowed to dynamically jump from a linked configuration
// to another, hence only link configurations that have equivalent level of security
if (!link.allowedKeyManagement.equals(config.allowedKeyManagement)) {
continue;
}
if (link.scanResultCache != null && link.scanResultCache.size() > 6) {
// Ignore configurations with large number of BSSIDs
continue;
}
if (config.defaultGwMacAddress != null && link.defaultGwMacAddress != null) {
// If both default GW are known, compare based on RSSI only if the GW is equal
if (config.defaultGwMacAddress.equals(link.defaultGwMacAddress)) {
if (VDBG) {
loge("linkConfiguration link due to same gw" + link.SSID +
" and " + config.SSID + " GW " + config.defaultGwMacAddress);
}
doLink = true;
}
} else {
// We do not know BOTH default gateways hence we will try to link
// hoping that WifiConfigurations are indeed behind the same gateway.
// once both WifiConfiguration have been tried and thus once both efault gateways
// are known we will revisit the choice of linking them
if ((config.scanResultCache != null) && (config.scanResultCache.size() <= 6)
&& (link.scanResultCache != null) && (link.scanResultCache.size() <= 6)) {
for (String abssid : config.scanResultCache.keySet()) {
for (String bbssid : link.scanResultCache.keySet()) {
if (VDBG) {
loge("linkConfiguration try to link due to DBDC BSSID match "
+ link.SSID +
" and " + config.SSID + " bssida " + abssid
+ " bssidb " + bbssid);
}
if (abssid.regionMatches(true, 0, bbssid, 0, 16)) {
// If first 16 ascii characters of BSSID matches,
// we assume this is a DBDC
doLink = true;
}
}
}
}
}
if (doLink) {
if (VDBG) {
loge("linkConfiguration: will link " + link.SSID + " and " + config.SSID);
}
if (link.linkedConfigurations == null) {
link.linkedConfigurations = new HashMap<String, Integer>();
}
if (config.linkedConfigurations == null) {
config.linkedConfigurations = new HashMap<String, Integer>();
}
link.linkedConfigurations.put(config.configKey(), Integer.valueOf(1));
config.linkedConfigurations.put(link.configKey(), Integer.valueOf(1));
} else {
//todo if they are linked, break the link
}
}
}
/*
* We try to link a scan result with a WifiConfiguration for which SSID and
* key management dont match,
* for instance, we try identify the 5GHz SSID of a DBDC AP,
* even though we know only of the 2.4GHz
*
* Obviously, this function is not optimal since it is used to compare every scan
* result with every Saved WifiConfiguration, with a string.equals operation.
* As a speed up, might be better to implement the mConfiguredNetworks store as a
* <String, WifiConfiguration> object instead of a <Integer, WifiConfiguration> object
* so as to speed this up. Also to prevent the tiny probability of hash collision.
*
*/
public WifiConfiguration associateWithConfiguration(ScanResult result) {
String configKey = WifiConfiguration.configKey(result);
if (configKey == null) {
if (DBG) loge("associateWithConfiguration(): no config key " );
return null;
}
//need to compare with quoted string
String SSID = "\"" + result.SSID + "\"";
WifiConfiguration config = null;
for (WifiConfiguration link : mConfiguredNetworks.values()) {
boolean doLink = false;
if (link.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DELETED || link.didSelfAdd) {
//make sure we dont associate the scan result to a deleted config
continue;
}
if (configKey.equals(link.configKey())) {
if (VDBG) loge("associateWithConfiguration(): found it!!! " + configKey );
return link; //found it exactly
}
if ((link.scanResultCache != null) && (link.scanResultCache.size() <= 4)) {
String bssid = "";
for (String key : link.scanResultCache.keySet()) {
bssid = key;
}
if (result.BSSID.regionMatches(true, 0, bssid, 0, 16)
&& SSID.regionMatches(false, 0, link.SSID, 0, 3)) {
// if first 16 ascii characters of BSSID matches, and first 3
// characters of SSID match, we assume this is a home setup
// and thus we will try to transfer the password from the known
// BSSID/SSID to the recently found BSSID/SSID
//if (VDBG)
// loge("associateWithConfiguration OK " );
doLink = true;
}
}
if (doLink) {
//try to make a non verified WifiConfiguration, but only if the original
//configuration was not self already added
if (VDBG) {
loge("associateWithConfiguration: will create " +
result.SSID + " and associate it with: " + link.SSID);
}
config = wifiConfigurationFromScanResult(result);
if (config != null) {
config.selfAdded = true;
config.didSelfAdd = true;
config.peerWifiConfiguration = link.configKey();
if (config.allowedKeyManagement.equals(link.allowedKeyManagement) &&
config.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
//transfer the credentials from the configuration we are linking from
String psk = readNetworkVariableFromSupplicantFile(link.SSID, "psk");
if (psk != null) {
config.preSharedKey = psk;
if (VDBG) {
if (config.preSharedKey != null)
loge(" transfer PSK : " + config.preSharedKey);
}
//link configurations
if (link.linkedConfigurations == null) {
link.linkedConfigurations = new HashMap<String, Integer>();
}
if (config.linkedConfigurations == null) {
config.linkedConfigurations = new HashMap<String, Integer>();
}
link.linkedConfigurations.put(config.configKey(), Integer.valueOf(1));
config.linkedConfigurations.put(link.configKey(), Integer.valueOf(1));
} else {
config = null;
}
} else {
config = null;
}
}
} else {
//todo if they are linked, break the link
}
}
return config;
}
public HashSet<Integer> makeChannelList(WifiConfiguration config, int age, boolean restrict) {
if (config == null)
return null;
long now_ms = System.currentTimeMillis();
HashSet<Integer> channels = new HashSet<Integer>();
//get channels for this configuration, if there are at least 2 BSSIDs
if (config.scanResultCache == null && config.linkedConfigurations == null) {
return null;
}
if (VDBG) {
StringBuilder dbg = new StringBuilder();
dbg.append("makeChannelList age=" + Integer.toString(age)
+ " for " + config.configKey());
if (config.scanResultCache != null) {
dbg.append(" bssids=" + config.scanResultCache.size());
}
if (config.linkedConfigurations != null) {
dbg.append(" linked=" + config.linkedConfigurations.size());
}
loge(dbg.toString());
}
if (config.scanResultCache != null && config.scanResultCache.size() > 0) {
for (ScanResult result : config.scanResultCache.values()) {
if (VDBG) {
boolean test = (now_ms - result.seen) < age;
loge("has " + result.BSSID + " freq=" + Integer.toString(result.frequency)
+ " age=" + Long.toString(now_ms - result.seen) + " ?=" + test);
}
if (((now_ms - result.seen) < age)/*||(!restrict || result.is24GHz())*/) {
channels.add(result.frequency);
}
}
}
//get channels for linked configurations
if (config.linkedConfigurations != null) {
for (String key : config.linkedConfigurations.keySet()) {
WifiConfiguration linked = getWifiConfiguration(key);
if (linked == null)
continue;
if (linked.scanResultCache == null) {
continue;
}
for (ScanResult result : linked.scanResultCache.values()) {
if (VDBG) {
loge("has link: " + result.BSSID
+ " freq=" + Integer.toString(result.frequency)
+ " age=" + Long.toString(now_ms - result.seen));
}
if (((now_ms - result.seen) < age)/*||(!restrict || result.is24GHz())*/) {
channels.add(result.frequency);
}
}
}
}
return channels;
}
public WifiConfiguration updateSavedNetworkHistory(ScanResult scanResult) {
WifiConfiguration found = null;
if (scanResult == null)
return found;
//first step, look for this scan Result by SSID + Key Management
String key = WifiConfiguration.configKey(scanResult);
int hash = key.hashCode();
Integer netId = mNetworkIds.get(hash);
if (netId == null) return null;
WifiConfiguration config = mConfiguredNetworks.get(netId);
if (config != null) {
if (config.scanResultCache == null) {
config.scanResultCache = new HashMap<String, ScanResult>();
}
if (config.scanResultCache == null) {
return null;
}
//add the scan result to this WifiConfiguration
config.scanResultCache.put(scanResult.BSSID, scanResult);
mConfiguredNetworks.put(netId, config);
linkConfiguration(config);
found = config;
}
if (VDBG) {
config = mConfiguredNetworks.get(netId);
if (config != null) {
if (config.scanResultCache != null) {
String status = "";
if (scanResult.autoJoinStatus > 0) {
status = " status=" + Integer.toString(scanResult.autoJoinStatus);
}
loge(" got known scan result " +
scanResult.BSSID + " key : " + key + " num: " +
Integer.toString(config.scanResultCache.size())
+ " rssi=" + Integer.toString(scanResult.level)
+ " freq=" + Integer.toString(scanResult.frequency)
+ status);
} else {
loge(" got known scan result and no cache" +
scanResult.BSSID + " key : " + key);
}
}
}
return found;
}
/* Compare current and new configuration and write to file on change */
private NetworkUpdateResult writeIpAndProxyConfigurationsOnChange(
WifiConfiguration currentConfig,
WifiConfiguration newConfig) {
boolean ipChanged = false;
boolean proxyChanged = false;
if (VDBG) {
loge("writeIpAndProxyConfigurationsOnChange: " + currentConfig.SSID + " -> " +
newConfig.SSID + " path: " + ipConfigFile);
}
switch (newConfig.getIpAssignment()) {
case STATIC:
if (currentConfig.getIpAssignment() != newConfig.getIpAssignment()) {
ipChanged = true;
} else {
ipChanged = !Objects.equals(
currentConfig.getStaticIpConfiguration(),
newConfig.getStaticIpConfiguration());
}
break;
case DHCP:
if (currentConfig.getIpAssignment() != newConfig.getIpAssignment()) {
ipChanged = true;
}
break;
case UNASSIGNED:
/* Ignore */
break;
default:
loge("Ignore invalid ip assignment during write");
break;
}
switch (newConfig.getProxySettings()) {
case STATIC:
case PAC:
ProxyInfo newHttpProxy = newConfig.getHttpProxy();
ProxyInfo currentHttpProxy = currentConfig.getHttpProxy();
if (newHttpProxy != null) {
proxyChanged = !newHttpProxy.equals(currentHttpProxy);
} else {
proxyChanged = (currentHttpProxy != null);
}
break;
case NONE:
if (currentConfig.getProxySettings() != newConfig.getProxySettings()) {
proxyChanged = true;
}
break;
case UNASSIGNED:
/* Ignore */
break;
default:
loge("Ignore invalid proxy configuration during write");
break;
}
if (ipChanged) {
currentConfig.setIpAssignment(newConfig.getIpAssignment());
currentConfig.setStaticIpConfiguration(newConfig.getStaticIpConfiguration());
log("IP config changed SSID = " + currentConfig.SSID);
if (currentConfig.getStaticIpConfiguration() != null) {
log(" static configuration: " +
currentConfig.getStaticIpConfiguration().toString());
}
}
if (proxyChanged) {
currentConfig.setProxySettings(newConfig.getProxySettings());
currentConfig.setHttpProxy(newConfig.getHttpProxy());
log("proxy changed SSID = " + currentConfig.SSID);
if (currentConfig.getHttpProxy() != null) {
log(" proxyProperties: " + currentConfig.getHttpProxy().toString());
}
}
if (ipChanged || proxyChanged) {
writeIpAndProxyConfigurations();
sendConfiguredNetworksChangedBroadcast(currentConfig,
WifiManager.CHANGE_REASON_CONFIG_CHANGE);
}
return new NetworkUpdateResult(ipChanged, proxyChanged);
}
/** Returns true if a particular config key needs to be quoted when passed to the supplicant. */
private boolean enterpriseConfigKeyShouldBeQuoted(String key) {
switch (key) {
case WifiEnterpriseConfig.EAP_KEY:
case WifiEnterpriseConfig.ENGINE_KEY:
return false;
default:
return true;
}
}
/**
* 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 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 = mWifiNative.getNetworkVariable(netId, WifiConfiguration.ssidVarName);
if (!TextUtils.isEmpty(value)) {
if (value.charAt(0) != '"') {
config.SSID = "\"" + WifiSsid.createFromHex(value).toString() + "\"";
//TODO: convert a hex string that is not UTF-8 decodable to a P-formatted
//supplicant string
} else {
config.SSID = value;
}
} else {
config.SSID = null;
}
value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.bssidVarName);
if (!TextUtils.isEmpty(value)) {
config.BSSID = value;
} else {
config.BSSID = null;
}
value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.priorityVarName);
config.priority = -1;
if (!TextUtils.isEmpty(value)) {
try {
config.priority = Integer.parseInt(value);
} catch (NumberFormatException ignore) {
}
}
value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.hiddenSSIDVarName);
config.hiddenSSID = false;
if (!TextUtils.isEmpty(value)) {
try {
config.hiddenSSID = Integer.parseInt(value) != 0;
} catch (NumberFormatException ignore) {
}
}
value = mWifiNative.getNetworkVariable(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 = mWifiNative.getNetworkVariable(netId,
WifiConfiguration.wepKeyVarNames[i]);
if (!TextUtils.isEmpty(value)) {
config.wepKeys[i] = value;
} else {
config.wepKeys[i] = null;
}
}
value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.pskVarName);
if (!TextUtils.isEmpty(value)) {
config.preSharedKey = value;
} else {
config.preSharedKey = null;
}
value = mWifiNative.getNetworkVariable(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 = mWifiNative.getNetworkVariable(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 = mWifiNative.getNetworkVariable(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 = mWifiNative.getNetworkVariable(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 = mWifiNative.getNetworkVariable(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);
}
}
}
if (config.enterpriseConfig == null) {
config.enterpriseConfig = new WifiEnterpriseConfig();
}
HashMap<String, String> enterpriseFields = config.enterpriseConfig.getFields();
for (String key : ENTERPRISE_CONFIG_SUPPLICANT_KEYS) {
value = mWifiNative.getNetworkVariable(netId, key);
if (!TextUtils.isEmpty(value)) {
if (!enterpriseConfigKeyShouldBeQuoted(key)) {
value = removeDoubleQuotes(value);
}
enterpriseFields.put(key, value);
} else {
enterpriseFields.put(key, EMPTY_VALUE);
}
}
if (migrateOldEapTlsNative(config.enterpriseConfig, netId)) {
saveConfig();
}
migrateCerts(config.enterpriseConfig);
// initializeSoftwareKeystoreFlag(config.enterpriseConfig, mKeyStore);
}
private static String removeDoubleQuotes(String string) {
int length = string.length();
if ((length > 1) && (string.charAt(0) == '"')
&& (string.charAt(length - 1) == '"')) {
return string.substring(1, length - 1);
}
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 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
loge("Failed to look-up a string: " + string);
return -1;
}
/* return the allowed key management based on a scan result */
public WifiConfiguration wifiConfigurationFromScanResult(ScanResult result) {
WifiConfiguration config = new WifiConfiguration();
config.SSID = "\"" + result.SSID + "\"";
if (VDBG) {
loge("WifiConfiguration from scan results " +
config.SSID + " cap " + result.capabilities);
}
if (result.capabilities.contains("WEP")) {
config.allowedKeyManagement.set(KeyMgmt.NONE);
config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN); //?
config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED);
}
if (result.capabilities.contains("PSK")) {
config.allowedKeyManagement.set(KeyMgmt.WPA_PSK);
}
if (result.capabilities.contains("EAP")) {
//this is probably wrong, as we don't have a way to enter the enterprise config
config.allowedKeyManagement.set(KeyMgmt.WPA_EAP);
config.allowedKeyManagement.set(KeyMgmt.IEEE8021X);
}
config.scanResultCache = new HashMap<String, ScanResult>();
if (config.scanResultCache == null)
return null;
config.scanResultCache.put(result.BSSID, result);
return config;
}
/* Returns a unique for a given configuration */
private static int configKey(WifiConfiguration config) {
String key = config.configKey();
return key.hashCode();
}
void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("WifiConfigStore");
pw.println("mLastPriority " + mLastPriority);
pw.println("Configured networks");
for (WifiConfiguration conf : getConfiguredNetworks()) {
pw.println(conf);
}
pw.println();
if (mLocalLog != null) {
pw.println("WifiConfigStore - Log Begin ----");
mLocalLog.dump(fd, pw, args);
pw.println("WifiConfigStore - Log End ----");
}
}
public String getConfigFile() {
return ipConfigFile;
}
protected void loge(String s) {
loge(s, false);
}
protected void loge(String s, boolean stack) {
if (stack) {
Log.e(TAG, s + " stack:" + Thread.currentThread().getStackTrace()[2].getMethodName()
+ " - " + Thread.currentThread().getStackTrace()[3].getMethodName()
+ " - " + Thread.currentThread().getStackTrace()[4].getMethodName()
+ " - " + Thread.currentThread().getStackTrace()[5].getMethodName());
} else {
Log.e(TAG, s);
}
}
protected void log(String s) {
Log.d(TAG, s);
}
private void localLog(String s) {
if (mLocalLog != null) {
mLocalLog.log(s);
}
}
private void localLog(String s, int netId) {
if (mLocalLog == null) {
return;
}
WifiConfiguration config;
synchronized(mConfiguredNetworks) {
config = mConfiguredNetworks.get(netId);
}
if (config != null) {
mLocalLog.log(s + " " + config.getPrintableSsid());
} else {
mLocalLog.log(s + " " + netId);
}
}
// Certificate and private key management for EnterpriseConfig
static boolean needsKeyStore(WifiEnterpriseConfig config) {
// Has no keys to be installed
if (config.getClientCertificate() == null && config.getCaCertificate() == null)
return false;
return true;
}
static boolean isHardwareBackedKey(PrivateKey key) {
return KeyChain.isBoundKeyAlgorithm(key.getAlgorithm());
}
static boolean hasHardwareBackedKey(Certificate certificate) {
return KeyChain.isBoundKeyAlgorithm(certificate.getPublicKey().getAlgorithm());
}
static boolean needsSoftwareBackedKeyStore(WifiEnterpriseConfig config) {
String client = config.getClientCertificateAlias();
if (!TextUtils.isEmpty(client)) {
// a valid client certificate is configured
// BUGBUG: keyStore.get() never returns certBytes; because it is not
// taking WIFI_UID as a parameter. It always looks for certificate
// with SYSTEM_UID, and never finds any Wifi certificates. Assuming that
// all certificates need software keystore until we get the get() API
// fixed.
return true;
}
/*
try {
if (DBG) Slog.d(TAG, "Loading client certificate " + Credentials
.USER_CERTIFICATE + client);
CertificateFactory factory = CertificateFactory.getInstance("X.509");
if (factory == null) {
Slog.e(TAG, "Error getting certificate factory");
return;
}
byte[] certBytes = keyStore.get(Credentials.USER_CERTIFICATE + client);
if (certBytes != null) {
Certificate cert = (X509Certificate) factory.generateCertificate(
new ByteArrayInputStream(certBytes));
if (cert != null) {
mNeedsSoftwareKeystore = hasHardwareBackedKey(cert);
if (DBG) Slog.d(TAG, "Loaded client certificate " + Credentials
.USER_CERTIFICATE + client);
if (DBG) Slog.d(TAG, "It " + (mNeedsSoftwareKeystore ? "needs" :
"does not need" ) + " software key store");
} else {
Slog.d(TAG, "could not generate certificate");
}
} else {
Slog.e(TAG, "Could not load client certificate " + Credentials
.USER_CERTIFICATE + client);
mNeedsSoftwareKeystore = true;
}
} catch(CertificateException e) {
Slog.e(TAG, "Could not read certificates");
mCaCert = null;
mClientCertificate = null;
}
*/
return false;
}
/** called when CS ask WiFistateMachine to disconnect the current network
* because the score is bad.
*/
void handleBadNetworkDisconnectReport(int netId, WifiInfo info) {
/* TODO verify the bad network is current */
WifiConfiguration config = mConfiguredNetworks.get(netId);
if (config != null) {
if ((info.getRssi() < WifiConfiguration.UNWANTED_BLACKLIST_SOFT_RSSI_24
&& info.is24GHz()) || (info.getRssi() <
WifiConfiguration.UNWANTED_BLACKLIST_SOFT_RSSI_5 && info.is5GHz())) {
//we got disconnected and RSSI was bad, so disable light
config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_TEMPORARY_DISABLED
+ WifiConfiguration.UNWANTED_BLACKLIST_SOFT_BUMP);
loge("handleBadNetworkDisconnectReport (+4) "
+ Integer.toString(netId) + " " + info);
} else {
//we got disabled but RSSI is good, so disable hard
config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_TEMPORARY_DISABLED
+ WifiConfiguration.UNWANTED_BLACKLIST_HARD_BUMP);
loge("handleBadNetworkDisconnectReport (+8) "
+ Integer.toString(netId) + " " + info);
}
}
}
boolean handleBSSIDBlackList(int netId, String BSSID, boolean enable) {
boolean found = false;
if (BSSID == null)
return found;
// Look for the BSSID in our config store
for (WifiConfiguration config : mConfiguredNetworks.values()) {
if (config.scanResultCache != null) {
for (ScanResult result: config.scanResultCache.values()) {
if (result.BSSID.equals(BSSID)) {
if (enable) {
result.setAutoJoinStatus(ScanResult.ENABLED);
} else {
// Black list the BSSID we were trying to join
// so as the Roam state machine
// doesn't pick it up over and over
result.setAutoJoinStatus(ScanResult.AUTO_ROAM_DISABLED);
found = true;
}
}
}
}
}
return found;
}
int getMaxDhcpRetries() {
return Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.WIFI_MAX_DHCP_RETRY_COUNT,
DEFAULT_MAX_DHCP_RETRIES);
}
void handleSSIDStateChange(int netId, boolean enabled, String message) {
WifiConfiguration config = mConfiguredNetworks.get(netId);
if (config != null) {
if (enabled) {
loge("SSID re-enabled for " + config.configKey() +
" had autoJoinStatus=" + Integer.toString(config.autoJoinStatus)
+ " self added " + config.selfAdded + " ephemeral " + config.ephemeral);
//TODO: http://b/16381983 Fix Wifi Network Blacklisting
//TODO: really I don't know if re-enabling is right but we
//TODO: should err on the side of trying to connect
//TODO: even if the attempt will fail
if (config.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE) {
config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED);
}
} else {
loge("SSID temp disabled for " + config.configKey() +
" had autoJoinStatus=" + Integer.toString(config.autoJoinStatus)
+ " self added " + config.selfAdded + " ephemeral " + config.ephemeral);
if (message != null) {
loge(" message=" + message);
}
if (config.selfAdded && config.lastConnected == 0) {
// This is a network we self added, and we never succeeded,
// the user did not create this network and never entered its credentials,
// so we want to be very aggressive in disabling it completely.
disableNetwork(config.networkId, WifiConfiguration.DISABLED_AUTH_FAILURE);
config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE);
config.disableReason = WifiConfiguration.DISABLED_AUTH_FAILURE;
} else {
if (message != null) {
if (message.contains("WRONG_KEY")
|| message.contains("AUTH_FAILED")) {
// This configuration has received an auth failure, so disable it
// temporarily because we don't want auto-join to try it out.
// this network may be re-enabled by the "usual"
// enableAllNetwork function
//TODO: resolve interpretation of WRONG_KEY and AUTH_FAILURE:
//TODO: if we could count on the wrong_ley or auth_failure
//TODO: message to be correct
//TODO: then we could just mark the configuration as
//TODO: DISABLED_ON_AUTH_FAILURE
//TODO: and the configuration will stay there until
//TODO: user enter new credentials
//TODO: It is not the case however, so instead of disabling, let's
//TODO: start blacklisting hard
//TODO: http://b/16381983 Fix Wifi Network Blacklisting
if (config.autoJoinStatus <=
WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE) {
// 4 auth failure will reach 128 and disable permanently
// autoJoinStatus: 0 -> 4 -> 20 -> 84 -> 128
config.setAutoJoinStatus(4 + config.autoJoinStatus * 4);
if (config.autoJoinStatus >
WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE)
config.setAutoJoinStatus
(WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE);
}
if (DBG) {
loge("blacklisted " + config.configKey() + " to "
+ Integer.toString(config.autoJoinStatus));
}
} else if (message.contains("DHCP FAILURE")) {
config.numConnectionFailures++;
config.lastConnectionFailure = System.currentTimeMillis();
int maxRetries = getMaxDhcpRetries();
// maxRetries == 0 means keep trying forever
if (maxRetries > 0 && config.numConnectionFailures > maxRetries) {
/**
* If we've exceeded the maximum number of retries for DHCP
* to a given network, disable the network
*/
config.setAutoJoinStatus
(WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE);
disableNetwork(netId, WifiConfiguration.DISABLED_DHCP_FAILURE);
}
if (DBG) {
loge("blacklisted " + config.configKey() + " to "
+ config.autoJoinStatus
+ " due to DHCP failure, count="
+ config.numConnectionFailures);
}
}
message.replace("\n", "");
message.replace("\r", "");
config.lastFailure = message;
}
}
}
}
}
boolean installKeys(WifiEnterpriseConfig config, String name) {
boolean ret = true;
String privKeyName = Credentials.USER_PRIVATE_KEY + name;
String userCertName = Credentials.USER_CERTIFICATE + name;
String caCertName = Credentials.CA_CERTIFICATE + name;
if (config.getClientCertificate() != null) {
byte[] privKeyData = config.getClientPrivateKey().getEncoded();
if (isHardwareBackedKey(config.getClientPrivateKey())) {
// Hardware backed key store is secure enough to store keys un-encrypted, this
// removes the need for user to punch a PIN to get access to these keys
if (DBG) Log.d(TAG, "importing keys " + name + " in hardware backed store");
ret = mKeyStore.importKey(privKeyName, privKeyData, android.os.Process.WIFI_UID,
KeyStore.FLAG_NONE);
} else {
// Software backed key store is NOT secure enough to store keys un-encrypted.
// Save keys encrypted so they are protected with user's PIN. User will
// have to unlock phone before being able to use these keys and connect to
// networks.
if (DBG) Log.d(TAG, "importing keys " + name + " in software backed store");
ret = mKeyStore.importKey(privKeyName, privKeyData, Process.WIFI_UID,
KeyStore.FLAG_ENCRYPTED);
}
if (ret == false) {
return ret;
}
ret = putCertInKeyStore(userCertName, config.getClientCertificate());
if (ret == false) {
// Remove private key installed
mKeyStore.delKey(privKeyName, Process.WIFI_UID);
return ret;
}
}
if (config.getCaCertificate() != null) {
ret = putCertInKeyStore(caCertName, config.getCaCertificate());
if (ret == false) {
if (config.getClientCertificate() != null) {
// Remove client key+cert
mKeyStore.delKey(privKeyName, Process.WIFI_UID);
mKeyStore.delete(userCertName, Process.WIFI_UID);
}
return ret;
}
}
// Set alias names
if (config.getClientCertificate() != null) {
config.setClientCertificateAlias(name);
config.resetClientKeyEntry();
}
if (config.getCaCertificate() != null) {
config.setCaCertificateAlias(name);
config.resetCaCertificate();
}
return ret;
}
private boolean putCertInKeyStore(String name, Certificate cert) {
try {
byte[] certData = Credentials.convertToPem(cert);
if (DBG) Log.d(TAG, "putting certificate " + name + " in keystore");
return mKeyStore.put(name, certData, Process.WIFI_UID, KeyStore.FLAG_NONE);
} catch (IOException e1) {
return false;
} catch (CertificateException e2) {
return false;
}
}
void removeKeys(WifiEnterpriseConfig config) {
String client = config.getClientCertificateAlias();
// a valid client certificate is configured
if (!TextUtils.isEmpty(client)) {
if (DBG) Log.d(TAG, "removing client private key and user cert");
mKeyStore.delKey(Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID);
mKeyStore.delete(Credentials.USER_CERTIFICATE + client, Process.WIFI_UID);
}
String ca = config.getCaCertificateAlias();
// a valid ca certificate is configured
if (!TextUtils.isEmpty(ca)) {
if (DBG) Log.d(TAG, "removing CA cert");
mKeyStore.delete(Credentials.CA_CERTIFICATE + ca, Process.WIFI_UID);
}
}
/** Migrates the old style TLS config to the new config style. This should only be used
* when restoring an old wpa_supplicant.conf or upgrading from a previous
* platform version.
* @return true if the config was updated
* @hide
*/
boolean migrateOldEapTlsNative(WifiEnterpriseConfig config, int netId) {
String oldPrivateKey = mWifiNative.getNetworkVariable(netId, OLD_PRIVATE_KEY_NAME);
/*
* If the old configuration value is not present, then there is nothing
* to do.
*/
if (TextUtils.isEmpty(oldPrivateKey)) {
return false;
} else {
// Also ignore it if it's empty quotes.
oldPrivateKey = removeDoubleQuotes(oldPrivateKey);
if (TextUtils.isEmpty(oldPrivateKey)) {
return false;
}
}
config.setFieldValue(WifiEnterpriseConfig.ENGINE_KEY, WifiEnterpriseConfig.ENGINE_ENABLE);
config.setFieldValue(WifiEnterpriseConfig.ENGINE_ID_KEY,
WifiEnterpriseConfig.ENGINE_ID_KEYSTORE);
/*
* The old key started with the keystore:// URI prefix, but we don't
* need that anymore. Trim it off if it exists.
*/
final String keyName;
if (oldPrivateKey.startsWith(WifiEnterpriseConfig.KEYSTORE_URI)) {
keyName = new String(
oldPrivateKey.substring(WifiEnterpriseConfig.KEYSTORE_URI.length()));
} else {
keyName = oldPrivateKey;
}
config.setFieldValue(WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY, keyName);
mWifiNative.setNetworkVariable(netId, WifiEnterpriseConfig.ENGINE_KEY,
config.getFieldValue(WifiEnterpriseConfig.ENGINE_KEY, ""));
mWifiNative.setNetworkVariable(netId, WifiEnterpriseConfig.ENGINE_ID_KEY,
config.getFieldValue(WifiEnterpriseConfig.ENGINE_ID_KEY, ""));
mWifiNative.setNetworkVariable(netId, WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY,
config.getFieldValue(WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY, ""));
// Remove old private_key string so we don't run this again.
mWifiNative.setNetworkVariable(netId, OLD_PRIVATE_KEY_NAME, EMPTY_VALUE);
return true;
}
/** Migrate certs from global pool to wifi UID if not already done */
void migrateCerts(WifiEnterpriseConfig config) {
String client = config.getClientCertificateAlias();
// a valid client certificate is configured
if (!TextUtils.isEmpty(client)) {
if (!mKeyStore.contains(Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID)) {
mKeyStore.duplicate(Credentials.USER_PRIVATE_KEY + client, -1,
Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID);
mKeyStore.duplicate(Credentials.USER_CERTIFICATE + client, -1,
Credentials.USER_CERTIFICATE + client, Process.WIFI_UID);
}
}
String ca = config.getCaCertificateAlias();
// a valid ca certificate is configured
if (!TextUtils.isEmpty(ca)) {
if (!mKeyStore.contains(Credentials.CA_CERTIFICATE + ca, Process.WIFI_UID)) {
mKeyStore.duplicate(Credentials.CA_CERTIFICATE + ca, -1,
Credentials.CA_CERTIFICATE + ca, Process.WIFI_UID);
}
}
}
}