diff options
author | Glen Kuhne <kuh@google.com> | 2016-02-22 17:21:08 -0800 |
---|---|---|
committer | Glen Kuhne <kuh@google.com> | 2016-02-24 11:26:34 -0800 |
commit | c485ebf64d1049d17db8108b85653f53fcdc8949 (patch) | |
tree | d2395f212f970bfc8f5f7ebfc8b55e8af907537a /service | |
parent | af8f9541d248f7e1ca8232774de1310838ef4b0a (diff) |
WifiConfigStore Refactor read&write NetworkHistory
Created a new helper class: WifiNetworkHistory
Moved readNetworkHistory() and writeKnownNetworkHistory() from
WifiConfigStore into this class, replacing them with calls to the
helper.
BUG=27294533
Change-Id: I10d43e3fd37e251d1845ef726fea0faf136d9626
Diffstat (limited to 'service')
-rw-r--r-- | service/java/com/android/server/wifi/WifiConfigManager.java | 490 | ||||
-rw-r--r-- | service/java/com/android/server/wifi/WifiNetworkHistory.java | 619 |
2 files changed, 645 insertions, 464 deletions
diff --git a/service/java/com/android/server/wifi/WifiConfigManager.java b/service/java/com/android/server/wifi/WifiConfigManager.java index 957456d9a..6bef3bd5b 100644 --- a/service/java/com/android/server/wifi/WifiConfigManager.java +++ b/service/java/com/android/server/wifi/WifiConfigManager.java @@ -79,14 +79,10 @@ import com.android.server.wifi.hotspot2.pps.HomeSP; import org.xml.sax.SAXException; -import java.io.BufferedInputStream; import java.io.BufferedReader; -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; @@ -210,60 +206,9 @@ public class WifiConfigManager { 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"; - static final String CONFIG_KEY = "CONFIG"; - private static final String CONFIG_BSSID_KEY = "CONFIG_BSSID"; - private static final String CHOICE_KEY = "CHOICE"; - private static final String CHOICE_TIME_KEY = "CHOICE_TIME"; - 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 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"; - 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 FQDN_KEY = "FQDN"; - 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 VALIDATED_INTERNET_ACCESS_KEY = "VALIDATED_INTERNET_ACCESS"; - private static final String NO_INTERNET_ACCESS_REPORTS_KEY = "NO_INTERNET_ACCESS_REPORTS"; - private static final String EPHEMERAL_KEY = "EPHEMERAL"; - private static final String NUM_ASSOCIATION_KEY = "NUM_ASSOCIATION"; - private static final String DELETED_CRC32_KEY = "DELETED_CRC32"; - private static final String DELETED_EPHEMERAL_KEY = "DELETED_EPHEMERAL"; - private static final String CREATOR_NAME_KEY = "CREATOR_NAME"; - private static final String UPDATE_NAME_KEY = "UPDATE_NAME"; - private static final String USER_APPROVED_KEY = "USER_APPROVED"; - private static final String CREATION_TIME_KEY = "CREATION_TIME"; - private static final String UPDATE_TIME_KEY = "UPDATE_TIME"; - static final String SHARED_KEY = "SHARED"; - private static final String NETWORK_SELECTION_STATUS_KEY = "NETWORK_SELECTION_STATUS"; - private static final String NETWORK_SELECTION_DISABLE_REASON_KEY = - "NETWORK_SELECTION_DISABLE_REASON"; - - private static final String SEPARATOR = ": "; - private static final String NL = "\n"; - private static final String THRESHOLD_GOOD_RSSI_5_KEY = "THRESHOLD_GOOD_RSSI_5"; private static final String THRESHOLD_LOW_RSSI_5_KEY @@ -506,6 +451,7 @@ public class WifiConfigManager { private IpConfigStore mIpconfigStore; private DelayedDiskWrite mWriter; + private final WifiNetworkHistory mWifiNetworkHistory; /** * The lastSelectedConfiguration is used to remember which network * was selected last by the user. @@ -737,6 +683,8 @@ public class WifiConfigManager { mSIMAccessor = new SIMAccessor(mContext); mWriter = new DelayedDiskWrite(); mIpconfigStore = new IpConfigStore(mWriter); + + mWifiNetworkHistory = new WifiNetworkHistory(c, mLocalLog, mWriter); } public void trimANQPCache(boolean all) { @@ -1103,8 +1051,10 @@ public class WifiConfigManager { mWifiNative.saveConfig(); updateLastConnectUid(config, uid); + writeKnownNetworkHistory(); + /* Enable the given network while disabling all other networks */ selectNetworkWithoutBroadcast(config.networkId); @@ -1294,7 +1244,6 @@ public class WifiConfigManager { } writeKnownNetworkHistory(); - return foundConfig; } @@ -1705,6 +1654,7 @@ public class WifiConfigManager { if (!config.ephemeral) { removeUserSelectionPreference(key); } + writeKnownNetworkHistory(); } return true; @@ -1800,6 +1750,7 @@ public class WifiConfigManager { ret = selectNetworkWithoutBroadcast(netId); if (VDBG) localLogNetwork("enableNetwork(disableOthers=true, uid=" + uid + ") ", netId); updateLastConnectUid(getWifiConfiguration(netId), uid); + writeKnownNetworkHistory(); sendConfiguredNetworksChangedBroadcast(); } else { @@ -2355,7 +2306,7 @@ public class WifiConfigManager { logKernelTime(); logContents(SUPPLICANT_CONFIG_FILE); logContents(SUPPLICANT_CONFIG_FILE_BACKUP); - logContents(networkHistoryConfigFile); + logContents(mWifiNetworkHistory.NETWORK_HISTORY_CONFIG_FILE); } } @@ -2554,189 +2505,28 @@ public class WifiConfigManager { }, false); } - public void writeKnownNetworkHistory() { + /** + * Write network history, WifiConfigurations and mScanDetailCaches to file. + */ + private void readNetworkHistory(Map<String, WifiConfiguration> configs) { + mWifiNetworkHistory.readNetworkHistory(configs, + mScanDetailCaches, + mDeletedSSIDs, + mDeletedEphemeralSSIDs); + } - /* Make a copy */ + /** + * Read Network history from file, merge it into mConfiguredNetowrks and mScanDetailCaches + */ + public void writeKnownNetworkHistory() { final List<WifiConfiguration> networks = new ArrayList<WifiConfiguration>(); for (WifiConfiguration config : mConfiguredNetworks.valuesForAllUsers()) { networks.add(new WifiConfiguration(config)); } - if (VDBG) { - loge(" writeKnownNetworkHistory() num networks:" - + mConfiguredNetworks.valuesForCurrentUser()); - } - - 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"); - */ - WifiConfiguration.NetworkSelectionStatus status = - config.getNetworkSelectionStatus(); - if (VDBG) { - int numlink = 0; - if (config.linkedConfigurations != null) { - numlink = config.linkedConfigurations.size(); - } - String disableTime; - if (config.getNetworkSelectionStatus().isNetworkEnabled()) { - disableTime = ""; - } else { - disableTime = "Disable time: " + DateFormat.getInstance().format( - config.getNetworkSelectionStatus().getDisableTime()); - } - loge("saving network history: " + config.configKey() + " gw: " - + config.defaultGwMacAddress + " Network Selection-status: " - + status.getNetworkStatusString() - + disableTime + " ephemeral=" + config.ephemeral - + " choice:" + status.getConnectChoice() - + " link:" + numlink - + " status:" + config.status - + " nid:" + config.networkId); - } - - if (!WifiServiceImpl.isValid(config)) - continue; - - if (config.SSID == null) { - if (VDBG) { - loge("writeKnownNetworkHistory trying to write config with null SSID"); - } - continue; - } - if (VDBG) { - loge("writeKnownNetworkHistory write config " + config.configKey()); - } - out.writeUTF(CONFIG_KEY + SEPARATOR + config.configKey() + NL); - - if (config.SSID != null) { - out.writeUTF(SSID_KEY + SEPARATOR + config.SSID + NL); - } - if (config.BSSID != null) { - out.writeUTF(CONFIG_BSSID_KEY + SEPARATOR + config.BSSID + NL); - } else { - out.writeUTF(CONFIG_BSSID_KEY + SEPARATOR + "null" + NL); - } - if (config.FQDN != null) { - out.writeUTF(FQDN_KEY + SEPARATOR + config.FQDN + NL); - } - - out.writeUTF(PRIORITY_KEY + SEPARATOR + - Integer.toString(config.priority) + NL); - out.writeUTF(NETWORK_ID_KEY + SEPARATOR + - Integer.toString(config.networkId) + NL); - out.writeUTF(SELF_ADDED_KEY + SEPARATOR + - Boolean.toString(config.selfAdded) + NL); - out.writeUTF(DID_SELF_ADD_KEY + SEPARATOR + - Boolean.toString(config.didSelfAdd) + NL); - out.writeUTF(NO_INTERNET_ACCESS_REPORTS_KEY + SEPARATOR + - Integer.toString(config.numNoInternetAccessReports) + NL); - out.writeUTF(VALIDATED_INTERNET_ACCESS_KEY + SEPARATOR + - Boolean.toString(config.validatedInternetAccess) + NL); - out.writeUTF(EPHEMERAL_KEY + SEPARATOR + - Boolean.toString(config.ephemeral) + NL); - if (config.creationTime != null) { - out.writeUTF(CREATION_TIME_KEY + SEPARATOR + config.creationTime + NL); - } - if (config.updateTime != null) { - out.writeUTF(UPDATE_TIME_KEY + SEPARATOR + config.updateTime + NL); - } - if (config.peerWifiConfiguration != null) { - out.writeUTF(PEER_CONFIGURATION_KEY + SEPARATOR + - config.peerWifiConfiguration + NL); - } - out.writeUTF(SCORER_OVERRIDE_KEY + SEPARATOR + - Integer.toString(config.numScorerOverride) + NL); - out.writeUTF(SCORER_OVERRIDE_AND_SWITCH_KEY + SEPARATOR + - Integer.toString(config.numScorerOverrideAndSwitchedNetwork) + NL); - out.writeUTF(NUM_ASSOCIATION_KEY + SEPARATOR + - Integer.toString(config.numAssociation) + NL); - out.writeUTF(CREATOR_UID_KEY + SEPARATOR + - Integer.toString(config.creatorUid) + NL); - out.writeUTF(CONNECT_UID_KEY + SEPARATOR + - Integer.toString(config.lastConnectUid) + NL); - out.writeUTF(UPDATE_UID_KEY + SEPARATOR + - Integer.toString(config.lastUpdateUid) + NL); - out.writeUTF(CREATOR_NAME_KEY + SEPARATOR + - config.creatorName + NL); - out.writeUTF(UPDATE_NAME_KEY + SEPARATOR + - config.lastUpdateName + NL); - out.writeUTF(USER_APPROVED_KEY + SEPARATOR + - Integer.toString(config.userApproved) + NL); - out.writeUTF(SHARED_KEY + SEPARATOR + Boolean.toString(config.shared) + NL); - String allowedKeyManagementString = - makeString(config.allowedKeyManagement, - WifiConfiguration.KeyMgmt.strings); - out.writeUTF(AUTH_KEY + SEPARATOR + - allowedKeyManagementString + NL); - out.writeUTF(NETWORK_SELECTION_STATUS_KEY + SEPARATOR - + status.getNetworkSelectionStatus() + NL); - out.writeUTF(NETWORK_SELECTION_DISABLE_REASON_KEY + SEPARATOR - + status.getNetworkSelectionDisableReason() + NL); - - if (status.getConnectChoice() != null) { - out.writeUTF(CHOICE_KEY + SEPARATOR + status.getConnectChoice() + NL); - out.writeUTF(CHOICE_TIME_KEY + SEPARATOR - + status.getConnectChoiceTimestamp() + NL); - } - - if (config.linkedConfigurations != null) { - log("writeKnownNetworkHistory write linked " - + config.linkedConfigurations.size()); - - for (String key : config.linkedConfigurations.keySet()) { - out.writeUTF(LINK_KEY + SEPARATOR + key + NL); - } - } - - String macAddress = config.defaultGwMacAddress; - if (macAddress != null) { - out.writeUTF(DEFAULT_GW_KEY + SEPARATOR + macAddress + NL); - } - - if (getScanDetailCache(config) != null) { - for (ScanDetail scanDetail : getScanDetailCache(config).values()) { - ScanResult result = scanDetail.getScanResult(); - out.writeUTF(BSSID_KEY + SEPARATOR + - result.BSSID + NL); - - out.writeUTF(FREQ_KEY + SEPARATOR + - Integer.toString(result.frequency) + NL); - - out.writeUTF(RSSI_KEY + SEPARATOR + - Integer.toString(result.level) + NL); - - out.writeUTF(BSSID_KEY_END + NL); - } - } - if (config.lastFailure != null) { - out.writeUTF(FAILURE_KEY + SEPARATOR + config.lastFailure + NL); - } - out.writeUTF(NL); - // Add extra blank lines for clarity - out.writeUTF(NL); - out.writeUTF(NL); - } - if (mDeletedSSIDs != null && mDeletedSSIDs.size() > 0) { - for (Long i : mDeletedSSIDs) { - out.writeUTF(DELETED_CRC32_KEY); - out.writeUTF(String.valueOf(i)); - out.writeUTF(NL); - } - } - if (mDeletedEphemeralSSIDs != null && mDeletedEphemeralSSIDs.size() > 0) { - for (String ssid : mDeletedEphemeralSSIDs) { - out.writeUTF(DELETED_EPHEMERAL_KEY); - out.writeUTF(ssid); - out.writeUTF(NL); - } - } - } - }); + mWifiNetworkHistory.writeKnownNetworkHistory(networks, + mScanDetailCaches, + mDeletedSSIDs, + mDeletedEphemeralSSIDs); } public void setAndEnableLastSelectedConfiguration(int netId) { @@ -2784,235 +2574,6 @@ public class WifiConfigManager { && lastSelectedConfiguration.equals(config.configKey())); } - /** - * Adds information stored in networkHistory.txt to the given configs. The configs are provided - * as a mapping from configKey to WifiConfiguration, because the WifiConfigurations themselves - * do not contain sufficient information to compute their configKeys until after the information - * that is stored in networkHistory.txt has been added to them. - * - * @param configs mapping from configKey to a WifiConfiguration that contains the information - * information read from wpa_supplicant.conf - */ - private void readNetworkHistory(Map<String, WifiConfiguration> configs) { - if (showNetworks) { - localLog("readNetworkHistory() path:" + networkHistoryConfigFile); - } - - try (DataInputStream in = - new DataInputStream(new BufferedInputStream( - new FileInputStream(networkHistoryConfigFile)))) { - - String bssid = null; - String ssid = null; - - int freq = 0; - int status = 0; - long seen = 0; - int rssi = WifiConfiguration.INVALID_RSSI; - String caps = null; - - WifiConfiguration config = null; - while (true) { - String line = in.readUTF(); - if (line == null) { - break; - } - int colon = line.indexOf(':'); - if (colon < 0) { - continue; - } - - String key = line.substring(0, colon).trim(); - String value = line.substring(colon + 1).trim(); - - if (key.equals(CONFIG_KEY)) { - config = configs.get(value); - - // skip reading that configuration data - // since we don't have a corresponding network ID - if (config == null) { - localLog("readNetworkHistory didnt find netid for hash=" - + Integer.toString(value.hashCode()) - + " key: " + value); - mLostConfigsDbg.add(value); - continue; - } else { - // After an upgrade count old connections as owned by system - if (config.creatorName == null || config.lastUpdateName == null) { - config.creatorName = - mContext.getPackageManager().getNameForUid(Process.SYSTEM_UID); - config.lastUpdateName = config.creatorName; - - if (DBG) Log.w(TAG, "Upgrading network " + config.networkId - + " to " + config.creatorName); - } - } - } else if (config != null) { - WifiConfiguration.NetworkSelectionStatus networkStatus = - config.getNetworkSelectionStatus(); - switch (key) { - case SSID_KEY: - if (config.isPasspoint()) { - break; - } - ssid = value; - 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; - } - break; - case CONFIG_BSSID_KEY: - config.BSSID = value.equals("null") ? null : value; - break; - case FQDN_KEY: - // Check for literal 'null' to be backwards compatible. - config.FQDN = value.equals("null") ? null : value; - break; - case DEFAULT_GW_KEY: - config.defaultGwMacAddress = value; - break; - case SELF_ADDED_KEY: - config.selfAdded = Boolean.parseBoolean(value); - break; - case DID_SELF_ADD_KEY: - config.didSelfAdd = Boolean.parseBoolean(value); - break; - case NO_INTERNET_ACCESS_REPORTS_KEY: - config.numNoInternetAccessReports = Integer.parseInt(value); - break; - case VALIDATED_INTERNET_ACCESS_KEY: - config.validatedInternetAccess = Boolean.parseBoolean(value); - break; - case CREATION_TIME_KEY: - config.creationTime = value; - break; - case UPDATE_TIME_KEY: - config.updateTime = value; - break; - case EPHEMERAL_KEY: - config.ephemeral = Boolean.parseBoolean(value); - break; - case CREATOR_UID_KEY: - config.creatorUid = Integer.parseInt(value); - break; - case BLACKLIST_MILLI_KEY: - networkStatus.setDisableTime(Long.parseLong(value)); - break; - case SCORER_OVERRIDE_KEY: - config.numScorerOverride = Integer.parseInt(value); - break; - case SCORER_OVERRIDE_AND_SWITCH_KEY: - config.numScorerOverrideAndSwitchedNetwork = Integer.parseInt(value); - break; - case NUM_ASSOCIATION_KEY: - config.numAssociation = Integer.parseInt(value); - break; - case CONNECT_UID_KEY: - config.lastConnectUid = Integer.parseInt(value); - break; - case UPDATE_UID_KEY: - config.lastUpdateUid = Integer.parseInt(value); - break; - case FAILURE_KEY: - config.lastFailure = value; - break; - case PEER_CONFIGURATION_KEY: - config.peerWifiConfiguration = value; - break; - case NETWORK_SELECTION_STATUS_KEY: - networkStatus.setNetworkSelectionStatus(Integer.parseInt(value)); - break; - case NETWORK_SELECTION_DISABLE_REASON_KEY: - networkStatus.setNetworkSelectionDisableReason(Integer.parseInt(value)); - break; - case CHOICE_KEY: - networkStatus.setConnectChoice(value); - break; - case CHOICE_TIME_KEY: - networkStatus.setConnectChoiceTimestamp(Long.parseLong(value)); - break; - case LINK_KEY: - if (config.linkedConfigurations == null) { - config.linkedConfigurations = new HashMap<>(); - } - else { - config.linkedConfigurations.put(value, -1); - } - break; - case BSSID_KEY: - status = 0; - ssid = null; - bssid = null; - freq = 0; - seen = 0; - rssi = WifiConfiguration.INVALID_RSSI; - caps = ""; - break; - case RSSI_KEY: - rssi = Integer.parseInt(value); - break; - case FREQ_KEY: - freq = Integer.parseInt(value); - break; - case 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); - */ - break; - case BSSID_KEY_END: - if ((bssid != null) && (ssid != null)) { - - if (getScanDetailCache(config) != null) { - WifiSsid wssid = WifiSsid.createFromAsciiEncoded(ssid); - ScanDetail scanDetail = new ScanDetail(wssid, bssid, - caps, rssi, freq, (long) 0, seen); - getScanDetailCache(config).put(scanDetail); - scanDetail.getScanResult().autoJoinStatus = status; - } - } - break; - case DELETED_CRC32_KEY: - mDeletedSSIDs.add(Long.parseLong(value)); - break; - case DELETED_EPHEMERAL_KEY: - if (!TextUtils.isEmpty(value)) { - mDeletedEphemeralSSIDs.add(value); - } - break; - case CREATOR_NAME_KEY: - config.creatorName = value; - break; - case UPDATE_NAME_KEY: - config.lastUpdateName = value; - break; - case USER_APPROVED_KEY: - config.userApproved = Integer.parseInt(value); - break; - case SHARED_KEY: - config.shared = Boolean.parseBoolean(value); - break; - } - } - } - } catch (NumberFormatException e) { - Log.e(TAG, "readNetworkHistory: failed to read, revert to default, " + e, e); - } catch (EOFException e) { - // do nothing - } catch (IOException e) { - Log.e(TAG, "readNetworkHistory: No config file, revert to default, " + e, e); - } - } - private void readAutoJoinConfig() { try (BufferedReader reader = new BufferedReader(new FileReader(autoJoinConfigFile))) { for (String key = reader.readLine(); key != null; key = reader.readLine()) { @@ -4085,6 +3646,7 @@ public class WifiConfigManager { mWifiNative.removeNetwork(config.networkId); } mWifiNative.saveConfig(); + writeKnownNetworkHistory(); } diff --git a/service/java/com/android/server/wifi/WifiNetworkHistory.java b/service/java/com/android/server/wifi/WifiNetworkHistory.java new file mode 100644 index 000000000..5edb02848 --- /dev/null +++ b/service/java/com/android/server/wifi/WifiNetworkHistory.java @@ -0,0 +1,619 @@ +/* + * Copyright (C) 2016 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.net.wifi.ScanResult; + +import android.net.wifi.WifiConfiguration; +import android.net.wifi.WifiSsid; +import android.os.Environment; +import android.os.Process; +import android.text.TextUtils; + +import android.util.LocalLog; +import android.util.Log; + +import com.android.server.net.DelayedDiskWrite; + +import java.io.BufferedInputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.EOFException; +import java.io.FileInputStream; +import java.io.IOException; +import java.text.DateFormat; +import java.util.BitSet; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Provides an API to read and write the network history from WifiConfigurations to file + * This is largely separate and extra to the supplicant config file. + */ +public class WifiNetworkHistory { + public static final String TAG = "WifiNetworkHistory"; + private static final boolean DBG = true; + private static final boolean VDBG = true; + static final String NETWORK_HISTORY_CONFIG_FILE = Environment.getDataDirectory() + + "/misc/wifi/networkHistory.txt"; + /* Network History Keys */ + private static final String SSID_KEY = "SSID"; + static final String CONFIG_KEY = "CONFIG"; + private static final String CONFIG_BSSID_KEY = "CONFIG_BSSID"; + private static final String CHOICE_KEY = "CHOICE"; + private static final String CHOICE_TIME_KEY = "CHOICE_TIME"; + 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 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"; + 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 FQDN_KEY = "FQDN"; + 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 VALIDATED_INTERNET_ACCESS_KEY = "VALIDATED_INTERNET_ACCESS"; + private static final String NO_INTERNET_ACCESS_REPORTS_KEY = "NO_INTERNET_ACCESS_REPORTS"; + private static final String EPHEMERAL_KEY = "EPHEMERAL"; + private static final String NUM_ASSOCIATION_KEY = "NUM_ASSOCIATION"; + private static final String DELETED_CRC32_KEY = "DELETED_CRC32"; + private static final String DELETED_EPHEMERAL_KEY = "DELETED_EPHEMERAL"; + private static final String CREATOR_NAME_KEY = "CREATOR_NAME"; + private static final String UPDATE_NAME_KEY = "UPDATE_NAME"; + private static final String USER_APPROVED_KEY = "USER_APPROVED"; + private static final String CREATION_TIME_KEY = "CREATION_TIME"; + private static final String UPDATE_TIME_KEY = "UPDATE_TIME"; + static final String SHARED_KEY = "SHARED"; + private static final String NETWORK_SELECTION_STATUS_KEY = "NETWORK_SELECTION_STATUS"; + private static final String NETWORK_SELECTION_DISABLE_REASON_KEY = + "NETWORK_SELECTION_DISABLE_REASON"; + + private static final String SEPARATOR = ": "; + private static final String NL = "\n"; + + protected final DelayedDiskWrite mWriter; + Context mContext; + private final LocalLog mLocalLog; + /* + * Lost config list, whenever we read a config from networkHistory.txt that was not in + * wpa_supplicant.conf + */ + HashSet<String> mLostConfigsDbg = new HashSet<String>(); + + public WifiNetworkHistory(Context c, LocalLog localLog, DelayedDiskWrite writer) { + mContext = c; + mWriter = writer; + mLocalLog = localLog; + } + + /** + * Write network history to file, for configured networks + * + * @param networks List of ConfiguredNetworks to write to NetworkHistory + */ + public void writeKnownNetworkHistory(final List<WifiConfiguration> networks, + final HashMap<Integer, ScanDetailCache> scanDetailCaches, + final Set<Long> deletedSSIDs, + final Set<String> deletedEphemeralSSIDs) { + + /* Make a copy */ + //final List<WifiConfiguration> networks = new ArrayList<WifiConfiguration>(); + + //for (WifiConfiguration config : mConfiguredNetworks.valuesForAllUsers()) { + // networks.add(new WifiConfiguration(config)); + //} + + mWriter.write(NETWORK_HISTORY_CONFIG_FILE, 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"); + */ + WifiConfiguration.NetworkSelectionStatus status = + config.getNetworkSelectionStatus(); + if (VDBG) { + int numlink = 0; + if (config.linkedConfigurations != null) { + numlink = config.linkedConfigurations.size(); + } + String disableTime; + if (config.getNetworkSelectionStatus().isNetworkEnabled()) { + disableTime = ""; + } else { + disableTime = "Disable time: " + DateFormat.getInstance().format( + config.getNetworkSelectionStatus().getDisableTime()); + } + loge("saving network history: " + config.configKey() + " gw: " + + config.defaultGwMacAddress + " Network Selection-status: " + + status.getNetworkStatusString() + + disableTime + " ephemeral=" + config.ephemeral + + " choice:" + status.getConnectChoice() + + " link:" + numlink + + " status:" + config.status + + " nid:" + config.networkId); + } + + if (!isValid(config)) { + continue; + } + + if (config.SSID == null) { + if (VDBG) { + loge("writeKnownNetworkHistory trying to write config with null SSID"); + } + continue; + } + if (VDBG) { + loge("writeKnownNetworkHistory write config " + config.configKey()); + } + out.writeUTF(CONFIG_KEY + SEPARATOR + config.configKey() + NL); + + if (config.SSID != null) { + out.writeUTF(SSID_KEY + SEPARATOR + config.SSID + NL); + } + if (config.BSSID != null) { + out.writeUTF(CONFIG_BSSID_KEY + SEPARATOR + config.BSSID + NL); + } else { + out.writeUTF(CONFIG_BSSID_KEY + SEPARATOR + "null" + NL); + } + if (config.FQDN != null) { + out.writeUTF(FQDN_KEY + SEPARATOR + config.FQDN + NL); + } + + out.writeUTF(PRIORITY_KEY + SEPARATOR + Integer.toString(config.priority) + NL); + out.writeUTF(NETWORK_ID_KEY + SEPARATOR + + Integer.toString(config.networkId) + NL); + out.writeUTF(SELF_ADDED_KEY + SEPARATOR + + Boolean.toString(config.selfAdded) + NL); + out.writeUTF(DID_SELF_ADD_KEY + SEPARATOR + + Boolean.toString(config.didSelfAdd) + NL); + out.writeUTF(NO_INTERNET_ACCESS_REPORTS_KEY + SEPARATOR + + Integer.toString(config.numNoInternetAccessReports) + NL); + out.writeUTF(VALIDATED_INTERNET_ACCESS_KEY + SEPARATOR + + Boolean.toString(config.validatedInternetAccess) + NL); + out.writeUTF(EPHEMERAL_KEY + SEPARATOR + + Boolean.toString(config.ephemeral) + NL); + if (config.creationTime != null) { + out.writeUTF(CREATION_TIME_KEY + SEPARATOR + config.creationTime + NL); + } + if (config.updateTime != null) { + out.writeUTF(UPDATE_TIME_KEY + SEPARATOR + config.updateTime + NL); + } + if (config.peerWifiConfiguration != null) { + out.writeUTF(PEER_CONFIGURATION_KEY + SEPARATOR + + config.peerWifiConfiguration + NL); + } + out.writeUTF(SCORER_OVERRIDE_KEY + SEPARATOR + + Integer.toString(config.numScorerOverride) + NL); + out.writeUTF(SCORER_OVERRIDE_AND_SWITCH_KEY + SEPARATOR + + Integer.toString(config.numScorerOverrideAndSwitchedNetwork) + NL); + out.writeUTF(NUM_ASSOCIATION_KEY + SEPARATOR + + Integer.toString(config.numAssociation) + NL); + out.writeUTF(CREATOR_UID_KEY + SEPARATOR + + Integer.toString(config.creatorUid) + NL); + out.writeUTF(CONNECT_UID_KEY + SEPARATOR + + Integer.toString(config.lastConnectUid) + NL); + out.writeUTF(UPDATE_UID_KEY + SEPARATOR + + Integer.toString(config.lastUpdateUid) + NL); + out.writeUTF(CREATOR_NAME_KEY + SEPARATOR + + config.creatorName + NL); + out.writeUTF(UPDATE_NAME_KEY + SEPARATOR + + config.lastUpdateName + NL); + out.writeUTF(USER_APPROVED_KEY + SEPARATOR + + Integer.toString(config.userApproved) + NL); + out.writeUTF(SHARED_KEY + SEPARATOR + Boolean.toString(config.shared) + NL); + String allowedKeyManagementString = + makeString(config.allowedKeyManagement, + WifiConfiguration.KeyMgmt.strings); + out.writeUTF(AUTH_KEY + SEPARATOR + + allowedKeyManagementString + NL); + out.writeUTF(NETWORK_SELECTION_STATUS_KEY + SEPARATOR + + status.getNetworkSelectionStatus() + NL); + out.writeUTF(NETWORK_SELECTION_DISABLE_REASON_KEY + SEPARATOR + + status.getNetworkSelectionDisableReason() + NL); + + if (status.getConnectChoice() != null) { + out.writeUTF(CHOICE_KEY + SEPARATOR + status.getConnectChoice() + NL); + out.writeUTF(CHOICE_TIME_KEY + SEPARATOR + + status.getConnectChoiceTimestamp() + NL); + } + + if (config.linkedConfigurations != null) { + log("writeKnownNetworkHistory write linked " + + config.linkedConfigurations.size()); + + for (String key : config.linkedConfigurations.keySet()) { + out.writeUTF(LINK_KEY + SEPARATOR + key + NL); + } + } + + String macAddress = config.defaultGwMacAddress; + if (macAddress != null) { + out.writeUTF(DEFAULT_GW_KEY + SEPARATOR + macAddress + NL); + } + + if (getScanDetailCache(config, scanDetailCaches) != null) { + for (ScanDetail scanDetail : getScanDetailCache(config, + scanDetailCaches).values()) { + ScanResult result = scanDetail.getScanResult(); + out.writeUTF(BSSID_KEY + SEPARATOR + + result.BSSID + NL); + out.writeUTF(FREQ_KEY + SEPARATOR + + Integer.toString(result.frequency) + NL); + + out.writeUTF(RSSI_KEY + SEPARATOR + + Integer.toString(result.level) + NL); + + out.writeUTF(BSSID_KEY_END + NL); + } + } + if (config.lastFailure != null) { + out.writeUTF(FAILURE_KEY + SEPARATOR + config.lastFailure + NL); + } + out.writeUTF(NL); + // Add extra blank lines for clarity + out.writeUTF(NL); + out.writeUTF(NL); + } + if (deletedSSIDs != null && deletedSSIDs.size() > 0) { + for (Long i : deletedSSIDs) { + out.writeUTF(DELETED_CRC32_KEY); + out.writeUTF(String.valueOf(i)); + out.writeUTF(NL); + } + } + if (deletedEphemeralSSIDs != null && deletedEphemeralSSIDs.size() > 0) { + for (String ssid : deletedEphemeralSSIDs) { + out.writeUTF(DELETED_EPHEMERAL_KEY); + out.writeUTF(ssid); + out.writeUTF(NL); + } + } + } + }); + } + + /** + * Adds information stored in networkHistory.txt to the given configs. The configs are provided + * as a mapping from configKey to WifiConfiguration, because the WifiConfigurations themselves + * do not contain sufficient information to compute their configKeys until after the information + * that is stored in networkHistory.txt has been added to them. + * + * @param configs mapping from configKey to a WifiConfiguration that contains the information + * information read from wpa_supplicant.conf + */ + public void readNetworkHistory(Map<String, WifiConfiguration> configs, + HashMap<Integer, ScanDetailCache> scanDetailCaches, + Set<Long> deletedSSIDs, + Set<String> deletedEphemeralSSIDs) { + localLog("readNetworkHistory() path:" + NETWORK_HISTORY_CONFIG_FILE); + + try (DataInputStream in = + new DataInputStream(new BufferedInputStream( + new FileInputStream(NETWORK_HISTORY_CONFIG_FILE)))) { + + String bssid = null; + String ssid = null; + + int freq = 0; + int status = 0; + long seen = 0; + int rssi = WifiConfiguration.INVALID_RSSI; + String caps = null; + + WifiConfiguration config = null; + while (true) { + String line = in.readUTF(); + if (line == null) { + break; + } + int colon = line.indexOf(':'); + if (colon < 0) { + continue; + } + + String key = line.substring(0, colon).trim(); + String value = line.substring(colon + 1).trim(); + + if (key.equals(CONFIG_KEY)) { + config = configs.get(value); + + // skip reading that configuration data + // since we don't have a corresponding network ID + if (config == null) { + localLog("readNetworkHistory didnt find netid for hash=" + + Integer.toString(value.hashCode()) + + " key: " + value); + mLostConfigsDbg.add(value); + continue; + } else { + // After an upgrade count old connections as owned by system + if (config.creatorName == null || config.lastUpdateName == null) { + config.creatorName = + mContext.getPackageManager().getNameForUid(Process.SYSTEM_UID); + config.lastUpdateName = config.creatorName; + + if (DBG) { + Log.w(TAG, "Upgrading network " + config.networkId + + " to " + config.creatorName); + } + } + } + } else if (config != null) { + WifiConfiguration.NetworkSelectionStatus networkStatus = + config.getNetworkSelectionStatus(); + switch (key) { + case SSID_KEY: + if (config.isPasspoint()) { + break; + } + ssid = value; + 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; + } + break; + case CONFIG_BSSID_KEY: + config.BSSID = value.equals("null") ? null : value; + break; + case FQDN_KEY: + // Check for literal 'null' to be backwards compatible. + config.FQDN = value.equals("null") ? null : value; + break; + case DEFAULT_GW_KEY: + config.defaultGwMacAddress = value; + break; + case SELF_ADDED_KEY: + config.selfAdded = Boolean.parseBoolean(value); + break; + case DID_SELF_ADD_KEY: + config.didSelfAdd = Boolean.parseBoolean(value); + break; + case NO_INTERNET_ACCESS_REPORTS_KEY: + config.numNoInternetAccessReports = Integer.parseInt(value); + break; + case VALIDATED_INTERNET_ACCESS_KEY: + config.validatedInternetAccess = Boolean.parseBoolean(value); + break; + case CREATION_TIME_KEY: + config.creationTime = value; + break; + case UPDATE_TIME_KEY: + config.updateTime = value; + break; + case EPHEMERAL_KEY: + config.ephemeral = Boolean.parseBoolean(value); + break; + case CREATOR_UID_KEY: + config.creatorUid = Integer.parseInt(value); + break; + case BLACKLIST_MILLI_KEY: + networkStatus.setDisableTime(Long.parseLong(value)); + break; + case SCORER_OVERRIDE_KEY: + config.numScorerOverride = Integer.parseInt(value); + break; + case SCORER_OVERRIDE_AND_SWITCH_KEY: + config.numScorerOverrideAndSwitchedNetwork = Integer.parseInt(value); + break; + case NUM_ASSOCIATION_KEY: + config.numAssociation = Integer.parseInt(value); + break; + case CONNECT_UID_KEY: + config.lastConnectUid = Integer.parseInt(value); + break; + case UPDATE_UID_KEY: + config.lastUpdateUid = Integer.parseInt(value); + break; + case FAILURE_KEY: + config.lastFailure = value; + break; + case PEER_CONFIGURATION_KEY: + config.peerWifiConfiguration = value; + break; + case NETWORK_SELECTION_STATUS_KEY: + networkStatus.setNetworkSelectionStatus(Integer.parseInt(value)); + break; + case NETWORK_SELECTION_DISABLE_REASON_KEY: + networkStatus.setNetworkSelectionDisableReason(Integer.parseInt(value)); + break; + case CHOICE_KEY: + networkStatus.setConnectChoice(value); + break; + case CHOICE_TIME_KEY: + networkStatus.setConnectChoiceTimestamp(Long.parseLong(value)); + break; + case LINK_KEY: + if (config.linkedConfigurations == null) { + config.linkedConfigurations = new HashMap<>(); + } else { + config.linkedConfigurations.put(value, -1); + } + break; + case BSSID_KEY: + status = 0; + ssid = null; + bssid = null; + freq = 0; + seen = 0; + rssi = WifiConfiguration.INVALID_RSSI; + caps = ""; + break; + case RSSI_KEY: + rssi = Integer.parseInt(value); + break; + case FREQ_KEY: + freq = Integer.parseInt(value); + break; + case 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); + */ + break; + case BSSID_KEY_END: + if ((bssid != null) && (ssid != null)) { + if (getScanDetailCache(config, scanDetailCaches) != null) { + WifiSsid wssid = WifiSsid.createFromAsciiEncoded(ssid); + ScanDetail scanDetail = new ScanDetail(wssid, bssid, + caps, rssi, freq, (long) 0, seen); + getScanDetailCache(config, scanDetailCaches).put(scanDetail); + scanDetail.getScanResult().autoJoinStatus = status; + } + } + break; + case DELETED_CRC32_KEY: + deletedSSIDs.add(Long.parseLong(value)); + break; + case DELETED_EPHEMERAL_KEY: + if (!TextUtils.isEmpty(value)) { + deletedEphemeralSSIDs.add(value); + } + break; + case CREATOR_NAME_KEY: + config.creatorName = value; + break; + case UPDATE_NAME_KEY: + config.lastUpdateName = value; + break; + case USER_APPROVED_KEY: + config.userApproved = Integer.parseInt(value); + break; + case SHARED_KEY: + config.shared = Boolean.parseBoolean(value); + break; + } + } + } + } catch (NumberFormatException e) { + Log.e(TAG, "readNetworkHistory: failed to read, revert to default, " + e, e); + } catch (EOFException e) { + // do nothing + } catch (IOException e) { + Log.e(TAG, "readNetworkHistory: No config file, revert to default, " + e, e); + } + } + + /** + * Ported this out of WifiServiceImpl, I have no idea what it's doing + * <TODO> figure out what/why this is doing + * <TODO> Port it into WifiConfiguration, then remove all the silly business from ServiceImpl + */ + public boolean isValid(WifiConfiguration config) { + if (config.allowedKeyManagement == null) { + return false; + } + if (config.allowedKeyManagement.cardinality() > 1) { + if (config.allowedKeyManagement.cardinality() != 2) { + return false; + } + if (!config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_EAP)) { + return false; + } + if ((!config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.IEEE8021X)) + && (!config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK))) { + return false; + } + } + return true; + } + + 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(); + } + + protected void log(String s) { + Log.d(TAG, s); + } + 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); + } + } + + private void localLog(String s) { + if (mLocalLog != null) { + mLocalLog.log(s); + } + } + + private ScanDetailCache getScanDetailCache(WifiConfiguration config, + HashMap<Integer, ScanDetailCache> scanDetailCaches) { + if (config == null || scanDetailCaches == null) return null; + ScanDetailCache cache = scanDetailCaches.get(config.networkId); + if (cache == null && config.networkId != WifiConfiguration.INVALID_NETWORK_ID) { + cache = new ScanDetailCache(config); + scanDetailCaches.put(config.networkId, cache); + } + return cache; + } +} |