checkService服务检查
checkService() 的核心保证是“只检查当前是否可得,不为缺失服务启动进程,也不等待它注册”。Android 17 中,这个保证由多个层次共同形成:Java 先查 sCache,native backend 再查自己的缓存,ServiceManager 服务端最终以 startIfNotFound=false 调用 tryGetBinder()。服务存在时仍要经过 isolated 与 SELinux find 判断;服务不存在时直接返回空,不触发 ctl.interface_start。
本文承接 getService查询服务 和 addService注册服务。前文解释了查询缓存、lazy 启动和服务表写入;本文只把 checkService 的非阻塞、非启动边界单独拆出来,并对比 getService 的差异。不展开 waitForService() 的等待循环,也不把“没有启动”误写成“没有权限检查”。
1. 调用入口
1.1 Java方法
源码文件:frameworks/base/core/java/android/os/ServiceManager.java
相关函数:ServiceManager.checkService()
public static IBinder checkService(String name) {
try {
IBinder service = sCache.get(name);
if (service != null) {
return service;
} else {
return Binder.allowBlocking(
getIServiceManager().checkService2(name)
.getServiceWithMetadata().service);
}
} catch (RemoteException e) {
Log.e(TAG, "error in checkService", e);
return null;
}
}Java 的“非阻塞”首先意味着不等待 lazy 服务启动;它不意味着不做任何工作。cache miss 仍然会同步发起一次 Binder 调用,等待 ServiceManager 返回当前结果。远端异常和不存在服务在这个包装层都可能表现为 null。
1.2 代理方法
源码文件:frameworks/base/core/java/android/os/ServiceManagerNative.java
相关类型:ServiceManagerProxy
public IBinder checkService(String name) throws RemoteException {
return checkService2(name).getServiceWithMetadata().service;
}
public Service checkService2(String name) throws RemoteException {
return mServiceManager.checkService2(name);
}代理把旧的 Binder 返回类型包装成 Service metadata 变体,再取出 service 字段。这里没有等待、轮询或启动调用;是否启动由服务端的布尔参数决定。
1.3 Native缓存
源码文件:frameworks/native/libs/binder/BackendUnifiedServiceManager.cpp
相关函数:BackendUnifiedServiceManager::checkService2()
Status BackendUnifiedServiceManager::checkService2(
const std::string& name, os::Service* out) {
os::Service service;
if (returnIfCached(name, out)) {
return Status::ok();
}
Status status = Status::ok();
if (mTheRealServiceManager) {
status = mTheRealServiceManager->checkService2(name, &service);
}
if (status.isOk()) {
status = toBinderService(name, service, out);
if (status.isOk()) {
return updateCache(name, service);
}
}
return status;
}native cache 命中会直接返回;未命中才进入真实 ServiceManager。缓存只改变是否跨进程访问,不改变服务端“缺失时不启动”的契约。
2. 服务端路径
2.1 布尔开关
源码文件:frameworks/native/cmds/servicemanager/ServiceManager.cpp
相关函数:checkService()、checkService2()、tryGetService()
Status ServiceManager::checkService(
const std::string& name, sp<IBinder>* outBinder) {
*outBinder = tryGetBinder(name, false).service;
return Status::ok();
}
Status ServiceManager::checkService2(
const std::string& name, os::Service* outService) {
*outService = tryGetService(name, false);
return Status::ok();
}false 是关键:它被传给 tryGetService() 的 startIfNotFound,而不是一个“是否阻塞线程”的线程调度标志。两个 legacy 接口即使返回空也报告 Status::ok(),调用者必须检查输出对象。
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 tag;vendor ServiceManager 构建跳过这段解析。checkService 不因为 accessor 分支而变成等待调用,布尔参数会继续传入底层 tryGetBinder()。
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)) {
return os::ServiceWithMetadata();
}
out = service->binder;
}
if (!mAccess->canFind(ctx, name)) {
return os::ServiceWithMetadata();
}服务不存在时仍会调用 canFind();权限检查与是否启动是两个独立维度。服务存在但 isolated caller 不被允许时会提前返回空,服务不存在或权限拒绝都不会进入启动分支。
3.2 分支收束
if (!out && startIfNotFound) {
tryStartService(ctx, name);
}
if (out) {
service->guaranteeClient = true;
CHECK(handleServiceClientCallback(
2 /* sm + transaction */, name, false));
service->guaranteeClient = true;
}
os::ServiceWithMetadata result;
result.service = out;
result.isLazyService = service
? service->dumpPriority & FLAG_IS_LAZY_SERVICE
: false;
return result;对 checkService 而言 startIfNotFound 恒为 false,因此 tryStartService() 不会被执行;已有 Binder 仍会触发 client callback 状态处理,并返回 lazy metadata。非启动检查不是“绕过服务端逻辑”,而是只跳过缺失服务的 init 请求。
4. 结果边界
4.1 空值含义
| 现象 | 说明 | 是否启动 |
|---|---|---|
Java sCache 命中 | 本进程已有 Binder | 否 |
| native cache 命中 | backend 已缓存结果 | 否 |
表无服务且有 find 权限 | 当前没有可用 Binder | 否 |
| isolated 不被允许 | 服务存在但调用者被过滤 | 否 |
SELinux find 拒绝 | 不能暴露服务 | 否 |
| 远端异常 | Java 包装返回 null | 否 |
空值不能单独证明“服务从未注册”:它也可能是 cache 尚未更新、服务已死亡、权限拒绝或 isolated 过滤。checkService 不会通过启动动作修复这些状态。
4.2 时序恢复
checkService 没有等待状态,也没有由本次调用拥有的取消句柄。若服务稍后由其他路径注册,下一次 checkService 才会重新读取当前表或缓存;若服务进程死亡,binderDied() 删除表项后,后续检查重新返回空。需要“缺失时启动并等到可用”的调用者应使用专门的 waitForService(),不能在 checkService 外层假设一次 null 会自动重试。
5. 测试边界
5.1 成功检查
源码文件:frameworks/native/cmds/servicemanager/test_sm.cpp
GetService.HappyHappy 先注册一个 Binder,再同时调用 getService2()、legacy getService()、checkService2() 和 checkService(),分别断言四个输出都指向同一个 Binder。它证明已有表项的检查路径不会因为 startIfNotFound=false 而丢失服务。
5.2 缺失与权限
同一测试文件的 GetService.NonExistant 断言缺失服务的 Status 仍为 ok、输出 Binder 为空;GetService.NoPermissionsForGettingService 让 canFind() 返回 false,断言已注册服务也返回空;GetService.NotAllowedFromIsolated 用 isolated UID 和 allowIsolated=false,断言返回空。测试输入分别覆盖“不存在”“无 find 权限”“isolated 被拒绝”,不能把三者归为同一种失败。
这些 ServiceManager 测试使用 mock access 和进程内对象,不执行 Java cache、native backend cache、真实 Binder 驱动或 init 属性。它们证明服务端分支,不证明设备上的启动时序。
5.3 可执行阅读
rg -n "checkService|checkService2|sCache" \
frameworks/base/core/java/android/os/ServiceManager.java \
frameworks/base/core/java/android/os/ServiceManagerNative.java
rg -n "BackendUnifiedServiceManager::checkService2|returnIfCached|updateCache" \
frameworks/native/libs/binder/BackendUnifiedServiceManager.cpp
rg -n "tryGetService|tryGetBinder|startIfNotFound|tryStartService|canFind|allowIsolated" \
frameworks/native/cmds/servicemanager/ServiceManager.cpp
rg -n "GetService\.HappyHappy|NonExistant|NoPermissionsForGettingService|NotAllowedFromIsolated" \
frameworks/native/cmds/servicemanager/test_sm.cpp排查 checkService() 返回空时,先确认是否命中任一缓存,再判断服务表是否有 Binder,随后区分 isolated 和 SELinux find。如果期望缺失服务被拉起,问题不在 checkService 的实现错误,而在调用方选择了明确不启动服务的接口。
