Binder入门(四) ServiceManager

981 阅读6分钟

初始化

andriod 系统启动时会先由内核启动 init 进程,init 进程通过 init.rc 启动 service_manager 以及 zygote 进程,zygote 然后启动 system_server 以及各种应用进程。也就是说sm 的启动先于所有应用进程

sm 启动后有几件准备工作要做,只有做完之后才可以成为真正的管家,接受客户端的调用:

  1. 打开 binder 驱动
  2. 将自己注册成 service_manager
  3. 通过循环等待事件到来。在这一步之前,sm 与 binder 驱动还有个交互,就是告诉 binder 驱动自己进入了循环

sm 的事件都在 main 函数中开始。源码

// 强指针,用于处理 c++ 中可能出现的野指针等问题,可理解为 java 中的强引用
// 这一步就是打开及注册
const char* driver = "/dev/binder";
sp<ProcessState> ps = ProcessState::initWithDriver(driver);
// 第二步,本地注册,将自己注册成 manager 服务。见 【addService】分析
sp<ServiceManager> manager = new ServiceManager(std::make_unique<Access>());
if (!manager->addService("manager", manager, false /*allowIsolated*/, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk()) {
     LOG(ERROR) << "Could not self register servicemanager";
}
// 这一步使用 the_context_object 记录 mananger
IPCThreadState::self()->setTheContextObject(manager);

// 第三步,向驱动注册,使自己成为 service 的管理员。见下面【becomeContextManager】分析
ps->becomeContextManager(nullptr, nullptr);
// 第四步:等待
BinderCallback::setupTo(looper);

先看第一步,它会创建 ProcessState 对象,在上一节中说过,ProcessState 是进程单例,而且构造方法中会通过 open、mmap 与 binder 驱动建立映射。ProcessState 的进程单例使用 gProcess 保存

addService

简单讲它就是将 ServiceManager 实例与 manager 字符串存储到一个 map 中

// 先列举 addService 使用到的属性
using ServiceMap = std::map<std::string, Service>;
ServiceMap mNameToService;

// addService 节选

// name = "manager",binder = ServiceManager 实例,allowIsolated = false
// dumpPriority = IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT
Status ServiceManager::addService(const std::string& name, const sp<IBinder>& binder, bool allowIsolated, int32_t dumpPriority) {
    // 省略安全检测
    if (!isValidServiceName(name)) {
        LOG(ERROR) << "Invalid service name: " << name;
        return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT);
    }
    // emplace 就是将 key-value 存储到 map 中
    auto entry = mNameToService.emplace(name, Service {
        .binder = binder,
        .allowIsolated = allowIsolated,
        .dumpPriority = dumpPriority,
        .debugPid = ctx.debugPid,
    });
    return Status::ok();
}

addService 中有一个小插曲:会对 server 的名字进行检测。通过源码可以知道名字必须由数字、字母、下划线、中划线、/ 与点 构成。

becomeContextManager

后看 ps->becomeContextManager(),它就是通过 ioctl 系统调用到 binder_ioctl 方法

// ProcessState 中
// mDriverFD 是在构造函数中赋值的,对应的是打开 binder 设备文件返回的 fd
// 因此,这句话就会执行到 binder_ioctl,进面将 sm 设置成 manager

// 在数据传输一节说过,flat_binder_object 是用于传输 binder 实体
// 要么它的 binder 指向实体的首地址,要么 handle 指向实体的句柄
// 但对 serviceManager 来说,它一个都没传
// 【这是 serviceManager 最特殊的地方】
flat_binder_object obj {
    .flags = FLAT_BINDER_FLAG_TXN_SECURITY_CTX,
};
result = ioctl(mDriverFD, BINDER_SET_CONTEXT_MGR, &dummy);

这里也放一个驱动收到后的处理逻辑,主要看创建的 binder_node

static int binder_ioctl_set_ctx_mgr(struct file *filp,
				    struct flat_binder_object *fbo)
{
	int ret = 0;
	struct binder_proc *proc = filp->private_data;
	struct binder_context *context = proc->context;
	struct binder_node *new_node;
	kuid_t curr_euid = current_euid();

	if (context->binder_context_mgr_node) {
		//只能设置一次
	}
	// 省略校验
        
        // 创建 binder_node
	new_node = binder_new_node(proc, fbo);
	binder_node_lock(new_node);
	new_node->local_weak_refs++;
	new_node->local_strong_refs++;
	new_node->has_strong_ref = 1;
	new_node->has_weak_ref = 1;
        
        // 记录到 context->binder_context_mgr_node
	context->binder_context_mgr_node = new_node;
	binder_put_node(new_node);
	return ret;
}

整个代码中对 binder_node 的处理与普通 binder 实体最大的不同就是:binder_node::ptr 就没有被赋值,是 null。也就是说 驱动中压根就没有 serviceManager 的首地址

等待事件

接着看 main 中的第三行代码,它调用是 BinderCallback 的静态方法 setupTo。为简单,这里会将 main() 中相关代码一起贴上:

// main 中相关代码
sp<Looper> looper = Looper::prepare(false /*allowNonCallbacks*/);
BinderCallback::setupTo(looper);

// BinderCallback 定义

class BinderCallback : public LooperCallback {
public:
    static sp<BinderCallback> setupTo(const sp<Looper>& looper) {
        sp<BinderCallback> cb = new BinderCallback;

        int binder_fd = -1;
        
        // self() 可以理解为 java 中的 getInstance(),都是通过静态方法返回一个对象
        // 所以会调用 IPCThreadState 的构造函数
        // 内部通过 mProcess 指向全局的 ProcessState 对象
        IPCThreadState::self()->setupPolling(&binder_fd);

        // 将 setupPolling() 传递给 binder 驱动
        IPCThreadState::self()->flushCommands();
        
        
        // looper 就是 Looper,handler 机制的核心 native 实现
        // 这里是通过 epoll 机制监听 binder_fd
        // 当 binder_fd 可读时就会调用下面的 handleEvent() 
        int ret = looper->addFd(binder_fd,
                                Looper::POLL_CALLBACK,
                                Looper::EVENT_INPUT,
                                cb,
                                nullptr /*data*/);
        return cb;
    }

    int handleEvent(int /* fd */, int /* events */, void* /* data */) override {
        // 分析见下
        IPCThreadState::self()->handlePolledCommands();
        return 1;  // Continue receiving callbacks.
    }
};

解释下 setupPolling。这个方法会向 IPCThreadState.mOut 中写入一个 int 值 BC_ENTER_LOOPER,然后在下面的 flushCommands 时传递给 binder 驱动。同时会将 binder_fd 指向 open('/dev/binder') 时 binder 设备文件对应的 fd。

setupPolling() 与 flushCommands() 相结合就是告诉 binder 驱动 service_manager 已进入循环模式,可以处理数据了

说一下下面的 addFd 方法。它是 Looper 中的方法,通过 epoll 机制监听 binder 对应的 fd,当可读时就会调用下面的 handleEvent。

至此,sm 的准备工作算是做好了,可以接收客户端的调用了。在旧版本上,sm 的循环是使用不断向 binder 驱动发消息的机制实现的,这里改为了使用 epoll 机制

IPCThreadState::handlePolledCommands

在上面说过,SM 通过 epoll 机制在 binder_fd 有变化时,就执行 handleEvent。这个方法只是告诉 sm binder 可读,具体信息需要再次从 binder 读取

image.png

getAndExecuteCommand() 的代码比较长,删删不重要的后如下:

// IPCThreadState::getAndExecuteCommand()

// 与 binder 驱动交互,读取数组
// talkWithDriver 不传参数时,默认为 true,这里使用了默认参数功能
result = talkWithDriver();
if (result >= NO_ERROR) {
    cmd = mIn.readInt32();
    // 根据传入的命令执行不同的操作
    result = executeCommand(cmd);
}

executeCommand() 最常用的命令就是 BR_TRANSACTION,这里只分析这个,有一点要注意这个方法即处理了 serviceManager 中的,也处理了普通 binder 实体的

    case BR_TRANSACTION:
        {
            binder_transaction_data_secctx tr_secctx;
            binder_transaction_data& tr = tr_secctx.transaction_data;

            if (cmd == (int) BR_TRANSACTION_SEC_CTX) {
                result = mIn.read(&tr_secctx, sizeof(tr_secctx));
            } else {
                // 将数据读到 binder_transaction_data 中
                result = mIn.read(&tr, sizeof(tr));
                tr_secctx.secctx = 0;
            }


            Parcel buffer;
            // 将 tr 中关于缓存 buffer 中
            buffer.ipcSetDataReference(
                reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
                tr.data_size,
                reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
                tr.offsets_size/sizeof(binder_size_t), freeBuffer, this);
            
            // 创建 Parcel,由真正的程序将处理结果写入
            // 剩余的逻辑就不区分 serviceManager 与普通的 binder 实体了
            Parcel reply;
            status_t error;
            if (tr.target.ptr) {
                // 在前面几节说过,tr.target.ptr 指向 binder 实体在宿主进程的首地址
                // 由驱动在写回数据时赋值的
                if (reinterpret_cast<RefBase::weakref_type*>(
                        tr.target.ptr)->attemptIncStrong(this)) {
                    // 使用 tr.cookie 强转成指针,然后调用 transact() 方法
                    error = reinterpret_cast<BBinder*>(tr.cookie)->transact(tr.code, buffer,
                            &reply, tr.flags);
                    reinterpret_cast<BBinder*>(tr.cookie)->decStrong(this);
                } else {
                    error = UNKNOWN_TRANSACTION;
                }

            } else {
                // 如果没值,只就是 serviceManager 了
                // 在上面 serviceManager 注册时就说过,binder 驱动压根就没有存储它的首地址
                // 所以肯定也返回不了
                // 别的 ibinder 对象肯定会有值
                the_context_object->transact(tr.code, buffer, &reply, tr.flags);
            }

            if ((tr.flags & TF_ONE_WAY) == 0) {
                // 非 oneway 模式,将处理结果返回给驱动,再由驱动使用 copytousr 复制到调用进程
                sendReply(reply, 0);
            } else {
                // oneway 模式,就不写返回结果了
            }
        }
        break;

sendReply 见下:

status_t IPCThreadState::sendReply(const Parcel& reply, uint32_t flags)
{
    // 通过 BC_REPLY 写回驱动 
    err = writeTransactionData(BC_REPLY, flags, -1, 0, reply, &statusBuffer);
    
    // 因为是将结果写给驱动,所以不需要驱动返回数据,两个参数传 null
    return waitForResponse(nullptr, nullptr);
}

一次完整的流程

下面的代码不保证是正确类的,但想着大体逻辑也差不多,就这么用吧

客户进程通过 getService() 向 serviceManager 拿服务,它与 binder 驱动交互前拿的肯定是 serviceManager 的代理,即 bpXXX。通过 cs.android.com 中搜索,得到 bp 相关的 BpServiceManager。它的源码删删减减后如下

::android::binder::Status BpServiceManager::getService(const ::std::string& name, ::android::sp<::android::IBinder>* _aidl_return) {

  ::android::Parcel _aidl_data;
  ::android::Parcel _aidl_reply;
  
    // 写入数据
  _aidl_data.writeInterfaceToken(getInterfaceDescriptor());
  _aidl_data.writeUtf8AsUtf16(name);
  
    // 通过 binder 驱动到了 serviceManager
    // serviceManager 会将结果写到 _aidl_reply 中
    // 请求的方法是 BnServiceManager::TRANSACTION_getService
  _aidl_ret_status = remote()->transact(BnServiceManager::TRANSACTION_getService, _aidl_data, &_aidl_reply, 0);

  _aidl_ret_status = _aidl_status.readFromParcel(_aidl_reply);
   // 将 _aidl_reply 的结果读到 _aidl_return,这就回到了调用进程 
  _aidl_ret_status = _aidl_reply.readNullableStrongBinder(_aidl_return);
}

一般情况下,响应请求的叫 BnXX,在同文件中就有一个 BnServiceManager,它的 onTransact() 关于上面的请求的代码片段如下:

case BnServiceManager::TRANSACTION_getService:
  {
    ::std::string in_name;
    ::android::sp<::android::IBinder> _aidl_return;

      // 读取客户进程请求的 serviceName
    _aidl_ret_status = _aidl_data.readUtf8FromUtf16(&in_name);
    
        // 调用了 getService(),将结果存储到 _aidl_return
        // 
    ::android::binder::Status _aidl_status(getService(in_name, &_aidl_return));
    
    _aidl_ret_status = _aidl_status.writeToParcel(_aidl_reply);
        // 将 _aidl_return 写入到 _aidl_reply
    _aidl_ret_status = _aidl_reply->writeStrongBinder(_aidl_return);
  }

结合上面说的 sendReply() 逻辑,serviceManager 会通过驱动将 _aidl_reply 中存储的数据发给客户进程。

虽然传的是一个 binder 实体,但实际上传给驱动的只是实体的一个句柄(基本上这个实体不可能在 serviceManager 进程中),驱动根据驱动为客户进程创建一个 binder_ref,然后在客户进程中为实体生成一个句柄,再返回给客户进程。客户进程拿到句柄后,自己又生成一系列的代理,这就是 bp 调用 transact() 后拿到的对象。