15、多用户与多并发:UserManager 隔离、多并发连接、配置存储隔离
多用户支持,是 Android 从早期版本就开始设计的一个核心能力。但在 Wi-Fi 这个场景下,多用户带来的问题远比想象中复杂。我最早接触这块时,以为只是给每个用户分一个配置文件就行了,结果踩了不少坑。
说白了,Wi-Fi 的多用户管理,要解决三个核心问题:用户隔离、并发连接、配置存储隔离。这三个问题环环相扣,任何一个没处理好,都会导致用户切换时 Wi-Fi 断连、配置丢失、甚至系统崩溃。
15.1 UserManager 与用户隔离机制
Android 的多用户模型,每个用户都有自己的 UserHandle。Wi-Fi Service 通过 UserManager 来识别当前操作的用户,并决定是否允许该用户访问 Wi-Fi 配置。
我习惯在代码里这样判断用户权限:
UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
UserHandle currentUser = Process.myUserHandle();
if (um.isUserUnlocked(currentUser)) {
// 用户已解锁,可以访问 Wi-Fi 配置
loadWifiConfigForUser(currentUser);
} else {
// 用户未解锁,不能操作 Wi-Fi
Log.w(TAG, "User " + currentUser + " is not unlocked yet.");
}
这里有个关键点:用户解锁状态。用户刚启动时,如果还没输入凭据解锁设备,Wi-Fi Service 是不能访问该用户的配置存储的。我曾经遇到过一个问题:设备重启后,Wi-Fi 一直连不上,查了半天发现是用户解锁事件没处理好,Wi-Fi Service 在用户解锁前就去读配置了,结果读了个空。
Intent.ACTION_USER_UNLOCKED 广播,在用户解锁后再加载 Wi-Fi 配置。否则会出现配置丢失的假象。
15.2 多并发连接:一个用户一个网络栈
多用户场景下,每个用户是否可以同时连接不同的 Wi-Fi 网络?答案是:可以,但有限制。
Android 的 Wi-Fi 架构中,WifiStateMachine 是全局唯一的。也就是说,底层硬件同一时刻只能连接一个 Wi-Fi 网络。但多用户并发连接,指的是不同用户可以在自己的 WifiConfiguration 列表中维护各自的连接状态,当用户切换时,Wi-Fi Service 会断开当前用户的连接,恢复目标用户的连接。
我画了一张图,帮你理解这个流程:
你看,每个用户有自己的配置存储,但底层硬件只有一个。用户切换时,Wi-Fi Service 会做一次完整的「断开-加载-连接」流程。这个流程如果处理不好,就会出现切换后 Wi-Fi 连不上的问题。
15.3 配置存储隔离:WifiConfigStore 的多用户设计
配置存储隔离,是 Wi-Fi 多用户支持的基础。Android 使用 WifiConfigStore 来管理所有 Wi-Fi 配置,每个用户的配置存储在各自的数据目录下。
具体路径是这样的:
/data/misc/apexdata/com.android.wifi/
├── WifiConfigStore.xml # 用户0(系统用户)的配置
├── WifiConfigStoreUser10.xml # 用户10的配置
├── WifiConfigStoreUser11.xml # 用户11的配置
└── ...
每个文件里存储的是该用户保存的所有 Wi-Fi 网络配置,包括 SSID、安全类型、密码(加密存储)、优先级等。
我建议你在开发时,重点关注 WifiConfigStore 的 loadFromStore() 和 saveToStore() 方法。这两个方法会根据当前用户自动选择对应的存储文件。
15.4 多用户并发连接的实战避坑
我在项目中遇到过几个典型问题,分享给你:
- 用户切换时 Wi-Fi 断连时间过长:原因是
WifiStateMachine在断开旧连接后,没有及时触发新用户的连接。解决方案是在用户切换广播中,提前预加载目标用户的配置。 - 配置丢失:用户 A 保存了 Wi-Fi 密码,切换到用户 B 再切回来,密码没了。后来发现是
WifiConfigStore在保存时用了错误的 UserHandle。嗯,这个问题排查了很久。 - 并发连接冲突:两个用户同时发起连接请求,导致
WifiStateMachine状态混乱。解决办法是在连接请求入口加锁,同一时刻只处理一个用户的连接请求。
WifiServiceImpl 中,可以通过 enforceAccessPermission() 来校验当前调用者的用户身份。如果调用者不是当前前台用户,直接拒绝操作。
15.5 多用户场景下的配置迁移
有时候,用户 A 希望把自己保存的 Wi-Fi 配置分享给用户 B。Android 提供了 WifiManager 的 addNetwork() 方法,但这个方法只能操作当前用户的配置。
如果你想实现跨用户配置共享,需要借助 ContentProvider 或者 FileProvider 来传递配置数据。我个人习惯的做法是:
// 用户A导出配置
WifiManager wifiManager = context.getSystemService(WifiManager.class);
List<WifiConfiguration> configs = wifiManager.getConfiguredNetworks();
// 序列化为JSON,通过Intent传递给用户B
// 用户B导入配置
Intent intent = getIntent();
String configJson = intent.getStringExtra("wifi_config");
WifiConfiguration config = deserializeConfig(configJson);
wifiManager.addNetwork(config);
wifiManager.saveConfiguration();
但要注意,getConfiguredNetworks() 返回的配置中,密码字段是加密的。你无法直接拿到明文密码。所以跨用户共享配置,通常只适用于开放网络或者企业级证书认证的网络。
15.6 总结一下
多用户与多并发,是 Wi-Fi Service 开发中比较容易忽视但又极其重要的部分。你想想看,如果用户切换后 Wi-Fi 连不上,用户的第一反应就是「这手机有问题」。所以,做好用户隔离、处理好并发连接、保证配置存储隔离,是 Wi-Fi 稳定性的基石。
我曾经在一个项目中,因为忽略了用户解锁事件,导致设备在开机后 Wi-Fi 配置全部丢失。排查了两天才找到原因。从那以后,我每次写 Wi-Fi 多用户相关的代码,都会先问自己三个问题:
- 当前用户是否已解锁?
- 配置存储是否指向了正确的用户目录?
- 并发连接请求是否做了排队处理?
这三个问题想清楚了,多用户 Wi-Fi 开发就不会出大问题。