You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 14:23:18