初始化
andriod 系统启动时会先由内核启动 init 进程,init 进程通过 init.rc 启动 service_manager 以及 zygote 进程,zygote 然后启动 system_server 以及各种应用进程。也就是说sm 的启动先于所有应用进程
sm 启动后有几件准备工作要做,只有做完之后才可以成为真正的管家,接受客户端的调用:
- 打开 binder 驱动
- 将自己注册成 service_manager
- 通过循环等待事件到来。在这一步之前,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 读取。
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() 后拿到的对象。