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 会断开当前用户的连接,恢复目标用户的连接。

我画了一张图,帮你理解这个流程:

用户A (UserHandle 10) 用户B (UserHandle 11) 用户C (UserHandle 12) Wi-Fi 配置存储 (WifiConfigStore) 每个用户独立存储,通过 UserHandle 隔离 WifiStateMachine (全局唯一) 同一时刻只能连接一个网络 用户切换时:断开当前连接 → 加载目标用户配置 → 发起连接

你看,每个用户有自己的配置存储,但底层硬件只有一个。用户切换时,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、安全类型、密码(加密存储)、优先级等。

我建议你在开发时,重点关注 WifiConfigStoreloadFromStore()saveToStore() 方法。这两个方法会根据当前用户自动选择对应的存储文件。

核心原则: 永远不要跨用户读写 Wi-Fi 配置。即使你有 root 权限,也不建议这么做。每个用户的配置只能由该用户自己的进程访问。

15.4 多用户并发连接的实战避坑

我在项目中遇到过几个典型问题,分享给你:

  • 用户切换时 Wi-Fi 断连时间过长:原因是 WifiStateMachine 在断开旧连接后,没有及时触发新用户的连接。解决方案是在用户切换广播中,提前预加载目标用户的配置。
  • 配置丢失:用户 A 保存了 Wi-Fi 密码,切换到用户 B 再切回来,密码没了。后来发现是 WifiConfigStore 在保存时用了错误的 UserHandle。嗯,这个问题排查了很久。
  • 并发连接冲突:两个用户同时发起连接请求,导致 WifiStateMachine 状态混乱。解决办法是在连接请求入口加锁,同一时刻只处理一个用户的连接请求。
小技巧:WifiServiceImpl 中,可以通过 enforceAccessPermission() 来校验当前调用者的用户身份。如果调用者不是当前前台用户,直接拒绝操作。

15.5 多用户场景下的配置迁移

有时候,用户 A 希望把自己保存的 Wi-Fi 配置分享给用户 B。Android 提供了 WifiManageraddNetwork() 方法,但这个方法只能操作当前用户的配置。

如果你想实现跨用户配置共享,需要借助 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 多用户相关的代码,都会先问自己三个问题:

  1. 当前用户是否已解锁?
  2. 配置存储是否指向了正确的用户目录?
  3. 并发连接请求是否做了排队处理?

这三个问题想清楚了,多用户 Wi-Fi 开发就不会出大问题。


公众号:蓝海资料掘金营,微信deep3321