WifiServiceImpl 接口实现:Binder 调用、权限检查、回调注册
好,咱们今天来聊聊 WifiServiceImpl 里最核心的几个机制。说白了,Wi-Fi 服务跑在系统进程里,App 在用户空间,它们怎么通信?靠的就是 Binder。那权限怎么控制?回调怎么注册?这些都是实打实的工程问题。
我个人习惯把 WifiServiceImpl 看作一个「守门人」。它不直接干活,但它负责验证身份、分发任务、通知结果。今天我们就拆开看看,这个守门人是怎么工作的。
Binder 调用的设计思路
WifiServiceImpl 继承自 IWifiManager.Stub,这意味着它天生就是一个 Binder 服务端。App 端拿到的是 IWifiManager 的代理,调用任何方法,最终都会走到 Stub 里的 onTransact,然后分发到对应的实现方法。
举个例子,App 调用 getWifiEnabledState(),流程是这样的:
// App 端
IWifiManager wifiManager = ...;
int state = wifiManager.getWifiEnabledState();
// 底层 Binder 驱动
// -> Binder.onTransact(GET_WIFI_ENABLED_STATE_TRANSACTION, data, reply)
// -> WifiServiceImpl.getWifiEnabledState()
嗯,这里要注意:Binder 调用是同步的。如果服务端处理时间太长,App 端会 ANR。所以耗时操作一定要异步处理。我在项目中就遇到过,某个第三方 App 频繁调用扫描接口,导致系统服务线程阻塞,最后整个 Wi-Fi 设置界面卡死。后来我们加了一层异步队列,才解决这个问题。
权限检查:三层防护
Wi-Fi 服务涉及用户隐私和系统安全,权限检查必须严格。WifiServiceImpl 的权限检查分三层:
- 调用者身份验证——检查调用者的 UID/PID,确认来源合法
- 权限声明检查——检查 App 是否声明了对应的权限
- 运行时权限检查——Android 6.0+ 需要动态授权
代码里最常见的写法是这样的:
private void enforceChangePermission() {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CHANGE_WIFI_STATE,
"WifiService");
}
private void enforceLocationPermission() {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.ACCESS_FINE_LOCATION,
"Need location permission");
}
你想想看,如果某个恶意 App 没有声明 CHANGE_WIFI_STATE 权限,却想调用 setWifiEnabled(),那它会在 enforceChangePermission 这一步直接抛出 SecurityException。系统会把它「请出去」。
enforceNetworkStackPermission,专门针对系统级调用做了加固。如果你在做系统开发,记得检查你的 App 是否在 platform.xml 的白名单里。
回调注册机制
回调,说白了就是「你告诉我什么时候 Wi-Fi 开了,我再去干活」。WifiServiceImpl 支持多种回调,最常用的是 WifiManager.WifiLock 和 WifiManager.WifiStateListener。
注册回调的流程大致如下:
// App 端注册
WifiManager wifiManager = (WifiManager) getSystemService(WIFI_SERVICE);
WifiManager.WifiStateListener listener = new WifiManager.WifiStateListener() {
@Override
public void onWifiStateChanged(int state) {
// 处理状态变化
}
};
wifiManager.registerWifiStateListener(listener);
// 服务端实现
public void registerWifiStateListener(IWifiStateListener listener) {
// 1. 权限检查
enforceLocationPermission();
// 2. 检查 listener 是否有效
if (listener == null) return;
// 3. 保存到回调列表
mWifiStateListeners.add(listener);
// 4. 绑定死亡通知
listener.asBinder().linkToDeath(new DeathRecipient() {
@Override
public void binderDied() {
mWifiStateListeners.remove(listener);
}
}, 0);
}
这里有个细节:死亡通知。如果 App 进程挂了,Binder 代理会收到死亡通知,服务端需要及时清理回调列表。否则会出现「僵尸回调」,浪费系统资源。
整体架构图
下面这张图展示了 WifiServiceImpl 的核心交互流程:
实际开发中的注意事项
讲完了理论,咱们聊聊实战中容易出问题的地方。
| 场景 | 问题 | 解决方案 |
|---|---|---|
| App 频繁注册/注销回调 | 回调列表膨胀,内存泄漏 | 限制注册频率,使用 WeakReference |
| 权限被动态撤销 | 回调仍在发送,App 收到无效通知 | 分发前检查 UID 权限状态 |
| Binder 线程池耗尽 | 所有 Binder 调用阻塞 | 异步处理,控制并发数 |
| 死亡通知未处理 | 僵尸回调残留 | 必须实现 linkToDeath/unlinkToDeath |
嗯,今天的内容就到这里。代码里还有很多细节,比如 WorkSource 的传递、Binder.clearCallingIdentity 的使用,这些我们后面再细聊。你先把 Binder 调用和权限检查的流程理清楚,后面学起来会轻松很多。