Android-Framework(五)-Media服务的添加过程-从ProcessState到ServiceManager的Binder链路

文章内容

一、从“添加 Media 服务”讲起

mediaserver 由 init 解析 rc 脚本启动。它的 main() 先准备 Binder 进程状态、取得 ServiceManager 接口,再调用一串 instantiate() 注册媒体服务。这里的关键问题是:进程内的一个服务对象,怎样变成另一个进程能够查询的服务?

注册时,mediaserver 是 Binder 调用的 Client,ServiceManager 是接收注册请求的 Server;被注册的 Media 服务则是后续媒体调用的服务端。

Binder 架构总览:用户空间的 Client / Server / ServiceManager 与内核空间的 Binder 驱动

先看入口:

cpp
// 代码位置 /frameworks/av/media/mediaserver/main_mediaserver.cpp 第 122—142 行// mediaserver 的 main() 入口:作为 Android 系统服务进程被 init 启动// 进入 mediaserver 的进程入口。int main(int argc, char** argv) {    // 取得进程级 Binder 状态并持有强引用。    sp<ProcessState> proc(ProcessState::self());    // 取得 ServiceManager 接口代理。    sp<IServiceManager> sm = defaultServiceManager();    // 记录当前 ServiceManager 代理地址。    ALOGI("ServiceManager: %p", sm.get());     // 一个个把媒体相关服务注册到 ServiceManager    // 注册 AudioFlinger 服务。    AudioFlinger::instantiate();    // 注册 MediaPlayerService 服务。    MediaPlayerService::instantiate();    // 注册 CameraService 服务。    CameraService::instantiate();    // 注册 AudioPolicyService 服务。    AudioPolicyService::instantiate();    // 注册 SoundTriggerHwService 服务。    SoundTriggerHwService::instantiate();    // 注册 RadioService 服务。    RadioService::instantiate();    // 注册 ResourceManagerService 服务。    ResourceManagerService::instantiate();    // 执行扩展注册入口。    registerExtensions();     // 启动持久的 Binder 线程池并让当前线程加入    // 启动 Binder 线程池。    ProcessState::self()->startThreadPool();    // 让当前线程加入 Binder 线程池。    IPCThreadState::self()->joinThreadPool();    // 正常返回。    return 0;// 结束当前作用域。}

入口不使用片段中的命令行参数;执行后先持有 ProcessState 和 ServiceManager 代理,再依次进入服务注册入口,最后启动并加入 Binder 线程池。素材没有展开各个 instantiate() 的实现,不能据此补出每个服务的注册名称或构造参数;后文使用素材明确给出的 media.player 作为请求示例。

理解这条链需要 C/C++ 对象与指针、智能指针、Linux 文件描述符及系统调用知识。继续读驱动还涉及内核和数据结构,包括红黑树。本文以素材展示的接口与代码为边界;具体 Android 版本待核实,不把这些片段视为所有版本通用的完整源码。

二、进程间通信的地基:ProcessState::self()

2.1 单例与进程锁

先看 self(),因为驱动描述符和接收映射属于进程级状态,多个线程需要取得同一个实例。

cpp
// 代码位置 /frameworks/native/libs/binder/ProcessState.cpp 第 70—78 行// 提供进程级单例入口。sp<ProcessState> ProcessState::self() {    // 持有进程锁,离开作用域时自动释放。    Mutex::Autolock _l(gProcessMutex);    // 判断进程单例是否已存在。    if (gProcess != NULL) {        // 返回进程级单例。        return gProcess;    // 结束当前作用域。    }    // 首次调用时创建进程级状态。    gProcess = new ProcessState;    // 返回进程级单例。    return gProcess;// 结束当前作用域。}

该方法没有输入参数,输出 sp<ProcessState>。首次调用在锁内创建 gProcess,后续调用返回已有实例;局部锁对象在离开作用域时释放。它说明单例的检查与创建受同一把进程锁保护,并不意味着所有 Binder 调用都被这把锁串行化。

2.2 构造阶段打开驱动、建立映射

看构造函数,是为了确定“拿到单例”实际准备了哪些资源,以及哪些失败会让进程无法继续。

cpp
// 代码位置 /frameworks/native/libs/binder/ProcessState.cpp 第 339—371 行// 初始化进程级 Binder 状态。ProcessState::ProcessState()    // 打开 Binder 驱动并保存文件描述符。    : mDriverFD(open_driver())    // 将映射地址初始化为失败标记。    , mVMStart(MAP_FAILED)    // 初始化线程计数锁。    , mThreadCountLock(PTHREAD_MUTEX_INITIALIZER)    // 初始化线程计数条件变量。    , mThreadCountDecrement(PTHREAD_COND_INITIALIZER)    // 将执行线程数初始化为零。    , mExecutingThreadsCount(0)    // 保存最大 Binder 线程数配置。    , mMaxThreads(DEFAULT_MAX_BINDER_THREADS)    // 初始化上下文管理状态。    , mManagingContexts(false)    // 初始化上下文检查函数指针。    , mBinderContextCheckFunc(NULL)    // 初始化上下文用户数据指针。    , mBinderContextUserData(NULL)    // 标记线程池尚未启动。    , mThreadPoolStarted(false)    // 初始化线程池序号。    , mThreadPoolSeq(1)// 开始函数体。{    // 仅在驱动成功打开时建立映射。    if (mDriverFD >= 0) {        // 为接收事务映射一段虚拟地址空间        // 申请用于接收事务的只读映射。        mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ,                        // 指定私有、不预留交换空间的映射标志及驱动描述符。                        MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);        // 检查映射是否失败。        if (mVMStart == MAP_FAILED) {            // 记录本分支的失败信息。            ALOGE("Using /dev/binder failed: unable to mmap transaction memory\n");            // 映射失败后关闭驱动描述符。            close(mDriverFD);            // 将驱动描述符标记为不可用。            mDriverFD = -1;        // 结束当前作用域。        }    // 进入前一条件不成立时的分支。    } else {        // 将驱动描述符标记为不可用。        mDriverFD = -1;    // 结束当前作用域。    }    // 驱动不可用时终止进程。    LOG_ALWAYS_FATAL_IF(mDriverFD < 0, "Binder driver could not be opened. Terminating.");// 结束当前作用域。}

初始化列表调用 open_driver(),保存文件描述符,并设置线程池相关初始状态;函数体随后建立接收事务的地址映射。映射失败会关闭描述符并设为 -1,最后的致命检查终止进程。这段代码还表明,保存线程上限与启动线程池是不同步骤,后者出现在 main() 尾部。

进一步看 open_driver(),用于区分打开设备与配置驱动这两个动作。

cpp
// 代码位置 /frameworks/native/libs/binder/ProcessState.cpp 第 311—337 行// 封装驱动打开和线程上限配置。static int open_driver() {    // 以读写方式打开 Binder 设备。    int fd = open("/dev/binder", O_RDWR);    // 检查设备是否打开成功。    if (fd >= 0) {        // 通知内核:本进程需要接收多大的线程池(默认 15)        // 向驱动提交最大线程数设置;第三参数形式待核实。        int result = ioctl(fd, BINDER_SET_MAX_THREADS,DEFAULT_MAX_BINDER_THREADS);        // 检查线程上限设置是否失败。        if (result < 0) {            // 记录本分支的失败信息。            ALOGE("Binder ioctl to set max threads failed: %s", strerror(errno));        // 结束当前作用域。        }    // 进入前一条件不成立时的分支。    } else {        // 记录本分支的失败信息。        ALOGE("opening /dev/binder failed: %s\n", strerror(errno));    // 结束当前作用域。    }    // 返回设备文件描述符或失败值。    return fd;// 结束当前作用域。}

返回值是 /dev/binder 的描述符或打开失败值。设备打开成功后,代码尝试用 BINDER_SET_MAX_THREADS 设置默认值 15;设置失败只记录日志。

再看两个宏,是为了给“接收区域”和“线程上限”提供确定数值。

cpp
// 代码位置 /frameworks/native/libs/binder/ProcessState.cpp 第 44—45 行// 定义接收映射大小为 1 MiB 减去 8 KiB。#define BINDER_VM_SIZE  ((1 * 1024 * 1024) - (4096 * 2))// 定义默认最大 Binder 线程数为 15。#define DEFAULT_MAX_BINDER_THREADS 15

宏分别提供映射长度和线程配置值,不执行资源分配。按源码计算,映射长度为 1,040,384 字节,即 1 MiB 减去 8 KiB;素材通常写作“1MB-8K”。PROT_READ 表示用户侧只读,MAP_PRIVATE | MAP_NORESERVE 是实际传入的映射标志,这片地址用于接收事务,不是后面 mOut 的发送缓冲。

ProcessState::self() 初始化:open /dev/binder → ioctl 15 → mmap 1MB-8K,映射失败会终止进程

三、拿到“总调度台”:defaultServiceManager()

3.1 从 0 号句柄到接口代理

ProcessState 解决进程如何接入驱动,defaultServiceManager() 解决注册请求发给谁。教程给出的转换关系是:getContextObject(NULL) 取得对应 0 号句柄的 BpBinder(0),interface_cast<IServiceManager> 再得到 BpServiceManager。

看单例获取函数,是为了确认代理的缓存方式,以及初始化未就绪时的等待行为。

cpp
// 代码位置 /frameworks/native/libs/binder/IServiceManager.cpp 第 33—48 行// 提供默认 ServiceManager 代理获取入口。sp<IServiceManager> defaultServiceManager() {    // 已有代理时直接返回。    if (gDefaultServiceManager != NULL) return gDefaultServiceManager;     // 持有默认 ServiceManager 单例锁。    AutoMutex _l(gDefaultServiceManagerLock);    // 加锁后再次检查代理是否为空。    if (gDefaultServiceManager == NULL) {        // 通过本地 0 号句柄拿到进程内唯一的 ServiceManager 代理        // 将 Binder 上下文对象转换为 ServiceManager 接口。        gDefaultServiceManager = interface_cast<IServiceManager>(            // 取得 ServiceManager 的上下文对象。            ProcessState::self()->getContextObject(NULL));    // 结束当前作用域。    }     // service manager 由 init 第一个创建,但可能尚未初始化完成,轮询等待    // 代理为空时进入等待循环。    while (gDefaultServiceManager == NULL) {        // 暂停一秒;该片段没有重新获取代理。        sleep(1);    // 结束当前作用域。    }    // 返回缓存的 ServiceManager 代理。    return gDefaultServiceManager;// 结束当前作用域。}

ServiceManager 和 mediaserver 是不同进程,它们由 init 启动,且 ServiceManager 先创建。

3.2 接口类型与传输对象的分工

需要将 interface_cast 的转换关联到 IMPLEMENT_META_INTERFACE 生成的 asInterface,接口标识为 android.os.IServiceManager。BpServiceManager 提供 addService() 等类型化方法,底层 BpBinder 持有目标句柄并承接事务发送。0 是这次事务的目的地,不是待注册 Media 服务自己的句柄。

defaultServiceManager():单例检查 + 锁 + interface_cast 得到 BpServiceManager,其中 while 仅 sleep(1)(完整重试待核实)

四、一次 addService 调用流程

4.1 请求字段按接口约定排列

看 BpServiceManager::addService(),是为了明确跨进程发送的不是 C++ 调用栈,而是按顺序写入 Parcel 的请求字段。

cpp
// 代码位置 /frameworks/native/libs/binder/IServiceManager.cpp 第 155—165 行// 声明服务注册方法,输入服务名和 Binder 对象。virtual status_t addService(const String16& name, const sp<IBinder>& service,                            // 接收是否允许隔离进程访问的参数。                            bool allowIsolated) {    // 分别创建请求和应答 Parcel。    Parcel data, reply;    // 写入接口标识 token    // 写入 ServiceManager 接口标识。    data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());    // 写入服务名称。    data.writeString16(name);    // 将服务 Binder 对象写入请求。    data.writeStrongBinder(service);    // 把隔离访问标志编码为整数。    data.writeInt32(allowIsolated ? 1 : 0);    // 使用相应事务码经远端 Binder 发送请求。    status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);    // 传输成功时返回应答异常码,否则返回传输错误。    return err == NO_ERROR ? reply.readExceptionCode() : err;// 结束当前作用域。}

输入是名称、服务 Binder 强引用和隔离访问标志;输出是事务错误,或者传输成功后从应答读取的异常码。

4.2 服务查询

服务对照查询和枚举方法,可以确认接口 token、事务码与业务参数各自负责什么,而无需为每个操作假设不同的底层传输机制。

cpp
// 代码位置 /frameworks/native/libs/binder/IServiceManager.cpp 第 146—153 行// 按名称获取服务 Binder。virtual sp<IBinder> getService(const String16& name) {    // 分别创建请求和应答 Parcel。    Parcel data, reply;    // 写入 ServiceManager 接口标识。    data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());    // 写入服务名称。    data.writeString16(name);    // 使用相应事务码经远端 Binder 发送请求。    status_t err = remote()->transact(GET_SERVICE_TRANSACTION, data, &reply);    // 成功时读取应答中的 Binder,否则返回空值。    return err == NO_ERROR ? reply.readStrongBinder() : NULL;// 结束当前作用域。} // 代码位置 /frameworks/native/libs/binder/IServiceManager.cpp 第 167—181 行// 枚举 ServiceManager 中的服务名称。virtual Vector<String16> listServices() {    // 创建结果列表。    Vector<String16> res;    // 将枚举索引初始化为零。    int n = 0;    // 持续枚举,直到事务返回错误。    for (;;) {        // 分别创建请求和应答 Parcel。        Parcel data, reply;        // 写入 ServiceManager 接口标识。        data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());        // 写入当前索引并递增。        data.writeInt32(n++);        // 使用相应事务码经远端 Binder 发送请求。        status_t err = remote()->transact(LIST_SERVICES_TRANSACTION, data, &reply);        // 事务失败时结束枚举。        if (err != NO_ERROR) break;        // 把应答中的服务名称加入结果列表。        res.add(reply.readString16());    // 结束当前作用域。    }    // 返回已获取的服务名称。    return res;// 结束当前作用域。}

getService() 输入名称,发送查询事务,成功时返回应答中的 Binder,失败时返回空值。listServices() 从索引 0 开始,每次请求写入递增索引,成功后收集一个名称,遇到事务错误时退出并返回已收集结果。

五、transact:把数据交给内核

5.1 程资源与线程收发状态

注册请求经 BpServiceManager 的 remote()->transact() 进入 BpBinder::transact(),再进入 IPCThreadState::transact()。素材给出的后续顺序是 writeTransactionData(BC_TRANSACTION)、talkWithDriver()、ioctl(BINDER_WRITE_READ, &bwr),最终进入驱动的 binder_ioctl。

cpp
// 代码位置 /frameworks/native/libs/binder/IPCThreadState.cpp 第 686—696 行// 初始化线程级 Binder 状态。IPCThreadState::IPCThreadState()    // 关联当前进程的 ProcessState。    : mProcess(ProcessState::self())    // 保存当前线程 ID。    , mMyThreadId(gettid())    // 初始化严格模式策略。    , mStrictModePolicy(0)    // 初始化上次事务标志。    , mLastTransactionBinderFlags(0)// 开始函数体。{    // 把该状态对象存入线程局部存储。    pthread_setspecific(gTLS, this);    // 为输出缓冲区设置 256 字节初始容量。    mOut.setDataCapacity(256);// 结束当前作用域。} // 代码位置 /frameworks/native/libs/binder/IPCThreadState.cpp 第 702—709 行// 发送应答 Parcel,接收事务标志。status_t IPCThreadState::sendReply(const Parcel& reply, uint32_t flags) {    // 用 BC_REPLY 组装应答命令。    status_t err = writeTransactionData(BC_REPLY, flags, -1, 0, reply, 0);    // 组装失败时立即返回错误。    if (err < NO_ERROR) return err;    // 等待驱动反馈。    return waitForResponse(NULL, NULL);// 结束当前作用域。}

构造时取得共享 ProcessState、保存线程 ID、把自身放入 TLS,并将 mOut 初始容量设为 256。这个容量不是事务最大长度,也不是前面的接收映射长度。sendReply() 输入应答 Parcel 和 flags,使用 BC_REPLY 组装数据,失败即返回,否则等待反馈;它展示的是应答路径,注册请求使用的是 BC_TRANSACTION。

命令及参数组装:

cpp
// 代码位置 /frameworks/native/libs/binder/IPCThreadState.cpp 第 678—684 行// IPCThreadState::clearDeathNotification 片段(素材仅给出这三行,非完整函数体,前后上下文待核实)mOut.writeInt32(BC_CLEAR_DEATH_NOTIFICATION);mOut.writeInt32(handle);mOut.writePointer((uintptr_t)proxy);

5.2 驱动通知

cpp
// 代码位置 /frameworks/native/libs/binder/IPCThreadState.cpp 第 712—801 行// 等待应答或引用获取结果。status_t IPCThreadState::waitForResponse(Parcel* reply, status_t* acquireResult) {    // 保存从输入缓冲读取的驱动命令。    uint32_t cmd;    // 保存驱动交互的返回状态。    int32_t err;     // 循环处理驱动返回的命令。    while (1) {        // 与驱动交换数据,失败时退出循环。        if ((err = talkWithDriver()) < NO_ERROR) break;        // 从输入缓冲读取下一条命令。        cmd = (uint32_t)mIn.readInt32();        // 按驱动返回命令分派处理。        switch (cmd) {        // 处理事务提交完成通知。        case BR_TRANSACTION_COMPLETE:            // 没有应答及引用结果需求时结束等待。            if (!reply && !acquireResult) goto finish;            // 结束当前分支。            break;        // 处理目标对象死亡通知。        case BR_DEAD_REPLY:            // 把错误设置为对象已死亡。            err = DEAD_OBJECT;            // 跳到统一返回位置。            goto finish;        // 处理事务失败通知。        case BR_FAILED_REPLY:            // 把错误设置为事务失败。            err = FAILED_TRANSACTION;            // 跳到统一返回位置。            goto finish;        // 处理引用获取结果,具体逻辑在素材中省略。        case BR_ACQUIRE_RESULT:            // ...            // 结束当前分支。            break;        // 结束当前作用域。        }    // 结束当前作用域。    }// 统一返回标签。finish:    // 返回处理状态。    return err;// 结束当前作用域。}

waitForResponse 的驱动命令分派:talkWithDriver() 失败即跳出循环,成功读出 cmd 后按 BR_TRANSACTION_COMPLETE、BR_DEAD_REPLY、BR_FAILED_REPLY、BR_ACQUIRE_RESULT 四个分支处理,只有应答和引用结果都不需要时才结束等待

输入决定调用方是否需要应答 Parcel 或引用获取结果,返回值报告交互状态。循环先调用 talkWithDriver(),再从 mIn 读取命令。BR_DEAD_REPLY 和 BR_FAILED_REPLY 分别转换为对象死亡和事务失败错误;BR_TRANSACTION_COMPLETE 仅在两种结果都不需要时结束等待。

六、数据跨进程

6.1 三层框架

cpp
// 代码位置 /bionic/libc/kernel/uapi/linux/binder.h 第 59—67 行// 一次 ioctl(BINDER_WRITE_READ) 的读写缓冲结构// 定义一次驱动读写交换的外层结构。struct binder_write_read {    // 记录待写入驱动的字节数。    binder_size_t write_size;    // 记录驱动已消费的写入字节数。    binder_size_t write_consumed;    // 保存输出缓冲地址。    binder_uintptr_t write_buffer;    // 记录输入缓冲可接收的字节数。    binder_size_t read_size;    // 记录已读取的字节数。    binder_size_t read_consumed;    // 保存输入缓冲地址。    binder_uintptr_t read_buffer;// 结束结构或联合体声明。}; // 代码位置 /bionic/libc/kernel/uapi/linux/binder.h 第 95—117 行// 一次跨进程事务的描述// 展示素材中的事务描述片段,字段布局待核实。struct binder_transaction_data {    // 开始联合体,各成员共享存储空间。    union {        // 保存 Binder 句柄;具体含义取决于所在结构。        __u32 handle;        // 保存目标指针表示。        binder_uintptr_t ptr;    // 结束目标联合体并命名为 target。    } target;    // 保存与 Binder 对象相关的 cookie。    binder_uintptr_t cookie;    // 保存事务码。    __u32 code;    // 保存事务或对象标志。    __u32 flags;    // 开始联合体,各成员共享存储空间。    union {        // 开始嵌套数据结构。        struct {            // 记录素材片段中的数据大小字段。            binder_size_t size;            // 保留素材中的 offset 字段,其准确布局待核实。            binder_size_t offset;            // 保存素材片段中的数据地址字段。            binder_uintptr_t buffer;        // 结束嵌套结构并命名为 data。        } data;    // 结束结构或联合体声明。    };    // ...// 结束结构或联合体声明。}; // 代码位置 /bionic/libc/kernel/uapi/linux/binder.h 第 47—56 行// Parcel 里被拍平的 binder 对象// 定义 Parcel 中的 Binder 对象描述。struct flat_binder_object {    // 标记本地 Binder、远端句柄或弱引用类型。    __u32 type;    // 保存事务或对象标志。    __u32 flags;    // 开始联合体,各成员共享存储空间。    union {        // 在本地对象分支中保存弱引用管理对象地址。        binder_uintptr_t binder;        // 保存 Binder 句柄;具体含义取决于所在结构。        __u32 handle;    // 结束结构或联合体声明。    };    // 保存与 Binder 对象相关的 cookie。    binder_uintptr_t cookie;// 结束结构或联合体声明。};

binder_write_read 描述一次驱动交互:写方向由 write_size 和 write_buffer 指定,读方向由 read_size 和 read_buffer 指定,对应的 consumed 字段记录已消费量。写缓冲中承载命令及参数;本次主线的命令是 BC_TRANSACTION,后接事务描述。

binder_transaction_data 的 target.handle 在注册请求中为 0,code 是 ADD_SERVICE_TRANSACTION。flat_binder_object 则位于请求 Parcel 内,描述被注册的服务对象。目标句柄回答“请求送到谁”,对象描述回答“此次注册哪个 Binder 对象”。

6.2 字段布局的证据边界

概念上可以确认:事务描述包含目标、事务码,以及指向 Parcel 数据的地址与大小。这里的地址不是允许 ServiceManager 直接解引用 mediaserver 用户指针的承诺。

Binder 数据结构:binder_write_read → binder_transaction_data → Parcel(token / media.player / flat_binder_object / allowIsolated=0),事务字段布局待核实

七、Parcel 怎么把“对象”拍平

cpp
// 代码位置 /frameworks/native/libs/binder/Parcel.cpp 第 177—207 行// 把强引用 Binder 转换为扁平对象并写入 Parcel。status_t flatten_binder(const sp<ProcessState>&, const sp<IBinder>& binder, Parcel* out) {    // 创建待写入的对象描述。    flat_binder_object obj;    // 设置对象标志并声明接受文件描述符。    obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;    // 区分有效 Binder 与空 Binder。    if (binder != NULL) {        // 尝试取得本地 Binder 对象。        IBinder* local = binder->localBinder();        // 进入本地对象分支。        if (local != NULL) {            // 将对象类型设为本地 Binder。            obj.type = BINDER_TYPE_BINDER;            // 记录本地对象的弱引用管理对象地址。            obj.binder = reinterpret_cast<uintptr_t>(local->getWeakRefs());            // 记录本地 Binder 对象地址。            obj.cookie = reinterpret_cast<uintptr_t>(local);        // 进入前一条件不成立时的分支。        } else {            // 将对象类型设为远端句柄。            obj.type = BINDER_TYPE_HANDLE;            // 记录代理在当前进程中的句柄。            obj.handle = binder->remoteBinder()->handle();        // 结束当前作用域。        }    // 进入前一条件不成立时的分支。    } else {        // 将对象类型设为本地 Binder。        obj.type = BINDER_TYPE_BINDER;        // 为空 Binder 清零对象地址。        obj.binder = 0;        // 为空 Binder 清零 cookie。        obj.cookie = 0;    // 结束当前作用域。    }    // 交给统一写入入口完成对象序列化。    return finish_flatten_binder(binder, obj, out);// 结束当前作用域。}

输入是 Binder 强引用与输出 Parcel,未命名的 ProcessState 参数在片段中没有使用;输出由 finish_flatten_binder() 返回。本地对象分支写入 BINDER_TYPE_BINDER,binder 保存 local->getWeakRefs() 地址,cookie 保存 local 地址。这里记录的是对象身份相关信息,代码没有序列化 Media 服务的业务成员。

远端代理分支写入 BINDER_TYPE_HANDLE 和代理的 handle();空对象分支使用 BINDER_TYPE_BINDER 并清零 binder 与 cookie。所有分支先设置 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS。远端分支没有展示 cookie 赋值,其完整初始化及驱动解释待核实,不在整理时自行添加语句。

八、ServiceManager 进程侧逻辑:收到即处理

到达 ServiceManager 之后,请求需要从通用 Binder 事务转成具体服务管理操作。

分发标识 对应操作
SVC_MGR_GET_SERVICE 获取服务
SVC_MGR_CHECK_SERVICE 检查服务
SVC_MGR_ADD_SERVICE 添加服务
SVC_MGR_LIST_SERVICES 枚举服务

本次注册落在添加服务分支。客户端已经准备了接口标识、名称、对象与访问标志;服务端按照操作类型消费相应请求,并通过 Binder 应答链返回处理结果。

BnServiceManager::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags):

cpp
// 代码位置 /frameworks/native/libs/binder/IServiceManager.cpp 第 189—190 行// 素材仅给出该签名,函数体未提供,待核实virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags);

8.1 两份必须对齐的标识定义

cpp
// 代码位置 /frameworks/native/libs/binder/IServiceManager.h 第 59—62 行// 客户端使用的四个事务码,从 FIRST_CALL_TRANSACTION 起依次递增enum {    GET_SERVICE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,    CHECK_SERVICE_TRANSACTION,    ADD_SERVICE_TRANSACTION,    LIST_SERVICES_TRANSACTION,};
c
// 代码位置 /frameworks/native/cmds/servicemanager/binder.h 第 31—42 行// ServiceManager 进程使用的分发标识,注释要求与另外两个头文件保持一致#define BINDER_SERVICE_MANAGER  0U#define SVC_MGR_NAME "android.os.IServiceManager" enum {    /* Must match definitions in IBinder.h and IServiceManager.h */    PING_TRANSACTION  = B_PACK_CHARS('_','P','N','G'),    SVC_MGR_GET_SERVICE = 1,    SVC_MGR_CHECK_SERVICE,    SVC_MGR_ADD_SERVICE,    SVC_MGR_LIST_SERVICES,};

IBinder.h 第 48 行把 FIRST_CALL_TRANSACTION 定为 0x00000001,四个事务码因此是 1、2、3、4;服务端那份定义从 1 开始枚举,取值和顺序都对得上。表格里的四个名字来自这个枚举,不是宏。两处定义分处 C++ 头文件和 C 头文件,一致性只由 binder.h 里那行注释维持:任何一边插入新项,编译期没有任何提示,事务会落到 default 分支。PING_TRANSACTION 两边都写成 B_PACK_CHARS('_','P','N','G'),由同一个宏展开,这一条不会错位。

8.2 收到事务后按标识分发

ServiceManager 进程在 6.0 里跑的是 C 实现:main() 打开驱动、用 binder_become_context_manager() 把自己登记成上下文管理器,最后 binder_loop(bs, svcmgr_handler) 进入循环,每收到一个事务回调一次 svcmgr_handler()(service_manager.c 第 357—384 行)。

c
// 代码位置 /frameworks/native/cmds/servicemanager/service_manager.c 第 244—338 行// ServiceManager 进程的分发入口:先校验目标和接口,再按 txn->code 选择操作int svcmgr_handler(struct binder_state *bs,                   struct binder_transaction_data *txn,                   struct binder_io *msg,                   struct binder_io *reply){    struct svcinfo *si;    uint16_t *s;    size_t len;    uint32_t handle;    uint32_t strict_policy;    int allow_isolated;     // 目标必须是 0 号句柄,也就是 ServiceManager 自己    if (txn->target.ptr != BINDER_SERVICE_MANAGER)        return -1;     if (txn->code == PING_TRANSACTION)        return 0;     // 等价于 Parcel::enforceInterface():读严格模式掩码和接口名    strict_policy = bio_get_uint32(msg);    s = bio_get_string16(msg, &len);    if (s == NULL) {        return -1;    }     // 接口名必须等于 android.os.IServiceManager    if ((len != (sizeof(svcmgr_id) / 2)) ||        memcmp(svcmgr_id, s, sizeof(svcmgr_id))) {        fprintf(stderr,"invalid id %s\n", str8(s, len));        return -1;    }     // 此处省略 sehandle 的 SELinux 标签刷新,与参数读取无关     switch(txn->code) {    // 获取与检查在服务端共用一段逻辑    case SVC_MGR_GET_SERVICE:    case SVC_MGR_CHECK_SERVICE:        s = bio_get_string16(msg, &len);        if (s == NULL) {            return -1;        }        handle = do_find_service(bs, s, len, txn->sender_euid, txn->sender_pid);        if (!handle)            break;        bio_put_ref(reply, handle);        return 0;     // 本次注册走这个分支:按顺序取出名称、对象引用、隔离访问标志    case SVC_MGR_ADD_SERVICE:        s = bio_get_string16(msg, &len);        if (s == NULL) {            return -1;        }        handle = bio_get_ref(msg);        allow_isolated = bio_get_uint32(msg) ? 1 : 0;        if (do_add_service(bs, s, len, handle, txn->sender_euid,            allow_isolated, txn->sender_pid))            return -1;        break;     // 枚举按索引沿服务链表取一个名字,越界即失败    case SVC_MGR_LIST_SERVICES: {        uint32_t n = bio_get_uint32(msg);         if (!svc_can_list(txn->sender_pid)) {            ALOGE("list_service() uid=%d - PERMISSION DENIED\n",                    txn->sender_euid);            return -1;        }        si = svclist;        while ((n-- > 0) && si)            si = si->next;        if (si) {            bio_put_string16(reply, si->name);            return 0;        }        return -1;    }    default:        ALOGE("unknown code %d\n", txn->code);        return -1;    }     // 添加服务分支走 break 落到这里,回一个 0 表示成功    bio_put_uint32(reply, 0);    return 0;}

函数进入分发前有一段固定的前置处理:先确认事务目标是 0 号句柄,遇到 PING_TRANSACTION 直接返回,再读出严格模式掩码和接口名。接口名要和 svcmgr_id(android.os.IServiceManager)逐字节相等,这正是 4.1 节里 writeInterfaceToken() 写入的内容,也是它不能省的原因:名字不匹配时 ServiceManager 直接返回 -1,事务失败。

switch 的四个分支对应表格里的四种操作。获取和检查在服务端是同一段代码,两者的区别留在客户端:getService() 循环五次调用 checkService(),每次失败后 sleep(1),而 checkService() 只发一次事务(IServiceManager.cpp 第 134—153 行)。添加服务分支按顺序取出名称、对象引用和隔离访问标志,与客户端 addService() 的写入次序一一对应:writeString16() 对 bio_get_string16(),writeStrongBinder() 对 bio_get_ref(),writeInt32() 对 bio_get_uint32()。binder_io 是按位置消费的字节流,没有字段名,两边顺序一旦不一致,读到的就是错位的数据,而且不会报错。添加服务分支走 break 落到函数尾部,bio_put_uint32(reply, 0) 就是 4.1 节里 reply.readExceptionCode() 取到的那个 0。

8.3 添加服务分支落到哪里

c
// 代码位置 /frameworks/native/cmds/servicemanager/service_manager.c 第 194—242 行// 把一次注册写入服务表,或者覆盖同名服务的旧记录int do_add_service(struct binder_state *bs,                   const uint16_t *s, size_t len,                   uint32_t handle, uid_t uid, int allow_isolated,                   pid_t spid){    struct svcinfo *si;     // 句柄无效或名字长度越界时拒绝注册    if (!handle || (len == 0) || (len > 127))        return -1;     // 按服务名查 SELinux 标签,判断调用方是否有 add 权限    if (!svc_can_register(s, len, spid)) {        ALOGE("add_service('%s',%x) uid=%d - PERMISSION DENIED\n",             str8(s, len), handle, uid);        return -1;    }     si = find_svc(s, len);    if (si) {        // 同名服务已存在:先释放旧引用,再用新句柄覆盖        if (si->handle) {            ALOGE("add_service('%s',%x) uid=%d - ALREADY REGISTERED, OVERRIDE\n",                 str8(s, len), handle, uid);            svcinfo_death(bs, si);        }        si->handle = handle;    } else {        // 新名字:分配表项并插到服务表头部        si = malloc(sizeof(*si) + (len + 1) * sizeof(uint16_t));        if (!si) {            ALOGE("add_service('%s',%x) uid=%d - OUT OF MEMORY\n",                 str8(s, len), handle, uid);            return -1;        }        si->handle = handle;        si->len = len;        memcpy(si->name, s, (len + 1) * sizeof(uint16_t));        si->name[len] = '\0';        si->death.func = (void*) svcinfo_death;        si->death.ptr = si;        si->allow_isolated = allow_isolated;        si->next = svclist;        svclist = si;    }     // 持有服务对象引用,并挂上死亡通知    binder_acquire(bs, handle);    binder_link_to_death(bs, handle, &si->death);    return 0;}

do_add_service() 才是把注册写进服务表的地方。它先拒绝两类输入:handle 为 0,或者名字长度为 0 或超过 127。随后 svc_can_register() 做 SELinux 检查,按服务名查 service_contexts 得到目标类型,再用调用方 pid 判断有没有 add 权限,uid 只用于日志(service_manager.c 第 100—114 行)。名字已存在时不报错,而是打印 ALREADY REGISTERED, OVERRIDE,用 svcinfo_death() 释放旧引用,再覆盖 handle;名字不存在就分配一个 svcinfo 插到 svclist 表头。同名注册是覆盖,不是拒绝,服务表里不会出现两份记录。

最后两行是这次注册在内核留下的状态:binder_acquire() 增加对服务对象的引用,binder_link_to_death() 给这个引用挂上死亡通知。没有这两步,mediaserver 退出后 ServiceManager 仍会返回一个已经失效的句柄;有了它们,进程退出时回调 svcinfo_death(),binder_release() 释放引用并把 si->handle 清零,后续查询就查不到这个服务。

allowIsolated=0 的作用点在查询侧:它存进 si->allow_isolated,do_find_service() 遇到 !si->allow_isolated 且调用方 uid 落在 AID_ISOLATED_START 到 AID_ISOLATED_END 之间时返回 0(service_manager.c 第 178—185 行),也就是拒绝。

应答方向用的是同一套扁平对象格式:bio_put_ref() 写回的 flat_binder_object 同样带 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS,类型为 BINDER_TYPE_HANDLE。

九、完整调用链总览

把各层串联起来,注册前先有进程状态和目的地代理,随后才是一次具体事务的编码、驱动传送和服务端处理。

addService 完整调用链:mediaserver → BpServiceManager → BpBinder → IPCThreadState → writeTransactionData(BC_TRANSACTION) → ioctl(BINDER_WRITE_READ) → Linux Binder 驱动 → ServiceManager,驱动返回 BR_* 命令并经“一次拷贝”传数据

  1. ProcessState::self() 在进程锁内建立单例,打开设备、配置线程上限并映射接收区。
  2. defaultServiceManager() 将 0 号句柄对应的 Binder 转换成 BpServiceManager 接口代理。
  3. addService() 把 token、名称、服务对象描述和隔离访问标志写入 Parcel,以添加服务事务码发起调用。
  4. BpBinder 将事务交给线程级状态;IPCThreadState 组织 BC_TRANSACTION 与事务描述,通过 binder_write_read 向驱动交换数据。
  5. 驱动依据目标句柄把请求送往 ServiceManager,后者按添加服务操作处理;调用方通过返回命令及应答获取结果。

本文由 AgentWorkFlow 生成

觉得不错?给作者点个赞吧

分类:Android系统

评论 (0)

无需注册,留言经站长审核后公开。

加载评论中…

Android-Framework(五)-Media服务的添加过程-从ProcessState到ServiceManager的Binder链路 · Yinchen 的博客