Skip to content

Binder版本边界

基于真实历史提交与当前源码,区分 Binder 协议版本、UAPI 扩展、驱动特性协商和 libbinder 行为变化。

基于android-17.0.0_r1
AndroidBinderUAPI版本演进源码阅读

Binder版本边界 ​

Binder 的“版本”不是一个数字能概括的事实。当前源码同时存在协议版本协商、UAPI 新命令、 运行时 feature 文件、libbinder fallback、Parcel 请求头和驱动对象扩展。

本文面向已经读过 Binder协议状态机 和 事务数据生命周期 的读者。问题边界是“如何判断一项 Binder 行为变化是否需要升级两端”,不把归档稿的 Android 年表直接当作证据。当前行为以固定 tag 源码为准;历史首次引入必须另查对应 commit/tag。

1. 三类变化 ​

协议数字 ​

源码文件:kernel/common/include/uapi/linux/android/binder.h

c
#ifdef BINDER_IPC_32BIT
#define BINDER_CURRENT_PROTOCOL_VERSION 7
#else
#define BINDER_CURRENT_PROTOCOL_VERSION 8
#endif

struct binder_version {
    __s32 protocol_version;
};

这个数字用于基础 ABI 协商,不等价于“每增加一个 ioctl 就增加 1”。32 位和 64 位构建还可 使用不同值;文章不能只列一个 Android 版本号替代 UAPI 检查。

UAPI扩展 ​

c
enum {
    BINDER_WRITE_READ = _IOWR('b', 1,
            struct binder_write_read),
    BINDER_VERSION = _IOWR('b', 9,
            struct binder_version),
    BINDER_SET_CONTEXT_MGR_EXT = _IOW('b', 13,
            struct flat_binder_object),
    BINDER_FREEZE = _IOW('b', 14,
            struct binder_freeze_info),
    BINDER_GET_FROZEN_INFO = _IOWR('b', 15,
            struct binder_frozen_status_info),
    BINDER_GET_EXTENDED_ERROR = _IOWR('b', 17,
            struct binder_extended_error),
};

新增能力通常通过新命令或扩展结构进入,而不是改变旧结构字段顺序。兼容性问题要同时看 旧用户态遇到新命令、新用户态遇到旧驱动时的返回和 fallback。

运行时能力 ​

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

cpp
bool ProcessState::isDriverFeatureEnabled(
        const DriverFeature feature) {
    if (feature == DriverFeature::ONEWAY_SPAM_DETECTION)
        return readDriverFeatureFile(
                DRIVER_FEATURES_PATH
                "oneway_spam_detection");
    if (feature == DriverFeature::EXTENDED_ERROR)
        return readDriverFeatureFile(
                DRIVER_FEATURES_PATH
                "extended_error");
    if (feature == DriverFeature::FREEZE_NOTIFICATION)
        return readDriverFeatureFile(
                DRIVER_FEATURES_PATH
                "freeze_notification");
    return false;
}

feature 文件描述运行时能力,和 protocol version 是两层协商。相同协议数字不保证同一内核 配置启用了所有 feature。

2. 启动协商 ​

驱动版本 ​

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

cpp
int vers = 0;
int result = ioctl(fd.get(),
        BINDER_VERSION, &vers);
if (result != 0) {
    error->appendFormat(
            "Binder ioctl to obtain version failed");
    return {};
}
if (vers != BINDER_CURRENT_PROTOCOL_VERSION) {
    error->appendFormat(
            "Binder driver protocol(%d) does not match "
            "user space protocol(%d)",
            vers, BINDER_CURRENT_PROTOCOL_VERSION);
    return {};
}

版本不匹配会在 ProcessState 初始化阶段失败,而不是等第一笔业务事务才发现。这个检查只 证明基础 ABI 配对,不证明可选 feature。

能力开关 ​

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

cpp
uint32_t enable =
        DEFAULT_ENABLE_ONEWAY_SPAM_DETECTION;
result = ioctl(mDriverFD,
        BINDER_ENABLE_ONEWAY_SPAM_DETECTION,
        &enable);
if (result == -1 &&
        isDriverFeatureEnabled(
                DriverFeature::ONEWAY_SPAM_DETECTION)) {
    ALOGI("Binder ioctl to enable oneway spam detection failed");
}

用户态会尝试启用能力,但失败处理取决于驱动是否声明支持。源码出现 ioctl 不等于设备一定 具备该能力。

3. 当前特性 ​

Oneway检测 ​

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

c
case BINDER_ENABLE_ONEWAY_SPAM_DETECTION: {
    uint32_t enable;
    if (copy_from_user(&enable,
            ubuf, sizeof(enable))) {
        ret = -EFAULT;
        goto err;
    }
    binder_inner_proc_lock(proc);
    proc->oneway_spam_detection_enabled =
            (bool)enable;
    binder_inner_proc_unlock(proc);
    break;
}

ioctl 只设置 proc 状态;真正的 BR_ONEWAY_SPAM_SUSPECT 还要经过异步 buffer 和事务路径。开关、 检测条件和用户态告警是三个不同层次。

冻结通知 ​

UAPI 包含 BINDER_FREEZE、BR_FROZEN_BINDER、BC_FREEZE_NOTIFICATION_DONE 等命令。libbinder 只有在 feature 文件表明支持时才使用对应回调。冻结不是死亡:冻结目标仍可能有 node/ref, 事务结果可能是 BR_FROZEN_REPLY 或 BR_TRANSACTION_PENDING_FROZEN。

扩展错误 ​

binder_extended_error 通过专用 ioctl 读取线程最近错误,不能与 BR_FAILED_REPLY 混成同一个 错误对象。判断兼容性要同时检查 UAPI 命令、驱动是否填充和 libbinder 是否读取。

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

cpp
void IPCThreadState::logExtendedError() {
    binder_extended_error error = {
            .command = BR_OK};
    if (!ProcessState::isDriverFeatureEnabled(
            ProcessState::DriverFeature::EXTENDED_ERROR)) {
        return;
    }
    if (ioctl(mProcess->mDriverFD,
            BINDER_GET_EXTENDED_ERROR, &error) < 0) {
        return;
    }
    ALOGE_IF(error.command != BR_OK,
            "Binder failure. id: %d, cmd: %d, error: %d",
            error.id, error.command, error.param);
}

驱动在线程状态中保存扩展错误,libbinder 只有检测到 feature 后才读取并清零。因此 UAPI 有 结构体不等于用户态一定消费了它。

4. 兼容策略 ​

新命令 ​

变化兼容方式需要检查
新 ioctl新编号,旧驱动返回错误UAPI、binder_ioctl、调用方 fallback
新 BC/BR新命令值UAPI、write/read switch、default
新对象新 type 或扩展结构binder_get_object、Parcel 支持
新运行策略feature 文件/开关ProcessState、proc 字段
ABI修复保留旧布局,新增 wrapperinternal header、历史 diff

“当前代码包含符号”只能证明当前存在,不能证明首次引入版本。历史章节必须使用对应 project 的 commit/tag 和首个消费者重新取证。

Parcel请求头 ​

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

cpp
writeInt32(threadState->getStrictModePolicy()
        | STRICT_MODE_PENALTY_GATHER);
updateWorkSourceRequestHeaderPosition();
writeInt32(threadState->shouldPropagateWorkSource()
        ? threadState->getCallingWorkSourceUid()
        : IPCThreadState::kUnsetWorkSource);
writeInt32(kHeader);

请求头也会随框架演进增加字段或校验。enforceInterface 必须按当前 header 读取后再读 descriptor;不能用旧版“只写字符串 token”假设手写 Stub。

5. 用户态fallback ​

返回命令 ​

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

cpp
case BR_FROZEN_REPLY:
    err = enableFrozenObjectErrorCode()
            ? FROZEN_OBJECT
            : FAILED_TRANSACTION;
    goto finish;

case BR_TRANSACTION_PENDING_FROZEN:
    ALOGW("Sending oneway calls to frozen process.");
    goto finish;

default:
    err = executeCommand(cmd);
    break;

同一返回命令的最终 Java/native 错误可能受 feature 或编译配置影响。旧用户态兼容策略不能 被推断为新用户态完整语义。

冻结回调 ​

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

cpp
status_t IPCThreadState::addFrozenStateChangeCallback(
        int32_t handle, BpBinder* proxy) {
    if (!ProcessState::isDriverFeatureEnabled(
            ProcessState::DriverFeature::FREEZE_NOTIFICATION)) {
        return INVALID_OPERATION;
    }
    proxy->getWeakRefs()->incWeak(proxy);
    mOut.writeInt32(BC_REQUEST_FREEZE_NOTIFICATION);
    mOut.writeInt32(handle);
    mOut.writePointer((uintptr_t)proxy);
    return flushCommands();
}

case BR_FROZEN_BINDER: {
    binder_frozen_state_info info;
    mIn.read(&info, sizeof(info));
    BpBinder* proxy = (BpBinder*)info.cookie;
    proxy->getPrivateAccessor()
            .onFrozenStateChanged(info.is_frozen);
    mOut.writeInt32(BC_FREEZE_NOTIFICATION_DONE);
    mOut.writePointer(info.cookie);
    break;
}

freeze notification 同时需要 feature gate、weak 引用、BC 请求、BR 通知和 DONE 回写;单独看到 一个 UAPI 命令不能证明整条功能链已完成。

对象边界 ​

当前对象 UAPI 通过公共 header 加 type 分流 flat、fd、PTR 和 FDA;新对象不应偷偷改变旧 union 解释。跨版本读取对象时,先看 type 和完整对象大小。

6. 历史证据 ​

历史证据 ​

要写 Android 8、11、12 或 16 的“首次引入”,至少需要:

  • 对应 project 的 tag 或 commit;
  • 变更前后的 UAPI/驱动/libbinder diff;
  • 首个真正消费者或测试;
  • 旧用户态/旧驱动的 fallback 行为。

当前 tag 的代码只能证明“现在仍存在”,不能自动证明“当年首次加入”。

不能外推 ​

以下说法不能仅凭当前源码成立:

  • Android 8 一定首次引入 scatter-gather;
  • Android 11 所有设备都默认开启 oneway spam;
  • Android 12 的扩展错误对所有旧驱动可用;
  • 协议版本 8 代表所有 UAPI feature 都支持。

7. 测试输入 ​

版本检查 ​

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

相关测试:Version()、VersionNull()

测试向 BINDER_VERSION 传入合法结构和空指针,分别断言协议版本或 EFAULT/EINVAL。它证明 外层版本 ioctl 的输入边界,不证明可选 feature 开启。

配置命令 ​

相关测试:SetMaxThreads()、SetContextMgrBusy()、ThreadExit()

这些测试覆盖配置 ioctl、context manager 已占用和线程退出后的重新进入;它们证明命令级兼容和 错误返回,不证明不同内核配置下全部命令可用。

事务兼容 ​

相关测试:Transaction()、RequestDeathNotification()

测试构造当前 UAPI 的 transaction_data、BC/BR 序列、死亡通知结构和 read_consumed 断言。它 证明当前协议布局和握手顺序,不能回溯历史首次出现时间。

8. 升级判断 ​

升级 Binder 代码时依次检查:

  1. BINDER_VERSION 是否仍匹配;
  2. 新 ioctl/BC/BR 是否有 feature gate;
  3. 旧用户态遇到新返回命令如何处理;
  4. 新结构是否通过扩展而非破坏旧布局;
  5. 测试是否覆盖能力关闭、命令失败和旧设备 fallback。

一个新命令没有驱动 switch、libbinder 读取路径、framework consumer 和测试,就只能说源码中 出现了符号,不能说平台完整支持该特性。

9. 阅读边界 ​

能力分层 ​

协议版本、运行时 feature、Parcel header 和用户态 fallback 是四种不同兼容边界;升级判断必须沿这条 链确认“新字段由谁消费”,不能只比较一个整数版本。

本文证明了 Binder 协议版本、UAPI 扩展、运行时 feature gate、Parcel header、历史复核方法和 测试边界。没有给出未经逐 tag 验证的 Android 年表,也没有展开 allocator、对象引用、线程池、 死亡通知、RPC Binder 和 SELinux 的完整演进。

继续阅读 Binder协议状态机 复习当前 BC/BR 状态,再回到 Binder对象编码 查看新对象 type 如何保持旧 union 边界。