基于 Android 13 的代码:http://aospxref.com/android-13.0.0_r3/
从这里我们知道版本的区别:http://aospxref.com/android-13.0.0_r3/xref/frameworks/base/wifi/migration_samples/README.txt
Shared files ============ 1) WifiConfigStore.xml - General storage for shared configurations. Includes user's saved Wi-Fi networks. AOSP Path in Android 10: /data/misc/wifi/WifiConfigStore.xml AOSP Path in Android 11: /data/misc/apexdata/com.android/wifi/WifiConfigStore.xml Sample File (in this folder): Shared_WifiConfigStore.xml 2) WifiConfigStoreSoftAp.xml - Storage for user's softap/tethering configuration. AOSP Path in Android 10: /data/misc/wifi/softap.conf. Note: Was key/value format in Android 10. Conversion to XML done in SoftApConfToXmlMigrationUtil.java. AOSP Path in Android 11: /data/misc/apexdata/com.android/wifi/WifiConfigStore.xml Sample File (in this folder): Shared_WifiConfigStoreSoftAp.xml User specific files ================== 3) WifiConfigStore.xml - General storage for user specific configurations. Includes user's saved passpoint networks, Wi-Fi network request approvals, etc. AOSP Path in Android 10: /data/misc_ce/<userId>/wifi/WifiConfigStore.xml AOSP Path in Android 11: /data/misc_ce/<userId>/apexdata/com.android/wifi/WifiConfigStore.xml Sample File (in this folder): User_WifiConfigStore.xml 4) WifiConfigStoreNetworkSuggestions.xml - Storage for app installed network suggestions. AOSP Path in Android 10: /data/misc_ce/<userId>/wifi/WifiConfigStoreNetworkSuggestions.xml AOSP Path in Android 11: /data/misc_ce/<userId>/apexdata/com.android/wifi/WifiConfigStoreNetworkSuggestions.xml Sample File (in this folder): User_WifiConfigStoreNetworkSuggestions.xml
Android 10 之前一般都在:/data/vendor/wifi/wpa 目录下有份 wpa_supplicant.conf 文件
这里我们记录下这个配置文件如何保存的
//Config store file name for general shared store file.
private static final String STORE_FILE_NAME_SHARED_GENERAL = "WifiConfigStore.xml";
public static final int STORE_FILE_SHARED_GENERAL = 0;
//Mapping of Store file Id to Store file names.
private static final SparseArray<String> STORE_ID_TO_FILE_NAME =
new SparseArray<String>() {{
//这里我们只关心 WifiConfigStore.xml 有关的,key + value的组合
//put(0, WifiConfigStore.xml)
put(STORE_FILE_SHARED_GENERAL, STORE_FILE_NAME_SHARED_GENERAL);
put(STORE_FILE_SHARED_SOFTAP, STORE_FILE_NAME_SHARED_SOFTAP);
put(STORE_FILE_USER_GENERAL, STORE_FILE_NAME_USER_GENERAL);
put(STORE_FILE_USER_NETWORK_SUGGESTIONS, STORE_FILE_NAME_USER_NETWORK_SUGGESTIONS); }};
// 创建的地方
public static @NonNull List<StoreFile> createSharedFiles(boolean shouldEncryptCredentials) {
return createFiles(
Environment.getWifiSharedDirectory(),
Arrays.asList(STORE_FILE_SHARED_GENERAL, STORE_FILE_SHARED_SOFTAP),
UserHandle.ALL,
shouldEncryptCredentials);
}
看下创建是的参数:Environment.getWifiSharedDirectory()
private static final String WIFI_APEX_NAME = "com.android.wifi"; public static File getWifiSharedDirectory() { // ApexEnvironment.getApexEnvironment() 方法,并传入 Wi-Fi APEX 的名称作为参数。 // 然后使用返回的ApexEnvironment对象调用getDeviceProtectedDataDir()方法,以获取 Wi-Fi APEX 模块的设备保护数据目录 return ApexEnvironment.getApexEnvironment(WIFI_APEX_NAME).getDeviceProtectedDataDir(); }
参数差不多就知道了,然后看下是什么地方调用这个 createSharedFiles 方法来创建
344 // New config store 345 mWifiConfigStore = new WifiConfigStore(mContext, wifiHandler, mClock, mWifiMetrics, 346 WifiConfigStore.createSharedFiles(mFrameworkFacade.isNiapModeOn(mContext)));
可以知道是在WifiInjector初始化的时候创建了这个,参数:mFrameworkFacade.isNiapModeOn(mContext) 是判断符不符合 NIAP,可以百度下NIAP是啥:NIAP(National Information Assurance Partnership)模式是一个安全标准和认证程序,旨在确保信息技术产品满足特定的安全性要求和标准。NIAP 是由美国国家安全局(NSA)和国家科学基金会(NSF)共同发起的一个合作伙伴关系计划。NIAP 的目标是确保计算机和通信系统的安全性,以保护敏感信息免受未经授权的访问和恶意活动的威胁。它通过制定一系列安全性要求和测试程序,对信息技术产品进行评估和认证,以验证其符合特定的安全标准。NIAP 模式是指在设备中启用了符合 NIAP 标准的安全配置和措施。它可能涉及硬件和软件层面的安全性要求,包括加密、身份验证、访问控制、漏洞修复等方面。设备处于 NIAP 模式可以提供更高的安全级别,并满足特定安全需求的组织和部门的要求。需要注意的是,NIAP 模式的具体实现和要求可能因不同的国家、组织和产品而有所不同。它通常涉及与特定的安全标准和认证计划相关的要求和规范
到此,我们知道了,上面的Shared files中的General storage for shared configurations的配置,接下来再看下User specific files中的General storage for user specific configurations,同理,创建的时候调用方法还是在WifiConfigStore.java中:
public static @Nullable List<StoreFile> createUserFiles(int userId, boolean shouldEncryptCredentials) { UserHandle userHandle = UserHandle.of(userId); return createFiles( Environment.getWifiUserDirectory(userId), ---> 这个上面已经记录过了,就是获取路径 //数组转集合,参数就是(2,3),对应 SparseArray<String> STORE_ID_TO_FILE_NAME 里面的value Arrays.asList(STORE_FILE_USER_GENERAL, STORE_FILE_USER_NETWORK_SUGGESTIONS), userHandle, shouldEncryptCredentials); } // 调用下面这个方法 private static @Nullable List<StoreFile> createFiles(File storeDir, List<Integer> storeFileIds, UserHandle userHandle, boolean shouldEncryptCredentials) { List<StoreFile> storeFiles = new ArrayList<>(); for (int fileId : storeFileIds) { // 参数:路径,需要创建的xml,……,……。具体的看下面 StoreFile storeFile = createFile(storeDir, fileId, userHandle, shouldEncryptCredentials); if (storeFile == null) { return null; } storeFiles.add(storeFile); } return storeFiles; } // 看这里创建啥 private static @Nullable StoreFile createFile(@NonNull File storeDir, @StoreFileId int fileId, UserHandle userHandle, boolean shouldEncryptCredentials) { if (!storeDir.exists()) { if (!storeDir.mkdir()) { ---> 不存在创建一个目录 Log.w(TAG, "Could not create store directory " + storeDir); return null; } } File file = new File(storeDir, STORE_ID_TO_FILE_NAME.get(fileId)); WifiConfigStoreEncryptionUtil encryptionUtil = null; if (shouldEncryptCredentials) { encryptionUtil = new WifiConfigStoreEncryptionUtil(file.getName()); } return new StoreFile(file, fileId, userHandle, encryptionUtil); ---> 和StoreFile对应,返回 } public StoreFile(File file, @StoreFileId int fileId, @NonNull UserHandle userHandle, @Nullable WifiConfigStoreEncryptionUtil encryptionUtil) { mAtomicFile = new AtomicFile(file); mFileName = file.getAbsolutePath(); mFileId = fileId; mUserHandle = userHandle; mEncryptionUtil = encryptionUtil; }
/** * 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() { ---> 在系统启动后就调用checkAndStartWifi方法,然后就加载配置,即此方法 // 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, mFrameworkFacade.isNiapModeOn(mContext)); if (userStoreFiles == null) { Log.wtf(TAG, "Failed to create user store files"); return false; } mWifiConfigStore.setUserStores(userStoreFiles); mDeferredUserUnlockRead = false; } try { mWifiConfigStore.read(); } catch (IOException | IllegalStateException e) { Log.wtf(TAG, "Reading from new store failed. All saved networks are lost!", e); return handleConfigStoreFailure(false); } catch (XmlPullParserException e) { Log.wtf(TAG, "XML deserialization of store failed. All saved networks are lost!", e); return handleConfigStoreFailure(false); } loadInternalData(mNetworkListSharedStoreData.getConfigurations(), mNetworkListUserStoreData.getConfigurations(), mRandomizedMacStoreData.getMacMapping()); return true; }
支持我们也大致知道了初始化的地方,那接着上面这段代码,大致我们知道这段代码是从存储中加载已保存的网络配置数据,并进行相应的处理和加载操作,以便在系统启动或用户解锁后能够正确地恢复网络配置。具体的我们看下这两个地方在干嘛,一个个来看:
public void read() throws XmlPullParserException, IOException { // Reset both share and user store data. for (StoreFile sharedStoreFile : mSharedStores) { ---> 这个就是指 STORE_FILE_NAME_SHARED_GENERAL,即 WifiConfigStore.xml resetStoreData(sharedStoreFile); } if (mUserStores != null) { for (StoreFile userStoreFile : mUserStores) { ---> STORE_FILE_NAME_USER_GENERAL,即 WifiConfigStore.xml resetStoreData(userStoreFile); } } // 上面先重置掉,现在下面这个去读取 long readStartTime = mClock.getElapsedSinceBootMillis(); readFromSharedStoreFiles(); --->最终通XmlUtil类里面的parseFromXml方法去匹配对应的xml文件 if (mUserStores != null) { readFromUserStoreFiles(); } 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."); }
从注释我们就大致知道这个方法了,把存储的填充到内存中
/** * 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 macAddressMapping */ private void loadInternalData( List<WifiConfiguration> sharedConfigurations, List<WifiConfiguration> userConfigurations, 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); 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(); sendConfiguredNetworkChangedBroadcast(WifiManager.CHANGE_REASON_ADDED); mPendingStoreRead = false; }
至此,我们大致就知道了文件是如何创建的。