Android11 热点配置信息保存分析
文章目录
- Android11 热点配置信息保存分析
- 一、Android11 wifi和热点 配置信息保存的文件位置
- 1、wifi和热点保存的实际位置
- 2、wifi和热点保存位置的描述
- 二、热点配置保存文件位置分析
- 1、热点信息保存流程
- (1)保存数据的具体代码:
- (2) 后续流程
- 2、热点信息保存流程
- (1)热点配置获取代码
- (2)热点配置获取WifiManager暴露接口
- (2)热点配置获取Service实现
- (2)热点配置获取具体存储类WifiApConfigStore
- 3、热点信息保存文件分析
- 继续追踪 逻辑。
- 还是 这个文件!继续追踪。
- 继续追文件定义源码:
- 继续追File对象源头,
- 还要往下追系统源目录
- 4、热点信息保存文件具体内容
- (1)wifi信息数据:
- (2)热点信息数据:
- 三、其他:
- 1、流程总结:
- 2、配置参数总结
- 3、在底层的生成的热点配置文件
本文分析热点信息保存生成的文件位置。
通过本文可以大致了解保存/获取热点信息过程,并且了解热点配置文件生成的具体文件位置。
直接从网上搜索很多都是说这个目录:/data/misc/wifi/
但是实际上,我从Android11 上对应的目录搜不到这个文件。所以在Android11 上这个目录肯定是不对的!
一、Android11 wifi和热点 配置信息保存的文件位置
1、wifi和热点保存的实际位置
先公布一下答案,Android11 中wifi 和热点信息保存的文件位置:
wifi信息保存位置:
/data/misc/apexdata//
热点信息保存位置:
/data/misc/apexdata//
2、wifi和热点保存位置的描述
通过系统源码全局搜索找到一个 的相关描述:
frameworks\base\wifi\java\android\net\wifi\migration_samples\
Shared files
============
//wifi信息保存的文件声明
1) - General storage for shared configurations. Includes
user's saved Wi-Fi networks.
AOSP Path in Android 10: /data/misc/wifi/
AOSP Path in Android 11: /data/misc/apexdata//wifi/
Sample File (in this folder): Shared_WifiConfigStore.xml
//热点信息保存的文件声明
2) - Storage for user's softap/tethering configuration.
AOSP Path in Android 10: /data/misc/wifi/.
Note: Was key/value format in Android 10. Conversion to XML done in .
AOSP Path in Android 11: /data/misc/apexdata//wifi/
Sample File (in this folder): Shared_WifiConfigStoreSoftAp.xml
从上面的描述可以看到无论是wifi还是热点的信息配置保存位置都是存在的变动。
wifi信息上面说的没问题,但是热点信息说的就比较不清楚了!
看起来是保存在 /data/misc/apexdata//wifi/ ,那不是和wifi信息重名了?
后面加了已经实际名称是:Shared_WifiConfigStoreSoftAp.xml,但是实际源码中分析只有 。
所以上面热点这里提示是同热点信息同一个文件夹(in this folder)。
所以我们要看Android11 wifi 和 热点信息的保存配置,查看 /data/misc/apexdata//wifi/ 目录就可以。
这个只是源码的提示,具体的还是要分析源码进行确认。这样有利于我们在不用系统版本同样能分析出具体目录。
二、热点配置保存文件位置分析
1、热点信息保存流程
(1)保存数据的具体代码:
WifiManager mWifiManager = (WifiManager) (Context.WIFI_SERVICE);
configBuilder = new ();
(getActivity().getString(.str_androidap));
(WiFiHotspotSetupDialog.BANd_5GHZ);
SoftApConfiguration config = ();
(config);
Android11 不能使用 (mConfig);
需要使用:(config);
(2) 后续流程
frameworks\base\wifi\java\android\net\wifi\
setSoftApConfiguration(SoftApConfiguration softApConfig)
frameworks\opt\net\wifi\service\java\com\android\server\wifi\
setSoftApConfiguration(SoftApConfiguration softApConfig, String packageName)
frameworks\opt\net\wifi\service\java\com\android\server\wifi\
setApConfiguration(softApConfig)
persistConfigAndTriggerBackupManagerProxy(config)
(true);//重点:保存配置信息成本地文件
frameworks\opt\net\wifi\service\java\com\android\server\wifi\
saveToStore(boolean forceWrite)
(forceWrite);
frameworks\opt\net\wifi\service\java\com\android\server\wifi\ //wifi 和 热点最终都在这里处理
write(boolean forceSync)
writeBufferedData();
(); //数据写入
这里找到StoreFile对应的路径就可以找到配置信息具体的文件位置了!
先看完热点获取信息流程,后续再分析具体目录文件。
2、热点信息保存流程
(1)热点配置获取代码
SoftApConfiguration wifiConfig = (); //重点
//获取名称
()
//获取加密类型
(); //SECURITY_TYPE_OPEN = 0;SECURITY_TYPE_WPA2_PSK = 1;
//获取密码
();
//获取band值
();
//获取channel值
();
(2)热点配置获取WifiManager暴露接口
frameworks\base\wifi\java\android\net\wifi\
public WifiConfiguration getWifiApConfiguration() {
try {
return ();
} catch (RemoteException e) {
throw ();
}
}
(2)热点配置获取Service实现
frameworks\opt\net\wifi\service\java\com\android\server\wifi\
public WifiConfiguration getWifiApConfiguration() {
...
return ((mWifiApConfigStore::getApConfiguration,
new ().build())).toWifiConfiguration();
}
(2)热点配置获取具体存储类WifiApConfigStore
frameworks\opt\net\wifi\service\java\com\android\server\wifi\
private SoftApConfiguration mPersistentWifiApConfig = null;
public synchronized SoftApConfiguration getApConfiguration() {
if (mPersistentWifiApConfig == null) {
/* Use default configuration. */
(TAG, "Fallback to use default AP configuration");
persistConfigAndTriggerBackupManagerProxy(getDefaultApConfiguration());
}
SoftApConfiguration sanitizedPersistentconfig =
sanitizePersistentApConfig(mPersistentWifiApConfig);
if (mPersistentWifiApConfig != sanitizedPersistentconfig) {
(TAG, "persisted config was converted, need to resave it");
persistConfigAndTriggerBackupManagerProxy(sanitizedPersistentconfig);
}
return mPersistentWifiApConfig;
}
//保存配置文件
private void persistConfigAndTriggerBackupManagerProxy(SoftApConfiguration config) {
mPersistentWifiApConfig = config;
mHasNewDataToSerialize = true;
(true);//重点:保存配置信息成本地文件
();
}
//系统默认配置,如果系统没有会生成保存到本地一次
//名称:AndroidAP_XXXX(XXX为随机数值),
//频段:2.4G Band=2,channel=0,channel后续会随机生成一个
//密码为随机15位字母字符串
private SoftApConfiguration getDefaultApConfiguration() {
configBuilder = new ();
(SoftApConfiguration.BAND_2GHZ);
(().getString(
.wifi_tether_configure_ssid_default) + "_" + getRandomIntForDefaultSsid());
if (ApConfigUtil.isWpa3SaeSupported(mContext)) {
(generatePassword(),
SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION);
} else {
(generatePassword(),
SoftApConfiguration.SECURITY_TYPE_WPA2_PSK);
}
return ();
}
从上面代码看出热点配置信息都是从 mPersistentWifiApConfig 返回的。
这个配置信息是在wifiService 启动的时候,会 WifiApConfigStore 并且从本地文件中读取新给 mPersistentWifiApConfig。
如果开机本地文件没用热点配置文件,那么就会 getDefaultApConfiguration() 生成默认配置,并保存到本地文件。
那么具体文件保存到哪里的,就要接着分析 里面的具体逻辑了!
frameworks\opt\net\wifi\service\java\com\android\server\wifi\ //wifi 和 热点最终都在这里处理
3、热点信息保存文件分析
那么重头戏来了!看懂这个,wifi配置信息一样能找到。
接上面的逻辑:
frameworks\opt\net\wifi\service\java\com\android\server\wifi\
saveToStore(boolean forceWrite)
(forceWrite);
frameworks\opt\net\wifi\service\java\com\android\server\wifi\ //wifi 和 热点最终都在这里处理
write(boolean forceSync)
writeBufferedData();
(); //数据写入
继续追踪 逻辑。
frameworks\opt\net\wifi\service\java\com\android\server\wifi\
public void write(boolean forceSync)
throws XmlPullParserException, IOException {
boolean hasAnyNewData = false;
// Serialize the provided data and send it to the respective stores. The actual write will
// be performed later depending on the |forceSync| flag .
for (StoreFile sharedStoreFile : mSharedStores) {
if (hasNewDataToSerialize(sharedStoreFile)) {
byte[] sharedDataBytes = serializeData(sharedStoreFile);
(sharedDataBytes);
hasAnyNewData = true;
}
}
if (mUserStores != null) {
for (StoreFile userStoreFile : mUserStores) {
if (hasNewDataToSerialize(userStoreFile)) {
byte[] userDataBytes = serializeData(userStoreFile);
(userDataBytes);
hasAnyNewData = true;
}
}
}
//上面的是否有newData,可以先不管,重点是:writeBufferedData()
if (hasAnyNewData) {
// Every write provides a new snapshot to be persisted, so |forceSync| flag overrides
// any pending buffer writes.
if (forceSync) {
writeBufferedData();
} else {
startBufferedWriteAlarm();
}
} else if (forceSync && mBufferedWritePending) {
// no new data to write, but there is a pending buffered write. So, |forceSync| should
// flush that out.
writeBufferedData();
}
}
//数据写入,这里看不到File的写入,只看到调用 writeBufferedRawData 方法,只能继续跟踪了!
private void writeBufferedData() throws IOException {
stopBufferedWriteAlarm();
long writeStartTime = ();
for (StoreFile sharedStoreFile : mSharedStores) {
(); //数据写入
}
if (mUserStores != null) {
for (StoreFile userStoreFile : mUserStores) {
(); //数据写入
}
}
long writeTime = () - writeStartTime;
try {
(toIntExact(writeTime));
} catch (ArithmeticException e) {
// Silently ignore on any overflow errors.
}
(TAG, "Writing to stores completed in " + writeTime + " ms.");
}
//内部类哦
public static class StoreFile {
/**
* The store file to be written to.
*/
private final AtomicFile mAtomicFile; //文件路径
/**
* This is an intermediate buffer to store the data to be written.
*/
private byte[] mWriteData; //写入的数据
/**
* Store the file name for setting the file permissions/logging purposes.
*/
private final String mFileName; //保存的文件名称
。。。
public StoreFile(File file, @StoreFileId int fileId,
@NonNull UserHandle userHandle,
@Nullable WifiConfigStoreEncryptionUtil encryptionUtil) {
mAtomicFile = new AtomicFile(file);
mFileName = ();
mFileId = fileId;
mUserHandle = userHandle;
mEncryptionUtil = encryptionUtil;
}
public String getName() {
return ().getName();
}
public byte[] readRawData() throws IOException {
byte[] bytes = null;
try {
bytes = ();
} catch (FileNotFoundException e) {
return null;
}
return bytes;
}
public void storeRawDataToWrite(byte[] data) {
mWriteData = data;
}
//重点:这里看到FileOutputStream,继续追踪对应的File,肯定能找到对应的保存文件路径了!
public void writeBufferedRawData() throws IOException {
if (mWriteData == null) return; // No data to write for this file.
// Write the data to the atomic file.
FileOutputStream out = null;
try {
out = ();
(mFileName, FILE_MODE);
(mWriteData); //数据写入
(out);
} catch (IOException e) {
if (out != null) {
(out);
}
throw e;
}
// Reset the pending write data after write.
mWriteData = null;
}
}
从上面代码可以看出找到FileOutputStream 对应的路径,就可以找到保存配置文件的路径了。
这里不用研究AtomicFile的实现,只要研究的new StoreFile 对象传入的 File 对象即可。
还是 这个文件!继续追踪。
//(1)查看创建StoreFile对象
private static @Nullable StoreFile createFile(@NonNull File storeDir,
@StoreFileId int fileId, UserHandle userHandle, boolean shouldEncryptCredentials) {
//判断是否存在该文件夹,如果没有就创建,创建不了就返回null
if (!()) {
if (!()) {
(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(());
}
return new StoreFile(file, fileId, userHandle, encryptionUtil); // **构造StoreFile
}
//(2)查看获取所有的StoreFile对象
private static @Nullable List<StoreFile> createFiles(File storeDir, List<Integer> storeFileIds,
UserHandle userHandle, boolean shouldEncryptCredentials) {
List<StoreFile> storeFiles = new ArrayList<>();
for (int fileId : storeFileIds) { //**所以重点是看storeFileIds里面有几个int值
StoreFile storeFile =
createFile(storeDir, fileId, userHandle, shouldEncryptCredentials);// 执行第一步的创建
if (storeFile == null) {
return null;
}
(storeFile);
}
return storeFiles;
}
//(3)获取StoreFile对象上一步,这个是暴露的,
//但是发现这里没传入路径,所以文件路径+文件名都是在 定义的
public static @NonNull List<StoreFile> createSharedFiles(boolean shouldEncryptCredentials) {
return createFiles(
(), //重点:路径文件夹
(STORE_FILE_SHARED_GENERAL, STORE_FILE_SHARED_SOFTAP), //重点:文件名称
,
shouldEncryptCredentials);
}
private static final SparseArray<String> STORE_ID_TO_FILE_NAME =
new SparseArray<String>() {{
//就是前面两个是wifi和热点的
put(STORE_FILE_SHARED_GENERAL, STORE_FILE_NAME_SHARED_GENERAL); //wifi配置信息
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);
}};
/**
/**
* Config store file for general shared store file.
*/
public static final int STORE_FILE_SHARED_GENERAL = 0;
/**
* Config store file for softap shared store file.
*/
public static final int STORE_FILE_SHARED_SOFTAP = 1;
/**
* Config store file for general user store file.
*/
public static final int STORE_FILE_USER_GENERAL = 2;
/**
* Config store file for network suggestions user store file.
*/
public static final int STORE_FILE_USER_NETWORK_SUGGESTIONS = 3;
/**
* Config store file name for general shared store file.
*/
private static final String STORE_FILE_NAME_SHARED_GENERAL = ""; //wifi配置名称
/**
* Config store file name for SoftAp shared store file.
*/
private static final String STORE_FILE_NAME_SHARED_SOFTAP = ""; //热点配置名称
/**
* Config store file name for general user store file.
*/
private static final String STORE_FILE_NAME_USER_GENERAL = "";
/**
* Config store file name for network suggestions user store file.
*/
private static final String STORE_FILE_NAME_USER_NETWORK_SUGGESTIONS =
"";
上面已经知道了wifi和热点文件名称,
可以使用find . -name “” 方式找到热点配置信息文件
继续追文件定义源码:
frameworks\opt\net\wifi\service\java\com\android\server\wifi\util\
/**
* Wifi apex name.
*/
private static final String WIFI_APEX_NAME = "";
/**
* Wifi shared folder.//这里写的就是wifi文件夹的意思
*/
public static File getWifiSharedDirectory() {
return (WIFI_APEX_NAME).getDeviceProtectedDataDir();
}
继续追File对象源头,
frameworks\base\core\java\android\content\
private static final String APEX_DATA = "apexdata";
private final String mApexModuleName;
private ApexEnvironment(String apexModuleName) {
mApexModuleName = apexModuleName;
}
public static ApexEnvironment getApexEnvironment(@NonNull String apexModuleName) {
(apexModuleName, "apexModuleName cannot be null");
//TODO(b/141148175): Check that apexModuleName is an actual APEX name
return new ApexEnvironment(apexModuleName);
}
@NonNull
public File getDeviceProtectedDataDir() {
return (
(), APEX_DATA, mApexModuleName);
}
从上面的代码可以文件夹路径有:apexdata + ,具体还有啥,要继续分析。
还要往下追系统源目录
frameworks\base\core\java\android\os\
private static final File DIR_ANDROID_ROOT = getDirectory(ENV_ANDROID_ROOT, "/system");
private static final File DIR_ANDROID_DATA = getDirectory(ENV_ANDROID_DATA, "/data"); //就是data目录
/** {@hide} */
public static File getDataMiscDirectory() {
return new File(getDataDirectory(), "misc");
}
/**
* Return the user data directory.
*/
public static File getDataDirectory() {
return DIR_ANDROID_DATA;
}
//文件对象,在base文件夹后面不断加后面参数添加文件夹层级名称
public static File buildPath(File base, String... segments) {
File cur = base;
for (String segment : segments) {
if (cur == null) {
cur = new File(segment);
} else {
cur = new File(cur, segment);
}
}
return cur;
}
所以最终的路径是:
data/misc/apexdata/
adb shell 使用命令看确认是在这里的!
console:/data/misc/apexdata/ # ls
这里看到一个是wifi信息保存文件和一个热点信息保存文件!
4、热点信息保存文件具体内容
再看看里面文件的数据:
(1)wifi信息数据:
部分内容如下:
console:/data/misc/apexdata/ # cat
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<WifiConfigStoreData>
<int name="Version" value="3" />
<NetworkList>
<Network>
<WifiConfiguration>
<string name="ConfigKey">"VPN_5G"WPA_PSK</string> //wifi名称 + 密码类型
<string name="SSID">"VPN_5G"</string> //wifi名称
<string name="PreSharedKey">"12345678"</string> wifi密码
。。。
</WifiConfiguration>
<IpConfiguration>//静态ip和代理信息
<string name="IpAssignment">DHCP</string>
<string name="ProxySettings">NONE</string>
</IpConfiguration>
</Network>
</NetworkList>
。。。
</WifiConfigStoreData>
console:/data/misc/apexdata/ #
(2)热点信息数据:
部分内容如下:
console:/data/misc/apexdata/ #
console:/data/misc/apexdata/ # cat
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<WifiConfigStoreData>
<int name="Version" value="3" />
<SoftAp>
<string name="SSID">AndroidAP_6564</string> //热点名称
<int name="ApBand" value="2" /> //热点频段 band
<int name="Channel" value="36" /> //热点信道 channel
<boolean name="HiddenSSID" value="false" />
<int name="SecurityType" value="1" /> //密码类型
<string name="Passphrase">gutir33r</string> //热点密码
<int name="MaxNumberOfClients" value="0" />
<boolean name="ClientControlByUser" value="false" />
<boolean name="AutoShutdownEnabled" value="true" /> //是否未使用自动关闭
<long name="ShutdownTimeoutMillis" value="0" /> //设置多久未使用自动关闭热点,未设置就是5分钟
<BlockedClientList />
<AllowedClientList />
</SoftAp>
</WifiConfigStoreData>
console:/data/misc/apexdata/ #
三、其他:
1、流程总结:
(1)
(2)(request, executor, tetheringCallback)
(3)
(4)(request, listener);
//方法名变化,使用null 对象开启热点
(5)(null /* use existing softap config */)
(6)(@Nullable SoftApConfiguration softApConfig)
//方法名再变化
(7)(apModeConfig);
(8)();
ActiveModeManager manager = (listener, callback, softApConfig);
(manager);
();
ActiveModeManager是接口类,会调用到()
(9)()
(10)(mApInterfaceName, (), mSoftApListener)
(11)(ifaceName, config, listener::onFailure)
(12)根据硬件版本调用不同的接口实现:addAccessPoint_X_X
2、配置参数总结
热点的配置在() 会有一定的修改,
比如channel ==0 的情况是会在中,对应的band范围内随机生成一个channel值。
frameworks\opt\net\wifi\service\java\com\android\server\wifi\util\
所以热点配置有变化,需要分析的生活,可以在SoftApManager 中多添加日志即可。
3、在底层的生成的热点配置文件
热点信息在底层逻辑也是会保存一个配置文件,这个配置文件是底层生成的,具体逻辑不清楚,没怎么开发过驱动逻辑。
/data/vendor/wifi/hostapd/hostapd_ap0.conf
console:/data/vendor/wifi/hostapd # ls
hostapd_ap0.conf sockets
console:/data/vendor/wifi/hostapd # cat hostapd_ap0.conf
interface=ap0
driver=nl80211
ctrl_interface=/data/vendor/wifi/hostapd/ctrl
ssid2=416e64726f696441505f36363636
channel=36
op_class=128
ieee80211n=1
ieee80211ac=1
hw_mode=a
ht_capab=[SHORT-GI-20][SHORT-GI-40][HT40+]
vht_oper_chwidth=1
vht_oper_centr_freq_seg0_idx=42
ignore_broadcast_ssid=0
wowlan_triggers=any
wpa=2
rsn_pairwise=CCMP
wpa_passphrase=123456789
console:/data/vendor/wifi/hostapd #
注意这里底层保存的配置也是在上层生成的,比如上层channel=0,会直接保存到,
但是传给底层前,会随机生成一个合适的channel值,传递给底层。底层开启成功就会保存。
channel 变化的具体的逻辑都在 ()和相关代码 里面。