Binder版本边界
Binder 的“版本”不是一个数字能概括的事实。当前源码同时存在协议版本协商、UAPI 新命令、 运行时 feature 文件、libbinder fallback、Parcel 请求头和驱动对象扩展。
本文面向已经读过 Binder协议状态机 和 事务数据生命周期 的读者。问题边界是“如何判断一项 Binder 行为变化是否需要升级两端”,不把归档稿的 Android 年表直接当作证据。当前行为以固定 tag 源码为准;历史首次引入必须另查对应 commit/tag。
1. 三类变化
协议数字
源码文件:kernel/common/include/uapi/linux/android/binder.h
#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扩展
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
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
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
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
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
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修复 | 保留旧布局,新增 wrapper | internal header、历史 diff |
“当前代码包含符号”只能证明当前存在,不能证明首次引入版本。历史章节必须使用对应 project 的 commit/tag 和首个消费者重新取证。
Parcel请求头
源码文件:frameworks/native/libs/binder/Parcel.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
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
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 代码时依次检查:
- BINDER_VERSION 是否仍匹配;
- 新 ioctl/BC/BR 是否有 feature gate;
- 旧用户态遇到新返回命令如何处理;
- 新结构是否通过扩展而非破坏旧布局;
- 测试是否覆盖能力关闭、命令失败和旧设备 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 边界。
