Skip to content

checkService服务检查

追踪 checkService 的非阻塞查询路径,解释缓存、权限、isolated 访问、lazy 服务不启动和空结果边界。

基于android-17.0.0_r1
AndroidBinderServiceManagercheckService源码阅读

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()

java
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

java
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()

cpp
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()

cpp
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变体 ​

cpp
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()

cpp
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 分支收束 ​

cpp
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 可执行阅读 ​

bash
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 的实现错误,而在调用方选择了明确不启动服务的接口。