Android Binder:通过ioctl从Context Manager获取引用的代码实现疑问
Binder Context Manager 交互的代码实现细节
1. Client 1 接收 Context Manager 返回的 Ref
Client 1 向 Context Manager 注册 Node 后,确实通过 BINDER_WRITE_READ ioctl 的返回结果(BR_REPLY 命令)获取 Ref,但需确保注册流程正确,否则可能拿到空值。以下是核心代码逻辑:
核心流程代码片段
#include <linux/binder.h> #include <sys/ioctl.h> #include <sys/mman.h> #include <stdio.h> #include <string.h> #define BINDER_BUFFER_SIZE (1024 * 1024) int main() { int binder_fd = open("/dev/binder", O_RDWR); if (binder_fd < 0) { perror("open binder"); return 1; } // 映射Binder内存 void *binder_buf = mmap(NULL, BINDER_BUFFER_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE, binder_fd, 0); if (binder_buf == MAP_FAILED) { perror("mmap"); return 1; } struct binder_write_read bwr = {0}; struct binder_transaction_data txn = {0}; struct flat_binder_object fbo = {0}; // 构造注册事务:绑定Node与服务名称 txn.target.handle = 0; // Context Manager的固定handle为0 txn.code = 1; // 对应service_manager_add_service命令码 txn.flags = TF_ACCEPT_FDS; txn.data_size = sizeof(fbo) + strlen("my_test_service") + 1; txn.offsets_size = sizeof(size_t); // 填充flat_binder_object:传递自身Node fbo.type = BINDER_TYPE_BINDER; fbo.binder = (uintptr_t)binder_buf; // 指向自身Binder节点内存 fbo.flags = FLAT_BINDER_FLAG_ACCEPTS_FDS; // 将服务名称与fbo拷贝到Binder缓冲区 char *data_ptr = binder_buf; strcpy(data_ptr, "my_test_service"); size_t *offset_ptr = (size_t*)(data_ptr + strlen("my_test_service") + 1); *offset_ptr = strlen("my_test_service") + 1; // fbo在数据中的偏移 memcpy(data_ptr + *offset_ptr, &fbo, sizeof(fbo)); // 发送BC_TRANSACTION命令 bwr.write_size = sizeof(txn); bwr.write_buffer = (uintptr_t)&txn; ioctl(binder_fd, BINDER_WRITE_READ, &bwr); // 读取并解析BR_REPLY bwr.read_size = BINDER_BUFFER_SIZE; bwr.read_buffer = (uintptr_t)binder_buf; ioctl(binder_fd, BINDER_WRITE_READ, &bwr); struct binder_cmd *cmd = (struct binder_cmd*)binder_buf; if (cmd->cmd == BR_REPLY) { struct binder_transaction_data *reply_txn = (struct binder_transaction_data*)(cmd + 1); struct flat_binder_object *reply_fbo = (struct flat_binder_object*)((char*)reply_txn + reply_txn->data.ptr.buffer + reply_txn->data.ptr.offsets[0]); if (reply_fbo->type == BINDER_TYPE_HANDLE) { printf("Received Ref handle: %d\n", reply_fbo->handle); // 此处handle即为Context Manager返回的有效Ref } } // 清理资源 munmap(binder_buf, BINDER_BUFFER_SIZE); close(binder_fd); return 0; }
空Ref问题排查
如果未获取到非零Ref,大概率是以下原因:
- 未绑定服务名称:Context Manager需通过名称关联Node与Ref,无名称则不会生成有效Ref
flat_binder_object的binder字段未指向自身Binder节点的有效内存- 事务命令码错误:需使用Context Manager约定的
add_service命令码(通常为1) - 回复解析错误:未正确通过
offsets数组定位flat_binder_object在缓冲区中的位置
2. Client 2 向 Context Manager 请求 Ref
Client 2 不能直接指定0xbeef这类硬编码值,必须通过Client 1注册时使用的服务名称发起查询。Context Manager内部维护「服务名称→Binder引用」的映射表,仅通过名称查询才能拿到对应Ref。
核心流程代码片段
#include <linux/binder.h> #include <sys/ioctl.h> #include <sys/mman.h> #include <stdio.h> #include <string.h> #define BINDER_BUFFER_SIZE (1024 * 1024) int main() { int binder_fd = open("/dev/binder", O_RDWR); if (binder_fd < 0) { perror("open binder"); return 1; } void *binder_buf = mmap(NULL, BINDER_BUFFER_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE, binder_fd, 0); if (binder_buf == MAP_FAILED) { perror("mmap"); return 1; } struct binder_write_read bwr = {0}; struct binder_transaction_data txn = {0}; // 构造查询事务:请求指定名称的服务Ref txn.target.handle = 0; // Context Manager的固定handle为0 txn.code = 2; // 对应service_manager_get_service命令码 txn.flags = 0; txn.data_size = strlen("my_test_service") + 1; txn.offsets_size = 0; // 查询无需传递flat_binder_object // 将服务名称拷贝到Binder缓冲区 strcpy(binder_buf, "my_test_service"); // 发送BC_TRANSACTION命令 bwr.write_size = sizeof(txn); bwr.write_buffer = (uintptr_t)&txn; ioctl(binder_fd, BINDER_WRITE_READ, &bwr); // 读取并解析BR_REPLY中的Ref bwr.read_size = BINDER_BUFFER_SIZE; bwr.read_buffer = (uintptr_t)binder_buf; ioctl(binder_fd, BINDER_WRITE_READ, &bwr); struct binder_cmd *cmd = (struct binder_cmd*)binder_buf; if (cmd->cmd == BR_REPLY) { struct binder_transaction_data *reply_txn = (struct binder_transaction_data*)(cmd + 1); struct flat_binder_object *reply_fbo = (struct flat_binder_object*)((char*)reply_txn + reply_txn->data.ptr.buffer + reply_txn->data.ptr.offsets[0]); if (reply_fbo->type == BINDER_TYPE_HANDLE) { printf("Got Ref handle for service: %d\n", reply_fbo->handle); // 使用该handle即可与Client 1通信 } } munmap(binder_buf, BINDER_BUFFER_SIZE); close(binder_fd); return 0; }
关键说明
- Context Manager的默认handle固定为0,所有交互均使用此值
- 查询命令码通常为2(对应
service_manager_get_service),注册命令码为1(对应service_manager_add_service) - 查询成功后,回复中的
flat_binder_object类型为BINDER_TYPE_HANDLE,其handle字段即为可用于通信的Ref
内容的提问来源于stack exchange,提问作者hkcode
相关产品推荐
相关产品推荐

