transact发送
Native Binder 的 transact() 不是一次函数调用,而是把目标 handle、事务 code、Parcel 数据和 flags 编成 binder_transaction_data,写入线程的输出 Parcel,再通过 BINDER_WRITE_READ 交给驱动。同步调用还要继续消费 BR_REPLY;oneway 调用只提交事务,不等待 reply。发送成功只代表驱动接受了事务,不代表服务端业务已经完成。
本文面向已经读过 IPCThreadState线程绑定、Binder事务发送 和 BINDER_WRITE_READ命令 的读者。本文聚焦 native IPCThreadState::transact() 的用户态主线、flags、同步/oneway 分叉和错误返回;驱动目标解析、buffer 分配与对象翻译沿用驱动专题的边界。
1. 入口参数
1.1 transact
源码文件:frameworks/native/libs/binder/IPCThreadState.cpp
相关函数:IPCThreadState::transact()
status_t IPCThreadState::transact(
int32_t handle, uint32_t code,
const Parcel& data, Parcel* reply,
uint32_t flags) {
LOG_ALWAYS_FATAL_IF(data.isForRpc(),
"Parcel constructed for RPC, "
"but being used with binder.");
flags |= TF_ACCEPT_FDS;
status_t err = writeTransactionData(
BC_TRANSACTION, flags, handle, code,
data, nullptr);
if (err != NO_ERROR) {
if (reply) reply->setError(err);
return (mLastError = err);
}
...
}Binder transaction 强制加上 TF_ACCEPT_FDS,表示目标端可以接受文件描述符。RPC Parcel 不能混入 kernel Binder;这是对象传输协议边界,不是业务 code 校验。
1.2 flags分叉
if ((flags & TF_ONE_WAY) == 0) {
if (reply) {
err = waitForResponse(reply);
} else {
Parcel fakeReply;
err = waitForResponse(&fakeReply);
}
} else {
err = waitForResponse(nullptr);
}同步调用需要等待 reply;即使调用者没有提供 reply 指针,libbinder 仍可能使用临时 Parcel 消费驱动返回。oneway 不要求业务 reply,但仍要处理驱动层返回的失败状态。
2. 写入事务
2.1 结构填充
源码文件:frameworks/native/libs/binder/IPCThreadState.cpp
相关函数:writeTransactionData()
binder_transaction_data tr;
tr.target.ptr = 0;
tr.target.handle = handle;
tr.code = code;
tr.flags = binderFlags;
tr.cookie = 0;
tr.sender_pid = 0;
tr.sender_euid = 0;
const status_t err = data.errorCheck();
if (err == NO_ERROR) {
tr.data_size = data.ipcDataSize();
tr.data.ptr.buffer = data.ipcData();
tr.offsets_size = data.ipcObjectsCount()
* sizeof(binder_size_t);
tr.data.ptr.offsets = data.ipcObjects();
}发送方只填充目标 handle、code、flags 和 Parcel 的数据/offsets 指针;sender pid/euid 由驱动和协议处理,不能从这里推导调用者身份。data.errorCheck() 失败时,事务不会进入输出队列。
2.2 输出队列
writeTransactionData() 把命令和 binder_transaction_data 写入 mOut。此时数据仍在用户态 IPCThreadState 输出 Parcel 中,目标进程还没有收到事务;真正提交发生在后续 talkWithDriver()。
3. 驱动提交
3.1 talkWithDriver
源码文件:frameworks/native/libs/binder/IPCThreadState.cpp
相关函数:talkWithDriver()
status_t IPCThreadState::talkWithDriver(bool doReceive) {
if (mProcess->mDriverFD < 0) return -EBADF;
binder_write_read bwr{};
const bool needRead =
mIn.dataPosition() >= mIn.dataSize();
const size_t outAvail =
(!doReceive || needRead) ? mOut.dataSize() : 0;
bwr.write_size = outAvail;
bwr.write_buffer = (uintptr_t)mOut.data();
bwr.read_size = doReceive ? mIn.dataCapacity() : 0;
bwr.read_buffer = doReceive
? (uintptr_t)mIn.data() : 0;
if (ioctl(mProcess->mDriverFD,
BINDER_WRITE_READ, &bwr) >= 0) {
...
return NO_ERROR;
}
return -errno;
}BINDER_WRITE_READ 可以同时写出 mOut 和读入 mIn。doReceive=false 时只提交输出;同步等待通常允许读回命令。若 mIn 仍有未消费数据,函数会避免覆盖输入缓冲。
3.2 输出消费
成功 ioctl 后,mOut 根据 write_consumed 前移;如果全部消费,输出 Parcel 可继续承载下一条命令。驱动返回的 BR_* 命令进入 mIn,由 waitForResponse() 或线程循环消费,不是 transact() 直接调用服务对象。
4. 同步回复
4.1 等待循环
源码文件:frameworks/native/libs/binder/IPCThreadState.cpp
相关函数:waitForResponse()
while (true) {
status_t err = talkWithDriver();
if (err < NO_ERROR) break;
err = mIn.errorCheck();
if (err < NO_ERROR) break;
if (mIn.dataAvail() == 0) continue;
uint32_t cmd = mIn.readInt32();
switch (cmd) {
case BR_REPLY:
...
return NO_ERROR;
case BR_FAILED_REPLY:
err = FAILED_TRANSACTION;
goto finish;
default:
err = executeCommand(cmd);
if (err != NO_ERROR) goto finish;
}
}等待期间可能先收到死亡通知、释放引用或其他 BR_* 命令;executeCommand() 负责消费这些命令,只有 BR_REPLY 才结束当前同步调用。服务端返回的 Binder status 和驱动传输错误是两个层级。
4.2 错误回写
finish:
if (err != NO_ERROR) {
if (acquireResult) *acquireResult = err;
if (reply) reply->setError(err);
mLastError = err;
logExtendedError();
}
return err;传输失败会写入 reply error、更新线程最近错误并记录扩展错误。调用者看到的 status_t 是本地 transport 结果,不应直接当作远端业务异常类型。
5. Oneway边界
5.1 不等业务回复
TF_ONE_WAY 事务提交后,发送线程不等待目标服务执行完成。驱动仍可能返回 buffer、冻结、oneway spam 等传输反馈;“oneway 返回 OK”只说明提交阶段没有立刻失败。
5.2 调用限制
if ((flags & TF_ONE_WAY) == 0 &&
mCallRestriction != ProcessState::CallRestriction::NONE) {
if (mCallRestriction ==
ProcessState::CallRestriction::ERROR_IF_NOT_ONEWAY) {
ALOGE("Process making non-oneway call ...");
} else {
LOG_ALWAYS_FATAL(
"Process may not make non-oneway calls");
}
}限制只针对本进程发出的同步调用;它不改变对端接口是否声明 oneway,也不阻止其他进程调用本进程。
6. 测试边界
6.1 同步与oneway
源码文件:frameworks/native/libs/binder/tests/binderLibTest.cpp
NopTransaction 发送同步请求并断言 NO_ERROR;NopTransactionOneway 传 TF_ONE_WAY 并断言提交成功;两者覆盖 flags 分叉,不证明服务端业务完成时刻。
6.2 冻结错误
FreezeTxn 在冻结目标进程后发送同步事务,断言返回 FROZEN_OBJECT 或 FAILED_TRANSACTION,再解冻并断言调用恢复成功。这个测试证明驱动错误能沿 transact() 返回,不证明所有失败都来自服务端。
6.3 可执行阅读
rg -n "IPCThreadState::transact|writeTransactionData|talkWithDriver|waitForResponse" \
frameworks/native/libs/binder/IPCThreadState.cpp
rg -n "NopTransaction|NopTransactionOneway|FreezeTxn|TF_ONE_WAY" \
frameworks/native/libs/binder/tests/binderLibTest.cpp排查一次 native Binder 调用失败时,按 Parcel 校验、mOut 写入、BINDER_WRITE_READ、BR_* 返回和 reply error 五层定位;若是 oneway,则不要等待“服务执行完成”作为成功条件。
