addService注册服务
注册一个 Binder 服务,真正改变的不是调用方手里的 IBinder 变量,而是 ServiceManager 进程中的 mNameToService 映射,以及 ServiceManager 对服务 Binder 建立的死亡通知。调用返回成功前,服务名、调用者身份、SELinux、Binder 非空和 VINTF 声明都必须通过;服务进程随后退出时,死亡通知又会把对应名字从映射中删除。
本文面向已经读过 ServiceManager启动、SM成为context_manager 和 Binder事务发送 的读者。前两篇解释 handle 0 如何到达 ServiceManager,事务文章解释 Binder 对象如何在驱动中翻译;本文把这些前置关系接到服务注册的实际入口。范围包括 Java 调用、native 桥接、服务端校验、表项写入、通知与死亡清理;不展开 getService() 的查询等待和 lazy service 启动。
1. 入口桥接
1.1 Java入口
源码文件:frameworks/base/core/java/android/os/ServiceManager.java
相关函数:ServiceManager.addService()、getIServiceManager()
public static void addService(String name, IBinder service, boolean allowIsolated,
int dumpPriority) {
try {
getIServiceManager().addService(name, service, allowIsolated, dumpPriority);
} catch (RemoteException e) {
Log.e(TAG, "error in addService", e);
}
}
private static IServiceManager getIServiceManager() {
if (sServiceManager != null) {
return sServiceManager;
}
sServiceManager = ServiceManagerNative.asInterface(
Binder.allowBlocking(BinderInternal.getContextObject()));
return sServiceManager;
}公开给系统代码的静态方法只负责取得缓存的 IServiceManager 代理并发起调用。BinderInternal.getContextObject() 得到的是前文所说的 context manager 目标;allowBlocking() 是客户端代理的调用策略,不是服务端权限放行。RemoteException 在这个 Java 包装层被记录,调用者不能仅凭“没有抛出 Java 异常”判断服务已经写入服务表。
1.2 代理层
源码文件:frameworks/base/core/java/android/os/ServiceManagerNative.java
相关类型:ServiceManagerProxy
public static IServiceManager asInterface(IBinder obj) {
if (obj == null) {
return null;
}
return new ServiceManagerProxy(obj);
}
public ServiceManagerProxy(IBinder remote) {
mRemote = remote;
mServiceManager = IServiceManager.Stub.asInterface(
Binder.allowBlocking(this.getNativeServiceManager()));
}
public void addService(String name, IBinder service, boolean allowIsolated,
int dumpPriority) throws RemoteException {
mServiceManager.addService(name, service, allowIsolated, dumpPriority);
}这里有两个 Binder 视角:mRemote 是 context object 得到的远端对象,getNativeServiceManager() 返回的 Binder 才被转换成生成的 IServiceManager 接口。Android 17 仍保留 ServiceManagerProxy,但实际 addService 已转给 AIDL 生成接口;不能把旧版 transact 手写代理代码直接套到这一版本。
1.3 JNI与缓存
frameworks/base/core/jni/android_os_ServiceManagerNative.cppframeworks/native/libs/binder/IServiceManagerFFI.cppframeworks/native/libs/binder/BackendUnifiedServiceManager.cpp
相关函数:Java_android_os_ServiceManagerProxy_getNativeServiceManager()、BackendUnifiedServiceManager::addService()
jobject JNICALL Java_android_os_ServiceManagerProxy_getNativeServiceManager(
JNIEnv* env, jobject /*obj*/) {
sp<IBinder> service = IInterface::asBinder(
impl::getJavaServicemanagerImplPrivateDoNotUseExceptInTheOnePlaceItIsUsed());
return javaObjectForIBinder(env, service);
}JNI 返回的是可以交给 Java AIDL Stub 解析的 Binder 对象;随后真正的注册转发发生在 native backend:
Status BackendUnifiedServiceManager::addService(const std::string& name,
const sp<IBinder>& service, bool allowIsolated, int32_t dumpPriority) {
if (mTheRealServiceManager) {
Status status = mTheRealServiceManager->addService(
name, service, allowIsolated, dumpPriority);
if (mEnableAddServiceCache && status.isOk()) {
return updateCache(name, service,
dumpPriority & android::os::IServiceManager::FLAG_IS_LAZY_SERVICE);
}
return status;
}
return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION,
kUnsupportedOpNoServiceManager);
}JNI 只把 native 统一 ServiceManager 实现包装成 Java IBinder。BackendUnifiedServiceManager 先调用真实 ServiceManager,成功后才可能更新 native cache;cache 更新失败也会通过返回的 Status 反映出来。因此服务端成功与客户端缓存成功是相邻但不同的状态,不能把二者合并成一次“注册完成”。
2. 校验顺序
2.1 调用者身份
源码文件:frameworks/native/cmds/servicemanager/ServiceManager.cpp
相关函数:ServiceManager::addService()、Access::getCallingContext()
auto ctx = mAccess->getCallingContext();
if (multiuser_get_app_id(ctx.uid) >= AID_APP) {
return Status::fromExceptionCode(
Status::EX_SECURITY, "App UIDs cannot add services.");
}
std::optional<std::string> accessorName;
if (auto status = canAddService(ctx, name, &accessorName);
!status.isOk()) {
return status;
}Access 从当前 Binder 调用上下文取得 pid、uid 和 SID。第一道检查按 app id 拒绝普通应用 UID;它发生在 canAddService() 之前,所以被拒绝的应用不会进入服务名对应的 SELinux 检查。canAddService() 再检查服务名及可能关联的 VINTF accessor 的 add 权限。
2.2 Binder与名称
if (binder == nullptr) {
return Status::fromExceptionCode(
Status::EX_ILLEGAL_ARGUMENT, "Null binder.");
}
if (!isValidServiceName(name)) {
ALOGE("%s Invalid service name: %s",
ctx.toDebugString().c_str(), name.c_str());
return Status::fromExceptionCode(
Status::EX_ILLEGAL_ARGUMENT, "Invalid service name.");
}空 Binder 和非法名称都在写入映射表前失败。Android 17 的 isValidServiceName() 允许字母、数字以及 _、-、.、/,并限制名称长度;因此名称是否能被 getService() 查询,不由 Java 字符串非空单独决定。
2.3 VINTF边界
#ifndef VENDORSERVICEMANAGER
if (!meetsDeclarationRequirements(ctx, binder, name)) {
return Status::fromExceptionCode(
Status::EX_ILLEGAL_ARGUMENT, "VINTF declaration error.");
}
#endif普通系统 ServiceManager 会检查声明要求;编译为 VENDORSERVICEMANAGER 时这段条件代码不参与。文章中的“注册成功”必须带上构建条件:不能把普通 servicemanager 的 VINTF 检查外推到所有 vendor service manager。
2.4 Dump标记
源码文件:frameworks/native/cmds/servicemanager/ServiceManager.cpp
相关函数:ServiceManager::addService()
if ((dumpPriority & DUMP_FLAG_PRIORITY_ALL) == 0) {
ALOGW("Dump flag priority is not set when adding %s", name.c_str());
}缺少 dump priority 只产生 warning,不会阻止注册。FLAG_IS_LAZY_SERVICE 位还可能被 native backend 用来更新 cache;它与 DUMP_FLAG_PRIORITY_* 的显示优先级不是同一组语义。
3. 表项写入
3.1 死亡通知
源码文件:frameworks/native/cmds/servicemanager/ServiceManager.cpp
相关函数:ServiceManager::addService()
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.");
}只有 remoteBinder() 非空时才向服务 Binder 注册死亡通知;本地 Binder 不需要跨进程死亡接收。ServiceManager 自身作为 DeathRecipient 被注册,意味着服务进程死亡时,驱动发出的死亡事件最终会调用 ServiceManager::binderDied()。
3.2 覆盖策略
auto it = mNameToService.find(name);
bool prevClients = false;
if (it != mNameToService.end()) {
const Service& existing = it->second;
prevClients = existing.hasClients;
ALOGI("Service '%s' ... is being registered again ...", name.c_str());
}
mNameToService[name] = Service{
.binder = binder,
.allowIsolated = allowIsolated,
.dumpPriority = dumpPriority,
.hasClients = prevClients,
.guaranteeClient = false,
.ctx = ctx,
};重复注册不是简单返回“已存在”。新 Service 覆盖旧表项,但保留旧项的 hasClients 状态;同时记录原注册 UID、SID、PID 与新调用者的差异日志。这个设计让同名服务替换可以成功,却仍能暴露多实例或迟到死亡通知等异常线索。
3.3 注册回调
源码文件:frameworks/native/cmds/servicemanager/ServiceManager.cpp
相关函数:dispatchRegistrationCallbacks()
if (auto it = mNameToRegistrationCallback.find(name);
it != mNameToRegistrationCallback.end()) {
mNameToService[name].guaranteeClient = true;
CHECK(handleServiceClientCallback(2 /* sm + transaction */, name, false));
mNameToService[name].guaranteeClient = true;
dispatchRegistrationCallbacks(name, binder, allowIsolated,
/*callbacks=*/it->second);
}注册回调存在时,ServiceManager 先处理 client callback 状态,再向等待该名字的监听者发送 onRegistration(name, binder)。dispatchRegistrationCallbacks() 还会跳过不允许 isolated caller 的回调,因此“服务已写入表”和“每个监听者都收到通知”不是同一件事。
4. 死亡清理
4.1 服务删除
源码文件:frameworks/native/cmds/servicemanager/ServiceManager.cpp
相关函数:ServiceManager::binderDied()
void ServiceManager::binderDied(const wp<IBinder>& who) {
for (auto it = mNameToService.begin();
it != mNameToService.end();) {
if (who == it->second.binder) {
it = mNameToService.erase(it);
} else {
++it;
}
}
for (auto it = mNameToRegistrationCallback.begin();
it != mNameToRegistrationCallback.end();) {
removeRegistrationCallback(who, &it, nullptr);
}
}死亡通知以 Binder 对象匹配服务表项,而不是按服务名猜测。匹配成功会删除名字;同一个死亡对象也可能是注册回调或 client callback 的 owner,因此后续循环继续清理回调表。删除发生在 ServiceManager 进程的对象状态中,驱动 node/ref 的底层释放由 Binder 生命周期另行完成。
4.2 结果状态
| 阶段 | owner | 失败/完成结果 | 消费者 |
|---|---|---|---|
| Java 发起 | 调用线程 | RemoteException 或进入 Binder | Java 调用方 |
| 服务器校验 | ServiceManager Binder 线程 | Status 错误码 | native backend / Java proxy |
| 表项写入 | mNameToService | 名字映射到 Binder 与 metadata | getService、listServices |
| 注册通知 | callback 列表 | onRegistration 逐个发送 | 监听者 |
| 进程死亡 | DeathRecipient | 删除表项和相关 callback | 后续查询、等待者 |
5. 源码验证
5.1 服务端测试
源码文件:frameworks/native/cmds/servicemanager/test_sm.cpp
测试输入和断言包括:
AddService.HappyHappy:普通名字、非空 Binder、默认 dump priority,断言Status::isOk();覆盖成功路径。AddService.EmptyNameDisallowed、TooLongNameDisallowed、WeirdCharactersDisallowed:空名、128 字节名称和$,断言失败;覆盖名称边界。AddService.AddNullServiceDisallowed:传入空 Binder,断言失败;覆盖参数拒绝。AddService.AddDisallowedFromApp:用 app UID 构造调用上下文,并断言canAdd不被调用;覆盖 app UID 在 SELinux 前被拒绝。AddService.OverwriteExistingService:先写入 A 再写入 B,随后通过getService2()断言返回 B;覆盖覆盖策略。
这些测试直接使用可控的 ServiceManager 和 MockAccess,证明服务端状态与错误分支;它们不执行 Java JNI、真实 Binder 驱动对象翻译或设备上的 SELinux policy。
5.2 Fake manager
源码文件:frameworks/native/libs/fakeservicemanager/test_sm.cpp
AddService.HappyHappy、SadNullBinder、HappyOverExistingService 和 HappyClearAddedService 使用 FakeServiceManager,分别断言成功、空 Binder 失败、覆盖和 clear() 后查询为空。它们适合验证客户端接口的返回约定,但不能证明真实 servicemanager 的 UID、VINTF 或死亡通知路径。
5.3 可执行阅读
rg -n "addService|getIServiceManager|ServiceManagerProxy" \
frameworks/base/core/java/android/os/ServiceManager.java \
frameworks/base/core/java/android/os/ServiceManagerNative.java
rg -n "getNativeServiceManager|BackendUnifiedServiceManager::addService" \
frameworks/base/core/jni/android_os_ServiceManagerNative.cpp \
frameworks/native/libs/binder/BackendUnifiedServiceManager.cpp
rg -n "Status ServiceManager::addService|canAddService|linkToDeath|mNameToService|binderDied" \
frameworks/native/cmds/servicemanager/ServiceManager.cpp
rg -n "AddService|OverwriteExistingService|AddDisallowedFromApp" \
frameworks/native/cmds/servicemanager/test_sm.cpp遇到“Java addService 返回但查询不到”的现象,先确认 Java 层是否捕获了 RemoteException,再检查 native Status 是否在真实 ServiceManager 校验阶段失败;若注册曾成功但随后消失,沿 linkToDeath() 到 binderDied() 检查服务 Binder 是否死亡。这样可以把调用失败、校验失败、回调未送达和服务进程退出四类现象分开。
