目录
1.思路
wifi config在手机中是以文件形式保存的,那开机的时候手机必然要解析文件成对应的config对象,梳理下流程
2.流程梳理
ClientModeImpl
case CMD_BOOT_COMPLETED:
// get other services that we need to manage
getAdditionalWifiServiceInterfaces();
new MemoryStoreImpl(mContext, mWifiInjector, mWifiScoreCard).start();
if (!mWifiConfigManager.loadFromStore()) {
Log.e(TAG, "Failed to load from config store");
}
registerNetworkFactory();
break;
WifiConfigManager
/**
* Read the config store and load the in-memory lists from the store data retrieved and sends
* out the networks changed broadcast.
*
* This reads all the network configurations from:
* 1. Shared WifiConfigStore.xml
* 2. User WifiConfigStore.xml
*
* @return true on success or not needed (fresh install), false otherwise.
*/
public boolean loadFromStore() {
// If the user unlock comes in before we load from store, which means the user store have
// not been setup yet for the current user. Setup the user store before the read so that
// configurations for the current user will also being loaded.
if (mDeferredUserUnlockRead) {
Log.i(TAG, "Handling user unlock before loading from store.");
List<WifiConfigStore.StoreFile> userStoreFiles =
WifiConfigStore.createUserFiles(mCurrentUserId);
if (userStoreFiles == null) {
Log.wtf(TAG, "Failed to create user store files");
return false;
}
mWifiConfigStore.setUserStores(userStoreFiles);
mDeferredUserUnlockRead = false;
}
try {
mWifiConfigStore.read();
} catch (IOException e) {
Log.wtf(TAG, "Reading from new store failed. All saved networks are lost!", e);
return false;
} catch (XmlPullParserException e) {
Log.wtf(TAG, "XML deserialization of store failed. All saved networks are lost!", e);
return false;
}
loadInternalData(mNetworkListSharedStoreData.getConfigurations(),
mNetworkListUserStoreData.getConfigurations(),
mDeletedEphemeralSsidsStoreData.getSsidToTimeMap(),
mRandomizedMacStoreData.getMacMapping());
return true;
}
WifiConfigStore
/**
* API to read the store data from the config stores.
* The method reads the user specific configurations from user specific config store and the
* shared configurations from the shared config store.
*/
public void read() throws XmlPullParserException, IOException {
// Reset both share and user store data.
resetStoreData(mSharedStore);
if (mUserStores != null) {
for (StoreFile userStoreFile : mUserStores) {
resetStoreData(userStoreFile);
}
}
long readStartTime = mClock.getElapsedSinceBootMillis();
byte[] sharedDataBytes = mSharedStore.readRawData();
deserializeData(sharedDataBytes, mSharedStore);
if (mUserStores != null) {
for (StoreFile userStoreFile : mUserStores) {
byte[] userDataBytes = userStoreFile.readRawData();
deserializeData(userDataBytes, userStoreFile);
}
}
long readTime = mClock.getElapsedSinceBootMillis() - readStartTime;
try {
mWifiMetrics.noteWifiConfigStoreReadDuration(toIntExact(readTime));
} catch (ArithmeticException e) {
// Silently ignore on any overflow errors.
}
Log.d(TAG, "Reading from all stores completed in " + readTime + " ms.");
}
顺便看下WifiConfigStore在WifiInjector的初始化
mWifiConfigStore = new WifiConfigStore(
mContext, clientModeImplLooper, mClock, mWifiMetrics,
WifiConfigStore.createSharedFile());
/**
* Create a new instance of the shared store file.
*
* @return new instance of the store file or null if the directory cannot be created.
*/
public static @Nullable StoreFile createSharedFile() {
return createFile(Environment.getDataMiscDirectory(), STORE_FILE_SHARED_GENERAL);
}
/**
* Config store file for general shared store file.
*/
public static final int STORE_FILE_SHARED_GENERAL = 0;
/**
* Helper method to create a store file instance for either the shared store or user store.
* Note: The method creates the store directory if not already present. This may be needed for
* user store files.
*
* @param storeBaseDir Base directory under which the store file is to be stored. The store file
* will be at <storeBaseDir>/wifi/WifiConfigStore.xml.
* @param fileId Identifier for the file. See {@link StoreFileId}.
* @return new instance of the store file or null if the directory cannot be created.
*/
private static @Nullable StoreFile createFile(File storeBaseDir, @StoreFileId int fileId) {
File storeDir = new File(storeBaseDir, STORE_DIRECTORY_NAME);
if (!storeDir.exists()) {
if (!storeDir.mkdir()) {
Log.w(TAG, "Could not create store directory " + storeDir);
return null;
}
}
return new StoreFile(new File(storeDir, STORE_ID_TO_FILE_NAME.get(fileId)), fileId);
}
/**
* Directory to store the config store files in.
*/
private static final String STORE_DIRECTORY_NAME = "wifi";
/**
* Config store file name for general shared store file.
*/
private static final String STORE_FILE_NAME_SHARED_GENERAL = "WifiConfigStore.xml";
文件路径保存为data/misc/wifi/WifiConfigStore.xml
数据解析
/**
* Deserialize data from a {@link StoreFile} for all {@link StoreData} instances registered.
*
* @param dataBytes The data to parse
* @param storeFile StoreFile that we read from. Will be used to retrieve the list of clients
* who have data to deserialize from this file.
*
* @throws XmlPullParserException
* @throws IOException
*/
private void deserializeData(@NonNull byte[] dataBytes, @NonNull StoreFile storeFile)
throws XmlPullParserException, IOException {
List<StoreData> storeDataList = retrieveStoreDataListForStoreFile(storeFile);
if (dataBytes == null) {
indicateNoDataForStoreDatas(storeDataList);
return;
}
final XmlPullParser in = Xml.newPullParser();
final ByteArrayInputStream inputStream = new ByteArrayInputStream(dataBytes);
in.setInput(inputStream, StandardCharsets.UTF_8.name());
// Start parsing the XML stream.
int rootTagDepth = in.getDepth() + 1;
parseDocumentStartAndVersionFromXml(in);
String[] headerName = new String[1];
Set<StoreData> storeDatasInvoked = new HashSet<>();
while (XmlUtil.gotoNextSectionOrEnd(in, headerName, rootTagDepth)) {
// There can only be 1 store data matching the tag (O indicates a fatal
// error).
StoreData storeData = storeDataList.stream()
.filter(s -> s.getName().equals(headerName[0]))
.findAny()
.orElse(null);
if (storeData == null) {
throw new XmlPullParserException("Unknown store data: " + headerName[0]
+ ". List of store data: " + storeDataList);
}
storeData.deserializeData(in, rootTagDepth + 1);
storeDatasInvoked.add(storeData);
}
// Inform all the other registered store data clients that there is nothing in the store
// for them.
Set<StoreData> storeDatasNotInvoked = new HashSet<>(storeDataList);
storeDatasNotInvoked.removeAll(storeDatasInvoked);
indicateNoDataForStoreDatas(storeDatasNotInvoked);
}
这边流程丢了一点,对StoreData的stream用法有点看不懂。。。
先默认StoreData的实现类是NetworkListStoreData
看下解析方法
@Override
public void deserializeData(XmlPullParser in, int outerTagDepth)
throws XmlPullParserException, IOException {
// Ignore empty reads.
if (in == null) {
return;
}
mConfigurations = parseNetworkList(in, outerTagDepth);
}
/**
* Parse a list of {@link WifiConfiguration} from an input stream in XML format.
*
* @param in The input stream to read from
* @param outerTagDepth The XML tag depth of the outer XML block
* @return List of {@link WifiConfiguration}
* @throws XmlPullParserException
* @throws IOException
*/
private List<WifiConfiguration> parseNetworkList(XmlPullParser in, int outerTagDepth)
throws XmlPullParserException, IOException {
List<WifiConfiguration> networkList = new ArrayList<>();
while (XmlUtil.gotoNextSectionWithNameOrEnd(in, XML_TAG_SECTION_HEADER_NETWORK,
outerTagDepth)) {
// Try/catch only runtime exceptions (like illegal args), any XML/IO exceptions are
// fatal and should abort the entire loading process.
try {
WifiConfiguration config = parseNetwork(in, outerTagDepth + 1);
networkList.add(config);
} catch (RuntimeException e) {
// Failed to parse this network, skip it.
Log.e(TAG, "Failed to parse network config. Skipping...", e);
}
}
return networkList;
}
之后就是xml解析出config了
/**
* Parse a {@link WifiConfiguration} from an input stream in XML format.
*
* @param in The input stream to read from
* @param outerTagDepth The XML tag depth of the outer XML block
* @return {@link WifiConfiguration}
* @throws XmlPullParserException
* @throws IOException
*/
private WifiConfiguration parseNetwork(XmlPullParser in, int outerTagDepth)
throws XmlPullParserException, IOException {
Pair<String, WifiConfiguration> parsedConfig = null;
NetworkSelectionStatus status = null;
IpConfiguration ipConfiguration = null;
WifiEnterpriseConfig enterpriseConfig = null;
String[] headerName = new String[1];
while (XmlUtil.gotoNextSectionOrEnd(in, headerName, outerTagDepth)) {
switch (headerName[0]) {
case XML_TAG_SECTION_HEADER_WIFI_CONFIGURATION:
if (parsedConfig != null) {
throw new XmlPullParserException("Detected duplicate tag for: "
+ XML_TAG_SECTION_HEADER_WIFI_CONFIGURATION);
}
parsedConfig = WifiConfigurationXmlUtil.parseFromXml(in, outerTagDepth + 1);
break;
case XML_TAG_SECTION_HEADER_NETWORK_STATUS:
if (status != null) {
throw new XmlPullParserException("Detected duplicate tag for: "
+ XML_TAG_SECTION_HEADER_NETWORK_STATUS);
}
status = NetworkSelectionStatusXmlUtil.parseFromXml(in, outerTagDepth + 1);
break;
case XML_TAG_SECTION_HEADER_IP_CONFIGURATION:
if (ipConfiguration != null) {
throw new XmlPullParserException("Detected duplicate tag for: "
+ XML_TAG_SECTION_HEADER_IP_CONFIGURATION);
}
ipConfiguration = IpConfigurationXmlUtil.parseFromXml(in, outerTagDepth + 1);
break;
case XML_TAG_SECTION_HEADER_WIFI_ENTERPRISE_CONFIGURATION:
if (enterpriseConfig != null) {
throw new XmlPullParserException("Detected duplicate tag for: "
+ XML_TAG_SECTION_HEADER_WIFI_ENTERPRISE_CONFIGURATION);
}
enterpriseConfig =
WifiEnterpriseConfigXmlUtil.parseFromXml(in, outerTagDepth + 1);
break;
default:
throw new XmlPullParserException("Unknown tag under "
+ XML_TAG_SECTION_HEADER_NETWORK + ": " + headerName[0]);
}
}
if (parsedConfig == null || parsedConfig.first == null || parsedConfig.second == null) {
throw new XmlPullParserException("XML parsing of wifi configuration failed");
}
String configKeyParsed = parsedConfig.first;
WifiConfiguration configuration = parsedConfig.second;
String configKeyCalculated = configuration.configKey();
if (!configKeyParsed.equals(configKeyCalculated)) {
throw new XmlPullParserException(
"Configuration key does not match. Retrieved: " + configKeyParsed
+ ", Calculated: " + configKeyCalculated);
}
// Set creatorUid/creatorName for networks which don't have it set to valid value.
String creatorName = mContext.getPackageManager().getNameForUid(configuration.creatorUid);
if (creatorName == null) {
Log.e(TAG, "Invalid creatorUid for saved network " + configuration.configKey()
+ ", creatorUid=" + configuration.creatorUid);
configuration.creatorUid = Process.SYSTEM_UID;
configuration.creatorName =
mContext.getPackageManager().getNameForUid(Process.SYSTEM_UID);
} else if (!creatorName.equals(configuration.creatorName)) {
Log.w(TAG, "Invalid creatorName for saved network " + configuration.configKey()
+ ", creatorUid=" + configuration.creatorUid
+ ", creatorName=" + configuration.creatorName);
configuration.creatorName = creatorName;
}
configuration.setNetworkSelectionStatus(status);
configuration.setIpConfiguration(ipConfiguration);
if (enterpriseConfig != null) {
configuration.enterpriseConfig = enterpriseConfig;
}
return configuration;
}
主要看下WifiConfigurationXmlUtil怎么解析的
这个类是./service/java/com/android/server/wifi/util/XmlUtil.java:309: public static class WifiConfigurationXmlUtil {
XmlUtil的静态内部类
/**
* Utility class to serialize and deserialize {@link WifiConfiguration} object to XML &
* vice versa.
* This is used by both {@link com.android.server.wifi.WifiConfigStore} &
* {@link com.android.server.wifi.WifiBackupRestore} modules.
* The |writeConfigurationToXml| has 2 versions, one for backup and one for config store.
* There is only 1 version of |parseXmlToConfiguration| for both backup & config store.
* The parse method is written so that any element added/deleted in future revisions can
* be easily handled.
*/
public static class WifiConfigurationXmlUtil {
...
/**
* Parses the configuration data elements from the provided XML stream to a
* WifiConfiguration object.
* Note: This is used for parsing both backup data and config store data. Looping through
* the tags make it easy to add or remove elements in the future versions if needed.
*
* @param in XmlPullParser instance pointing to the XML stream.
* @param outerTagDepth depth of the outer tag in the XML document.
* @return Pair<Config key, WifiConfiguration object> if parsing is successful,
* null otherwise.
*/
public static Pair<String, WifiConfiguration> parseFromXml(
XmlPullParser in, int outerTagDepth)
throws XmlPullParserException, IOException {
WifiConfiguration configuration = new WifiConfiguration();
String configKeyInData = null;
boolean macRandomizationSettingExists = false;
// Loop through and parse out all the elements from the stream within this section.
while (!XmlUtil.isNextSectionEnd(in, outerTagDepth)) {
String[] valueName = new String[1];
Object value = XmlUtil.readCurrentValue(in, valueName);
if (valueName[0] == null) {
throw new XmlPullParserException("Missing value name");
}
switch (valueName[0]) {
case XML_TAG_CONFIG_KEY:
configKeyInData = (String) value;
break;
case XML_TAG_SSID:
configuration.SSID = (String) value;
break;
case XML_TAG_BSSID:
configuration.BSSID = (String) value;
break;
case XML_TAG_PRE_SHARED_KEY:
configuration.preSharedKey = (String) value;
break;
case XML_TAG_WEP_KEYS:
populateWepKeysFromXmlValue(value, configuration.wepKeys);
break;
case XML_TAG_WEP_TX_KEY_INDEX:
configuration.wepTxKeyIndex = (int) value;
break;
case XML_TAG_HIDDEN_SSID:
configuration.hiddenSSID = (boolean) value;
break;
case XML_TAG_REQUIRE_PMF:
configuration.requirePMF = (boolean) value;
break;
case XML_TAG_ALLOWED_KEY_MGMT:
byte[] allowedKeyMgmt = (byte[]) value;
configuration.allowedKeyManagement = BitSet.valueOf(allowedKeyMgmt);
break;
case XML_TAG_ALLOWED_PROTOCOLS:
byte[] allowedProtocols = (byte[]) value;
configuration.allowedProtocols = BitSet.valueOf(allowedProtocols);
break;
case XML_TAG_ALLOWED_AUTH_ALGOS:
byte[] allowedAuthAlgorithms = (byte[]) value;
configuration.allowedAuthAlgorithms = BitSet.valueOf(allowedAuthAlgorithms);
break;
case XML_TAG_ALLOWED_GROUP_CIPHERS:
byte[] allowedGroupCiphers = (byte[]) value;
configuration.allowedGroupCiphers = BitSet.valueOf(allowedGroupCiphers);
break;
case XML_TAG_ALLOWED_PAIRWISE_CIPHERS:
byte[] allowedPairwiseCiphers = (byte[]) value;
configuration.allowedPairwiseCiphers =
BitSet.valueOf(allowedPairwiseCiphers);
break;
case XML_TAG_ALLOWED_GROUP_MGMT_CIPHERS:
byte[] allowedGroupMgmtCiphers = (byte[]) value;
configuration.allowedGroupManagementCiphers =
BitSet.valueOf(allowedGroupMgmtCiphers);
break;
case XML_TAG_ALLOWED_SUITE_B_CIPHERS:
byte[] allowedSuiteBCiphers = (byte[]) value;
configuration.allowedSuiteBCiphers =
BitSet.valueOf(allowedSuiteBCiphers);
break;
case XML_TAG_SHARED:
configuration.shared = (boolean) value;
break;
case XML_TAG_STATUS:
int status = (int) value;
// Any network which was CURRENT before reboot needs
// to be restored to ENABLED.
if (status == WifiConfiguration.Status.CURRENT) {
status = WifiConfiguration.Status.ENABLED;
}
configuration.status = status;
break;
case XML_TAG_FQDN:
configuration.FQDN = (String) value;
break;
case XML_TAG_PROVIDER_FRIENDLY_NAME:
configuration.providerFriendlyName = (String) value;
break;
case XML_TAG_LINKED_NETWORKS_LIST:
configuration.linkedConfigurations = (HashMap<String, Integer>) value;
break;
case XML_TAG_DEFAULT_GW_MAC_ADDRESS:
configuration.defaultGwMacAddress = (String) value;
break;
case XML_TAG_VALIDATED_INTERNET_ACCESS:
configuration.validatedInternetAccess = (boolean) value;
break;
case XML_TAG_NO_INTERNET_ACCESS_EXPECTED:
configuration.noInternetAccessExpected = (boolean) value;
break;
case XML_TAG_USER_APPROVED:
configuration.userApproved = (int) value;
break;
case XML_TAG_METERED_HINT:
configuration.meteredHint = (boolean) value;
break;
case XML_TAG_METERED_OVERRIDE:
configuration.meteredOverride = (int) value;
break;
case XML_TAG_USE_EXTERNAL_SCORES:
configuration.useExternalScores = (boolean) value;
break;
case XML_TAG_NUM_ASSOCIATION:
configuration.numAssociation = (int) value;
break;
case XML_TAG_CREATOR_UID:
configuration.creatorUid = (int) value;
break;
case XML_TAG_CREATOR_NAME:
configuration.creatorName = (String) value;
break;
case XML_TAG_CREATION_TIME:
configuration.creationTime = (String) value;
break;
case XML_TAG_LAST_UPDATE_UID:
configuration.lastUpdateUid = (int) value;
break;
case XML_TAG_LAST_UPDATE_NAME:
configuration.lastUpdateName = (String) value;
break;
case XML_TAG_LAST_CONNECT_UID:
configuration.lastConnectUid = (int) value;
break;
case XML_TAG_IS_LEGACY_PASSPOINT_CONFIG:
configuration.isLegacyPasspointConfig = (boolean) value;
break;
case XML_TAG_ROAMING_CONSORTIUM_OIS:
configuration.roamingConsortiumIds = (long[]) value;
break;
case XML_TAG_RANDOMIZED_MAC_ADDRESS:
configuration.setRandomizedMacAddress(
MacAddress.fromString((String) value));
break;
case XML_TAG_MAC_RANDOMIZATION_SETTING:
configuration.macRandomizationSetting = (int) value;
macRandomizationSettingExists = true;
break;
default:
throw new XmlPullParserException(
"Unknown value name found: " + valueName[0]);
}
}
if (!macRandomizationSettingExists) {
configuration.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_NONE;
}
return Pair.create(configKeyInData, configuration);
}
}
对xml中的每个字段进行解析并存储
再回到WifiConfigManager
loadInternalData(mNetworkListSharedStoreData.getConfigurations(),
mNetworkListUserStoreData.getConfigurations(),
mDeletedEphemeralSsidsStoreData.getSsidToTimeMap(),
mRandomizedMacStoreData.getMacMapping());
将解析好的config进行加载
// Config Manager
mWifiConfigManager = new WifiConfigManager(mContext, mClock,
UserManager.get(mContext), makeTelephonyManager(),
mWifiKeyStore, mWifiConfigStore, mWifiPermissionsUtil,
mWifiPermissionsWrapper, this, new NetworkListSharedStoreData(mContext),
new NetworkListUserStoreData(mContext),
new DeletedEphemeralSsidsStoreData(mClock), new RandomizedMacStoreData(),
mFrameworkFacade, mWifiCoreHandlerThread.getLooper());
联系之前的各种storeData是哪里来的
// Register store data for network list and deleted ephemeral SSIDs.
mNetworkListSharedStoreData = networkListSharedStoreData;
mNetworkListUserStoreData = networkListUserStoreData;
mDeletedEphemeralSsidsStoreData = deletedEphemeralSsidsStoreData;
mRandomizedMacStoreData = randomizedMacStoreData;
mWifiConfigStore.registerStoreData(mNetworkListSharedStoreData);
mWifiConfigStore.registerStoreData(mNetworkListUserStoreData);
mWifiConfigStore.registerStoreData(mDeletedEphemeralSsidsStoreData);
mWifiConfigStore.registerStoreData(mRandomizedMacStoreData);
WifiConfigStore
/**
* Register a {@link StoreData} to read/write data from/to a store. A {@link StoreData} is
* responsible for a block of data in the store file, and provides serialization/deserialization
* functions for those data.
*
* @param storeData The store data to be registered to the config store
* @return true if registered successfully, false if the store file name is not valid.
*/
public boolean registerStoreData(@NonNull StoreData storeData) {
if (storeData == null) {
Log.e(TAG, "Unable to register null store data");
return false;
}
int storeFileId = storeData.getStoreFileId();
if (STORE_ID_TO_FILE_NAME.get(storeFileId) == null) {
Log.e(TAG, "Invalid shared store file specified" + storeFileId);
return false;
}
mStoreDataList.add(storeData);
return true;
}
和之前各个storeData的实现类连起来了。
WifiConfigManager进行数据的加载
/**
* Helper function to populate the internal (in-memory) data from the retrieved stores (file)
* data.
* This method:
* 1. Clears all existing internal data.
* 2. Sends out the networks changed broadcast after loading all the data.
*
* @param sharedConfigurations list of network configurations retrieved from shared store.
* @param userConfigurations list of network configurations retrieved from user store.
* @param deletedEphemeralSsidsToTimeMap map of ssid's representing the ephemeral networks
* deleted by the user to the wall clock time at which
* it was deleted.
*/
private void loadInternalData(
List<WifiConfiguration> sharedConfigurations,
List<WifiConfiguration> userConfigurations,
Map<String, Long> deletedEphemeralSsidsToTimeMap,
Map<String, String> macAddressMapping) {
// Clear out all the existing in-memory lists and load the lists from what was retrieved
// from the config store.
clearInternalData();
loadInternalDataFromSharedStore(sharedConfigurations, macAddressMapping);
loadInternalDataFromUserStore(userConfigurations, deletedEphemeralSsidsToTimeMap);
generateRandomizedMacAddresses();
if (mConfiguredNetworks.sizeForAllUsers() == 0) {
Log.w(TAG, "No stored networks found.");
}
// reset identity & anonymous identity for networks using SIM-based authentication
// on load (i.e. boot) so that if the user changed SIMs while the device was powered off,
// we do not reuse stale credentials that would lead to authentication failure.
resetSimNetworks();
sendConfiguredNetworksChangedBroadcast();
mPendingStoreRead = false;
}
将获取的所有config塞到config集合里去
/**
* Helper function to populate the internal (in-memory) data from the retrieved shared store
* (file) data.
*
* @param configurations list of configurations retrieved from store.
*/
private void loadInternalDataFromSharedStore(
List<WifiConfiguration> configurations,
Map<String, String> macAddressMapping) {
for (WifiConfiguration configuration : configurations) {
configuration.networkId = mNextNetworkId++;
if (mVerboseLoggingEnabled) {
Log.v(TAG, "Adding network from shared store " + configuration.configKey());
}
try {
mConfiguredNetworks.put(configuration);
} catch (IllegalArgumentException e) {
Log.e(TAG, "Failed to add network to config map", e);
}
}
mRandomizedMacAddressMapping.putAll(macAddressMapping);
}