getService查询服务
getService() 的结果不是简单的“按名字从 map 取 Binder”。Android 17 的 ServiceManager 会先区分 legacy getService 与 getService2,再根据调用者身份检查 isolated 访问和 SELinux find 权限;服务不存在且调用方允许启动时,还会异步请求 init 启动 lazy AIDL 服务。服务存在时,查询过程还会更新 client callback 状态,并把 isLazyService metadata 一起返回给 native backend。
本文面向已经读过 addService注册服务、SM成为context_manager 和 Binder事务发送 的读者。本文回答一个具体问题:一次查询从 Java ServiceManager.getService() 如何到达 ServiceManager::tryGetBinder(),服务表、权限、lazy 启动和缓存分别由谁拥有、何时生效。本文不展开 waitForService() 的阻塞重试协议,也不把 Java sCache 与 native backend cache 当作同一个缓存。
1. 入口分层
1.1 Java查询
源码文件:frameworks/base/core/java/android/os/ServiceManager.java
相关函数:getService()、rawGetService()
public static IBinder getService(String name) {
try {
IBinder service = sCache.get(name);
if (service != null) {
return service;
} else {
return Binder.allowBlocking(rawGetService(name));
}
} catch (RemoteException e) {
Log.e(TAG, "error in getService", e);
}
return null;
}
private static IBinder rawGetService(String name) throws RemoteException {
final IBinder binder = getIServiceManager().getService2(name)
.getServiceWithMetadata().service;
return binder;
}Java 层先查 sCache,命中时不会进入 Binder;未命中才调用 rawGetService()。因此“ServiceManager 服务表中已有服务”并不等于每个 Java 调用都会穿过 ServiceManager 进程。RemoteException 或返回的空 Binder 最终都表现为 null,调用者需要结合日志和后端状态进一步区分原因。
1.2 AIDL代理
源码文件:frameworks/base/core/java/android/os/ServiceManagerNative.java
相关类型:ServiceManagerProxy
public IBinder getService(String name) throws RemoteException {
return checkService2(name).getServiceWithMetadata().service;
}
public Service getService2(String name) throws RemoteException {
return checkService2(name);
}
public Service checkService2(String name) throws RemoteException {
return mServiceManager.checkService2(name);
}Android 17 的旧 getService() 代理最终调用 checkService2();真正决定是否尝试 lazy 启动的是服务端方法是否使用 startIfNotFound=true。Java 代理的命名不能直接当作服务端语义:服务端 legacy getService() 与 getService2() 都走 tryGetService(name, true),而 checkService2() 走 false。
1.3 Native缓存
源码文件:frameworks/native/libs/binder/BackendUnifiedServiceManager.cpp
相关函数:getService2()、checkService2()
Status BackendUnifiedServiceManager::getService2(
const std::string& name, os::Service* out) {
if (returnIfCached(name, out)) {
return Status::ok();
}
os::Service service;
Status status = Status::ok();
if (mTheRealServiceManager) {
status = mTheRealServiceManager->getService2(name, &service);
}
if (status.isOk()) {
status = toBinderService(name, service, out);
if (status.isOk()) {
return updateCache(name, service);
}
}
return status;
}BackendUnifiedServiceManager 的 cache 位于 native backend,命中后直接返回;未命中才调用真实 ServiceManager。checkService2() 也有相同的 cache 入口,但服务端使用的 startIfNotFound 不同。native cache 的更新发生在结果转换成功之后,不能把 cache 命中误解成服务端再次执行权限检查。
2. 服务端分叉
2.1 Legacy接口
源码文件:frameworks/native/cmds/servicemanager/ServiceManager.cpp
相关函数:getService()、getService2()、checkService()、checkService2()
Status ServiceManager::getService(const std::string& name,
sp<IBinder>* outBinder) {
*outBinder = tryGetBinder(name, true).service;
return Status::ok();
}
Status ServiceManager::getService2(const std::string& name,
os::Service* outService) {
*outService = tryGetService(name, true);
return Status::ok();
}
Status ServiceManager::checkService2(const std::string& name,
os::Service* outService) {
*outService = tryGetService(name, false);
return Status::ok();
}四个接口最终都返回 Status::ok(),即使服务不存在或权限导致结果为空;查询结果放在输出参数或 os::Service 变体中。这是 legacy 兼容语义,不能用 Binder Status 是否为 ok 判断服务是否找到。真正的结果要看输出 Binder、service tag 和 metadata。
2.2 Accessor分支
os::Service ServiceManager::tryGetService(const std::string& name,
bool startIfNotFound) {
std::optional<std::string> accessorName;
#ifndef VENDORSERVICEMANAGER
accessorName = getVintfAccessorName(name);
#endif
if (accessorName.has_value()) {
auto ctx = mAccess->getCallingContext();
if (!mAccess->canFind(ctx, name)) {
return os::Service::make<os::Service::Tag::accessor>(nullptr);
}
return os::Service::make<os::Service::Tag::accessor>(
tryGetBinder(*accessorName, startIfNotFound).service);
}
return os::Service::make<os::Service::Tag::serviceWithMetadata>(
tryGetBinder(name, startIfNotFound));
}VINTF 声明的服务可能通过 accessor 变体返回,而不是直接返回普通 serviceWithMetadata。调用者先对原始名字做 find 权限检查,再查询 accessor 名字;VENDORSERVICEMANAGER 构建条件会跳过 accessor 解析。这个 tag 差异是 native backend 需要调用 toBinderService() 转换的原因之一。
3. 查询状态
3.1 表查找
源码文件:frameworks/native/cmds/servicemanager/ServiceManager.cpp
相关函数:tryGetBinder()
auto ctx = mAccess->getCallingContext();
sp<IBinder> out;
Service* service = nullptr;
if (auto it = mNameToService.find(name);
it != mNameToService.end()) {
service = &(it->second);
if (!service->allowIsolated &&
is_multiuser_uid_isolated(ctx.uid)) {
LOG(WARNING) << "Isolated app requested '" << name << "'";
return os::ServiceWithMetadata();
}
out = service->binder;
}表查找先得到 Service*,再检查 isolated UID 是否被该服务允许。服务存在但 isolated caller 不被允许时,函数立即返回空结果,后面的 canFind() 和 lazy 启动都不会执行。这个顺序意味着“服务名存在”不是“当前调用者可获得 Binder”。
3.2 SELinux查找
if (!mAccess->canFind(ctx, name)) {
return os::ServiceWithMetadata();
}
if (!out && startIfNotFound) {
tryStartService(ctx, name);
}服务不存在时仍必须先通过 find 权限检查,只有权限通过且 startIfNotFound 为 true 才会请求 lazy 启动。权限失败返回空对象,不会因为服务缺失而启动一个调用者无权访问的服务。
3.3 Client状态
if (out) {
service->guaranteeClient = true;
CHECK(handleServiceClientCallback(
2 /* sm + transaction */, name, false));
service->guaranteeClient = true;
}
os::ServiceWithMetadata serviceWithMetadata;
serviceWithMetadata.service = out;
serviceWithMetadata.isLazyService = service
? service->dumpPriority & FLAG_IS_LAZY_SERVICE
: false;
return serviceWithMetadata;成功查询会把 ServiceManager 自己和当前 Binder transaction 计入已知 client 数量,并重新设置 guaranteeClient,避免定时器过早清除状态。最后根据注册时的 dumpPriority 返回 isLazyService metadata;这个位和 dump 显示优先级共享整数参数,但服务端把它解释为 lazy 标志。
4. Lazy启动
4.1 属性请求
源码文件:frameworks/native/cmds/servicemanager/ServiceManager.cpp
相关函数:tryStartService()
void ServiceManager::tryStartService(
const Access::CallingContext& ctx,
const std::string& name) {
std::thread([=] {
if (!base::SetProperty(
"ctl.interface_start", "aidl/" + name)) {
ALOGI("Tried to start aidl service %s, but was unable to.",
name.c_str());
}
}).detach();
}启动请求在 detached thread 中执行,并通过 ctl.interface_start 交给 init;查询线程不会同步等待服务变为可用。因此 getService() 对未找到服务的返回仍可能是空,即使 init 随后成功拉起服务。只有专门的等待接口才承担“启动后等待 ready”的语义。
4.2 两种查询
| 调用 | startIfNotFound | 缺失时动作 | 典型结果 |
|---|---|---|---|
getService / getService2 | true | 异步请求 lazy AIDL 启动 | 当前调用仍可能得到空 |
checkService / checkService2 | false | 不启动 | 直接得到空 |
waitForService | 独立 native 路径 | 启动并等待 | 见等待专题 |
5. 失败边界
5.1 结果分类
| 现象 | 源码位置 | 是否触发 lazy 启动 |
|---|---|---|
Java sCache 命中 | ServiceManager.getService() | 否 |
| 服务存在且允许访问 | tryGetBinder() | 否 |
| isolated caller 不被允许 | tryGetBinder() | 否 |
SELinux find 拒绝 | tryGetBinder() | 否 |
服务不存在,getService | tryGetBinder() | 是,异步 |
服务不存在,checkService | tryGetBinder() | 否 |
| backend 无真实 manager | BackendUnifiedServiceManager | 由 backend 返回错误 |
5.2 取消与恢复
查询本身没有可取消的等待状态:getService() 只是一次调用,lazy 启动请求在 detached thread 中提交后不由这次 Binder 调用持有。恢复路径由后续查询或 waitForService() 重新观察服务表;服务进程注册成功后,addService() 会写入表并触发注册回调。若服务进程死亡,前文所述 binderDied() 删除表项,下一次允许启动的查询又可能重新请求 lazy 服务。
6. 源码测试
6.1 服务端测试
源码文件:frameworks/native/cmds/servicemanager/test_sm.cpp
GetService.HappyHappy 先注册 Binder,再调用 getService2() 与 legacy getService(),断言返回的是同一个 Binder;GetService.NonExistent 断言不存在服务返回空;GetService.IsolatedNotAllowed 使用 isolated UID 且服务不允许 isolated,断言结果为空。这些测试覆盖表查找、返回对象和 isolated 分支,但不直接断言真实 init 属性启动。
6.2 Fake缓存测试
源码文件:frameworks/native/libs/fakeservicemanager/test_sm.cpp
SetupFakeServiceManager.GetExistingService 验证 fake manager 注册后查询得到同一 Binder;ClearFakeServiceManager.GetServiceAfterClear 注册后清空 fake manager,再断言查询为空。它们证明客户端接口可以观察到“写入/清理”状态,不证明真实 ServiceManager 的 SELinux、VINTF、lazy 属性或死亡通知。
6.3 可执行阅读
rg -n "getService\(|rawGetService|sCache|checkService2" \
frameworks/base/core/java/android/os/ServiceManager.java \
frameworks/base/core/java/android/os/ServiceManagerNative.java
rg -n "getService2|checkService2|returnIfCached|updateCache" \
frameworks/native/libs/binder/BackendUnifiedServiceManager.cpp
rg -n "tryGetService|tryGetBinder|startIfNotFound|tryStartService|canFind|guaranteeClient" \
frameworks/native/cmds/servicemanager/ServiceManager.cpp
rg -n "GetService|IsolatedNotAllowed|ClearFakeServiceManager" \
frameworks/native/cmds/servicemanager/test_sm.cpp \
frameworks/native/libs/fakeservicemanager/test_sm.cpp当 getService() 返回空时,先问四个问题:Java cache 是否命中、服务表是否有名字、调用者是否通过 isolated/SELinux 检查、这次查询是否允许 lazy 启动。只有沿这四个分叉定位,才能把“服务不存在”“无权限”“启动尚未完成”和“缓存/后端问题”区分开。
