Skip to content

getCallingUid与getCallingPid

拆解 getCallingUid/getCallingPid 的 Native、JNI、Java 语义,覆盖非事务、oneway、嵌套和显式身份边界。

基于android-17.0.0_r1
AndroidBinder权限

getCallingUid与getCallingPid ​

getter 文章只解释“当前线程读到什么”,不重复身份字段如何从驱动进入线程,也不讨论 clear/restore token。下面的图显示 native、JNI 和 Java 三层如何读取同一 IPCThreadState。

本文承接 Binder调用者身份 和 checkCallingPermission。BD083 追踪身份字段从驱动到线程状态的流转;本文专门回答 API 使用时最容易混淆的四个问题:何时返回 caller、何时返回自身、为什么 oneway 的 PID 可能是 0、如何用 getCallingUidOrThrow 防止在错误线程读取身份。

1. Native返回 ​

源码文件:frameworks/native/libs/binder/IPCThreadState.cpp

cpp
pid_t IPCThreadState::getCallingPid() const {
    checkContextIsBinderForUse(__func__);
    return mCallingPid;
}

uid_t IPCThreadState::getCallingUid() const {
    checkContextIsBinderForUse(__func__);
    return mCallingUid.has_value() ? mCallingUid.value() : getuid();
}

PID 始终读取线程字段;UID 在 optional 没有值时回退当前进程 getuid()。这两个 getter 不会发起 Binder RPC,也不会重新询问驱动,调用成本只是线程局部状态读取和上下文保护。

2. JNI转发 ​

源码文件:frameworks/base/core/jni/android_util_Binder.cpp

cpp
static jint android_os_Binder_getCallingPid(CRITICAL_JNI_PARAMS) {
    return IPCThreadState::self()->getCallingPid();
}
static jint android_os_Binder_getCallingUid(CRITICAL_JNI_PARAMS) {
    return IPCThreadState::self()->getCallingUid();
}

gBinderMethods 将两个 Java @CriticalNative 方法直接注册到这些函数。Java 层没有复制或缓存 caller 身份;同一线程的 Native 状态就是 Java API 的唯一来源。

3. Java回退 ​

源码文件:frameworks/base/core/java/android/os/Binder.java

文档规定:当前线程不处理入站事务时,getCallingPid() 返回自身 PID,getCallingUid() 返回自身 UID。这个回退便于系统服务代码在本地调用和远程调用两种场景共用,但安全检查若必须拒绝本地路径,应先调用 isDirectlyHandlingTransaction() 或使用 getCallingUidOrThrow()。

4. 事务上下文 ​

Native 在 BR_TRANSACTION 分支进入 Stub 前设置 mCallingPid/mCallingUid,执行结束后恢复旧值。isDirectlyHandlingTransactionNative 通过 getCurrentServingCall() == BinderCallType::BINDER 判断当前线程是否真的在 Binder 回调上下文;Java 的 isDirectlyHandlingTransaction() 还叠加测试 override 标志。

这一区分很重要:线程可能刚从 Binder 回调返回、仍保留显式 identity token,也可能完全不在 Binder 上下文。单看 UID 数值无法判断身份是否可信。

5. Throw保护 ​

Binder.getCallingUidOrThrow() 在“非直接处理事务且没有显式 identity”时抛 IllegalStateException,然后才读取 UID。getCallingUidOrWtf 采取同一判断但记录 wtf 日志。它们把误在 Handler/后台线程使用 caller UID 的错误提前暴露,而普通 getter 会静默返回自身 UID。

6. PID为零 ​

源码文件:kernel/common/drivers/android/binder.c

驱动创建 oneway 事务时不设置 t->from;交付时 binder_get_txn_from(t) 为空,trd->sender_pid 写 0。进程死亡或某些无来源回复路径也可能没有 sender thread。Java 文档因此明确警告:PID 为 0 不能证明请求来自系统或可信组件。

UID 仍可能由 sender_euid 提供,但 oneway 权限检查不应依赖 PID;需要审计调用方时应改用同步事务、UID/权限和 SELinux 上下文组合。

7. 嵌套读取 ​

服务线程处理 A→B 时同步调用 C,再收到 C→B 回调,内层 BR_TRANSACTION 会暂时覆盖 mCallingPid/mCallingUid;内层返回后恢复 B 当前外层 caller。若在内层调用前 clear identity,恢复 token 的层级必须匹配,否则 getter 读取的是显式身份而不是原始 caller。

8. Java业务使用 ​

Binder.getCallingUid() 常用于 UserHandle.getUserId、AppOps、权限检查和日志标记;Binder.clearCallingIdentity() 适合服务代表自己访问本地服务。不要把 getCallingPid 当稳定授权主体,也不要把普通 getter 在非 Binder 线程的自身 UID 当成远端授权结果。

9. 测试边界 ​

源码文件:frameworks/native/libs/binder/tests/binderLibTest.cpp

测试通过 GetCallingUidWithSetuid 验证服务 euid 改变不会修改当前 Binder caller UID;服务代码还比较 getCallingUid() 与 getuid(),覆盖远端 caller 与本地身份分离。Java 层的 getCallingUidOrThrow 契约则专门覆盖错误上下文,而不是验证驱动字段。

10. 排查顺序 ​

先调用 isDirectlyHandlingTransaction() 判断上下文,再记录 PID/UID 是否经历 clear/restore;若 PID 为 0,检查 oneway/无来源事务;若 UID 与预期不符,回到驱动 sender_euid 和 Native mCallingUid 设置点。最后才检查 Java permission/AppOps 层,避免把 API 回退语义误判为驱动身份丢失。

11. 阅读检查 ​

分别给出远程同步、本地直接调用、oneway 远程、clear identity 后调用四种场景的 getCallingPid、getCallingUid、isDirectlyHandlingTransaction 和 getCallingUidOrThrow 结果,并为每个结果指出 Native 或 Java 源码分支。