Skip to content

SM的Binder死亡通知

追踪 ServiceManager 对服务 Binder、注册回调和客户端回调的死亡监听,以及 binderDied 清理服务表和回调表的边界。

基于android-17.0.0_r1
AndroidBinderServiceManager死亡通知源码阅读

SM的Binder死亡通知 ​

ServiceManager 的死亡通知不是“服务死了就按名字删除一项”这么简单。addService() 为远程服务 Binder 注册 ServiceManager 作为 DeathRecipient;registerForNotifications() 和 registerClientCallback() 也把回调 Binder 纳入同一个死亡接收者。驱动发出死亡事件后,ServiceManager::binderDied() 会分别扫描服务表、注册回调表和客户端回调表,并按 Binder 对象匹配清理。

本文面向已经读过 addService注册服务、getService查询服务 和 Binder死亡通知 的读者。本文回答“谁注册死亡监听、死亡事件由谁消费、哪些状态被删除、同名服务覆盖后旧 Binder 的迟到死亡如何影响新表项”这一组源码问题;不展开驱动 BR_DEAD_BINDER 的用户态确认协议。

1. 三类监听 ​

1.1 服务Binder ​

源码文件:frameworks/native/cmds/servicemanager/ServiceManager.cpp

相关函数:ServiceManager::addService()

cpp
if (binder->remoteBinder() != nullptr &&
        binder->linkToDeath(
                sp<ServiceManager>::fromExisting(this)) != OK) {
    ALOGE("Could not linkToDeath when adding %s", name.c_str());
    return Status::fromExceptionCode(
            Status::EX_ILLEGAL_STATE, "Couldn't linkToDeath.");
}

只有远程 Binder 才需要跨进程死亡监听;本地 Binder 不经过驱动的远程死亡路径。注册失败发生在写入 mNameToService 之前,因此失败不会留下一个没有死亡保护的服务表项。

1.2 注册回调 ​

cpp
if (OK != IInterface::asBinder(callback)->linkToDeath(
        sp<ServiceManager>::fromExisting(this))) {
    return Status::fromExceptionCode(
            Status::EX_ILLEGAL_STATE, "Couldn't link to death.");
}
mNameToRegistrationCallback[name].push_back({callback, ctx});

注册监听者本身也是远程 Binder。回调进程死亡时,ServiceManager 不应继续持有无效 callback;因此同一个 binderDied() 入口还负责回调表清理。

1.3 客户端回调 ​

源码文件:frameworks/native/cmds/servicemanager/ServiceManager.cpp

相关函数:registerClientCallback()、mNameToClientCallback

客户端回调记录某服务是否存在客户端引用,也会调用 linkToDeath()。它和注册回调的用途不同:前者反映 client count,后者通知服务注册;但二者死亡后都由 binderDied() 移除。

监听对象所在表注册时机死亡后的清理
服务 BindermNameToServiceaddService删除同 Binder 的服务项
IServiceCallbackmNameToRegistrationCallbackregisterForNotifications删除回调记录
IClientCallbackmNameToClientCallbackregisterClientCallback删除回调记录

2. 事件入口 ​

2.1 DeathRecipient ​

  • frameworks/native/cmds/servicemanager/ServiceManager.h
  • frameworks/native/cmds/servicemanager/ServiceManager.cpp

相关类型:ServiceManager、IBinder::DeathRecipient

ServiceManager 实现 binderDied(const wp<IBinder>& who),所以死亡回调携带的是弱 Binder 引用。这个参数是“哪个 Binder 死了”的身份,而不是服务名;ServiceManager 必须在自己的表中做对象匹配。

驱动事件只把控制权交给 Binder 用户态线程;真正删除名字和回调的是 ServiceManager 对象。死亡通知到达与清理完成之间存在异步时序,查询在清理前可能短暂看到旧表项。

3. 清理算法 ​

3.1 服务表匹配 ​

源码文件:frameworks/native/cmds/servicemanager/ServiceManager.cpp

相关函数:ServiceManager::binderDied()

cpp
for (auto it = mNameToService.begin();
        it != mNameToService.end();) {
    if (who == it->second.binder) {
        it = mNameToService.erase(it);
    } else {
        ++it;
    }
}

使用 erase 返回的新迭代器,允许在遍历中安全删除。匹配条件是 Binder 对象相等,不是名字、PID 或 UID。一个死亡 Binder 若被多个名字注册,循环会删除所有匹配项;源码没有在命中一个名字后立即 break。

3.2 回调表清理 ​

cpp
for (auto it = mNameToRegistrationCallback.begin();
        it != mNameToRegistrationCallback.end();) {
    removeRegistrationCallback(who, &it, nullptr);
}

for (auto it = mNameToClientCallback.begin();
        it != mNameToClientCallback.end();) {
    removeClientCallback(who, &it);
}

两个 helper 都负责在删除元素后推进迭代器,并在某个名字下没有剩余 callback 时删除 map entry。清理注册回调不会删除服务本身;清理 client callback 也不会改变服务 Binder,三张表的 owner 和生命周期必须分开理解。

3.3 同名覆盖 ​

源码文件:frameworks/native/cmds/servicemanager/ServiceManager.cpp

相关函数:ServiceManager::addService()、ServiceManager::binderDied()

cpp
// addService overwrites the old service if it exists
mNameToService[name] = Service{
        .binder = binder,
        .allowIsolated = allowIsolated,
        .dumpPriority = dumpPriority,
        .hasClients = prevClients,
        .guaranteeClient = false,
        .ctx = ctx,
};

同名注册会把 map 中的 Binder 换成新对象,但旧 Binder 的死亡通知可能稍后到达。binderDied() 只比较当前 map 里存的 Binder,因此旧对象的死亡通知不会匹配新对象,不应删除新服务。这是按对象匹配优于按名字删除的关键原因。

图中的“ignored”表示当前服务表匹配不到旧 Binder;旧 Binder 的其他死亡监听记录仍可能在回调表中被清理,但不会凭名字删除 B。

4. 观察边界 ​

4.1 查询竞态 ​

服务死亡、驱动通知、用户态回调和 map 删除不是同一瞬间。getService() 或 checkService() 在 binderDied() 执行前仍可能拿到表中的旧 Binder;清理后才返回空。文章不把“进程已经退出”直接等同于“下一条查询必然为空”。

4.2 重新注册 ​

死亡清理只删除当前匹配对象。随后新的服务进程调用 addService(),会重新通过权限、名称、VINTF 和 linkToDeath() 校验,再写入同名 map。死亡通知机制本身不负责自动重启;lazy 服务是否被 init 拉起属于查询路径或其他启动路径。

4.3 回调通知 ​

死亡清理只移除 callback,不向注册监听者发送一个“服务死亡”的 onRegistration 事件。注册通知发生在服务加入或替换时;死亡后的重新注册会形成新的注册事件。

5. 测试边界 ​

5.1 注册回调 ​

源码文件:frameworks/native/cmds/servicemanager/test_sm.cpp

ServiceNotifications.GetNotification 先注册 CallbackHistorian,再注册同名服务,断言 callback 收到名字和 Binder;GetNotificationForAlreadyRegisteredService 反过来先注册服务,再注册 callback,断言 callback 立即收到当前 Binder。GetMultipleNotification 连续替换同名 Binder,断言收到两次注册事件。这些测试证明 callback 的注册时序与替换通知,不直接模拟真实进程死亡。

5.2 Binder替身 ​

测试中的 LinkableBinder 重写 linkToDeath() 并返回 OK,让 ServiceManager 能继续执行。它证明注册逻辑会尝试建立死亡链接,但没有触发驱动 BR_DEAD_BINDER 或真实 binderDied() 回调。因此当前测试不能证明设备上死亡事件的到达时刻和内核引用回收。

5.3 可执行阅读 ​

bash
rg -n "linkToDeath|binderDied|removeRegistrationCallback|removeClientCallback" \
  frameworks/native/cmds/servicemanager/ServiceManager.cpp \
  frameworks/native/cmds/servicemanager/ServiceManager.h

rg -n "GetNotification|GetMultipleNotification|LinkableBinder" \
  frameworks/native/cmds/servicemanager/test_sm.cpp

rg -n "BR_DEAD_BINDER|DeathRecipient|binderDied" \
  frameworks/native/libs/binder/IPCThreadState.cpp \
  frameworks/native/libs/binder/BpBinder.cpp

遇到“服务已退出但查询仍返回 Binder”,先检查死亡通知是否已经交付并进入 binderDied(),再确认当前 map 中保存的是不是同一个 Binder 对象;若发生同名替换,旧 Binder 的迟到死亡不能作为删除新服务的依据。