1 ServiceManager的启动
1.1 服务的启动与注册
上一篇笔记中有说到,ServiceManager
是一个特殊的binder service,所以它和普通的service一样需要打开binder驱动,在驱动中创建一个属于ServiceManager
进程的binder_proc
。
int main(int argc, char** argv) {
const char* driver = argc == 2 ? argv[1] : "/dev/binder";
// 打开Binder驱动,mmap
sp<ProcessState> ps = ProcessState::initWithDriver(driver);
ps->setThreadPoolMaxThreadCount(0);
ps->setCallRestriction(ProcessState::CallRestriction::FATAL_IF_NOT_ONEWAY);
// 创建ServiceManager对象
sp<ServiceManager> manager = sp<ServiceManager>::make(std::make_unique<Access>());
// 将ServiceManager自身先加入到服务列表当中
if (!manager->addService("manager", manager, false /*allowIsolated*/, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk()) {
LOG(ERROR) << "Could not self register servicemanager";
}
IPCThreadState::self()->setTheContextObject(manager);
// 通知binder driver,我就是servicemanager
ps->becomeContextManager();
// 监听消息
sp<Looper> looper = Looper::prepare(false /*allowNonCallbacks*/);
BinderCallback::setupTo(looper);
ClientCallbackCallback::setupTo(looper, manager);
while(true) {
looper->pollAll(-1);
}
return EXIT_FAILURE;
}
那它和普通service不一样的地方在哪里呢?就在于ServiceManager
需要告诉binder驱动它就是servicemanager,这通过调用becomeContextManager完成。
bool ProcessState::becomeContextManager()
{
AutoMutex _l(mLock);
flat_binder_object obj {
.flags = FLAT_BINDER_FLAG_TXN_SECURITY_CTX,
};
int result = ioctl(mDriverFD, BINDER_SET_CONTEXT_MGR_EXT, &obj);
return result == 0;
}
接下来我们到内核态中看看ioctl对应做了什么:
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
case BINDER_SET_CONTEXT_MGR_EXT: {
struct flat_binder_object fbo;
// 将cmd从用户态copy过来
if (copy_from_user(&fbo, ubuf, sizeof(fbo))) {
ret = -EINVAL;
goto err;
}
ret = binder_ioctl_set_ctx_mgr(filp, &fbo);
if (ret)
goto err;
break;
}
}
static int binder_ioctl_set_ctx_mgr(struct file *filp,
struct flat_binder_object *fbo)
{
int ret = 0;
// 1. 获取binder_proc
struct binder_proc *proc = filp->private_data;
// 2. 获取binder_proc 中保存的 binder_context
struct binder_context *context = proc->context;
// 创建一个binder_node
struct binder_node *new_node;
kuid_t curr_euid = current_euid();
mutex_lock(&context->context_mgr_node_lock);
// 判断是否已经注册有servicemanager
if (context->binder_context_mgr_node) {
pr_err("BINDER_SET_CONTEXT_MGR already set\n");
ret = -EBUSY;
goto out;
}
ret = security_binder_set_context_mgr(proc->cred);
if (uid_valid(context->binder_context_mgr_uid)) {
} else {
context->binder_context_mgr_uid = curr_euid;
}
// 3. 用binder_proc实例化binder_node
new_node = binder_new_node(proc, fbo);
if (!new_node) {
ret = -ENOMEM;
goto out;
}
binder_node_lock(new_node);
// 强弱引用计数加 1
new_node->local_weak_refs++;
new_node->local_strong_refs++;
new_node->has_strong_ref = 1;
new_node->has_weak_ref = 1;
// 4. 将binder_context中的binder_context_mgr_node 指向 servicemanager进程对应的binder_node
context->binder_context_mgr_node = new_node;
binder_node_unlock(new_node);
binder_put_node(new_node);
out:
mutex_unlock(&context->context_mgr_node_lock);
return ret;
}
binder_ioctl处理BINDER_SET_CONTEXT_MGR_EXT这条cmd主要做了以下几件事情:
- 从fd中获取
binder_proc
,binder_proc
指代的是ServiceManager所在进程; - 获取
binder_proc
中的binder_context
成员,从上一篇我们可以知道binder_context
指向的是binder_device
中的binder_context
成员(ServiceManager),只不过在ServiceManager进程起来前该binder_context
成员为空; - 用
binder_proc
实例化binder_node
; - 将
binder_context
中的binder_context_mgr_node
指向 servicemanager进程对应的;binder_node
,到这里binder_device
中的binder_context
成员就不为空了,binder驱动也就指定了ServiceManager。
1.2 服务的监听
ServiceManager注册完成之后,就会开始监听binder驱动,查看是否有消息发给自己,接下来我们一起看看它是如何监听消息的。
class BinderCallback : public LooperCallback {
public:
static sp<BinderCallback> setupTo(const sp<Looper>& looper) {
sp<BinderCallback> cb = sp<BinderCallback>::make();
int binder_fd = -1;
IPCThreadState::self()->setupPolling(&binder_fd);
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.
}
};
ServiceManager
监听的是打开binder驱动时返回的fd,如果有事件则调用IPCThreadState
的handlePolledCommands方法来处理。
status_t IPCThreadState::handlePolledCommands()
{
do {
result = getAndExecuteCommand();
} while (mIn.dataPosition() < mIn.dataSize());
}
status_t IPCThreadState::getAndExecuteCommand()
{
status_t result;
int32_t cmd;
// 1. 与binder驱动进行通信
result = talkWithDriver();
if (result >= NO_ERROR) {
size_t IN = mIn.dataAvail();
cmd = mIn.readInt32();
// 2. 收到数据执行命令
result = executeCommand(cmd);
}
return result;
}
一路追过去我们发现,ServiceManager
会调用talkWithDriver与binder驱动进行通信,如果有数据就调用executeCommand执行命令。
status_t IPCThreadState::talkWithDriver(bool doReceive)
{
// 1. 创建binder_write_read对象
binder_write_read bwr;
const bool needRead = mIn.dataPosition() >= mIn.dataSize();
const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0;
bwr.write_size = outAvail;
bwr.write_buffer = (uintptr_t)mOut.data();
// servicemanager中的监听指的接收,所以这里的doReceive为true
if (doReceive && needRead) {
bwr.read_size = mIn.dataCapacity();
// 将binder_write_read的buffer指向mIn的buffer
bwr.read_buffer = (uintptr_t)mIn.data();
} else {
bwr.read_size = 0;
bwr.read_buffer = 0;
}
// 初始化其他成员
bwr.write_consumed = 0;
bwr.read_consumed = 0;
status_t err;
do {
// 2. 调用ioctl与binder驱动通讯,传入参数为binder_write_read
if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
err = NO_ERROR;
else
err = -errno;
} while (err == -EINTR);
if (err >= NO_ERROR) {
if (bwr.write_consumed > 0) {
if (bwr.write_consumed < mOut.dataSize())
else {
mOut.setDataSize(0);
processPostWriteDerefs();
}
}
if (bwr.read_consumed > 0) {
mIn.setDataSize(bwr.read_consumed);
mIn.setDataPosition(0);
}
return NO_ERROR;
}
return err;
}
talkWithDriver主要做了如下几件事情:
- 构建并初始化
binder_write_read
,如果是发送(doReceive=true),则将binder_write_read
中的参数用mIn初始化,否则用mOut初始化; - 调用ioctl与binder驱动通讯读取数据,传入参数为刚刚构建的
binder_write_read
,cmd为BINDER_WRITE_READ;
接下来我们一起进入内核态看看binder_ioctl中对应BINDER_WRITE_READ的分支做了什么。
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
case BINDER_WRITE_READ:
ret = binder_ioctl_write_read(filp, cmd, arg, thread);
if (ret)
goto err;
break;
}
static int binder_ioctl_write_read(struct file *filp,
unsigned int cmd, unsigned long arg,
struct binder_thread *thread)
{
int ret = 0;
struct binder_proc *proc = filp->private_data;
unsigned int size = _IOC_SIZE(cmd);
void __user *ubuf = (void __user *)arg;
struct binder_write_read bwr;
// 1. 从用户态拷贝binder_write_read
if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
ret = -EFAULT;
goto out;
}
// 这里是给binder驱动发送数据,暂时不看
if (bwr.write_size > 0) {
ret = binder_thread_write(proc, thread,
bwr.write_buffer,
bwr.write_size,
&bwr.write_consumed);
trace_binder_write_done(ret);
if (ret < 0) {
bwr.read_consumed = 0;
if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
ret = -EFAULT;
goto out;
}
}
// 这里是从binder驱动收取数据,先看这里
if (bwr.read_size > 0) {
// 2. 读取数据
ret = binder_thread_read(proc, thread, bwr.read_buffer,
bwr.read_size,
&bwr.read_consumed,
filp->f_flags & O_NONBLOCK);
}
// 3. 将数据拷贝到用户空间
if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {
ret = -EFAULT;
goto out;
}
out:
return ret;
}
读取数据主要调用了binder_ioctl_write_read,做了如下事情:
- 创建
binder_write_read
对象bwr,并且从将用户态的数据拷贝到该对象中; - 调用binder_thread_read读取数据到bwr;
- 将bwr中的数据重新拷贝会用户态。
因为还没有数据传过来,所以binder_thread_read我们暂不分析,后面看到数据发送后我们再一起看。
同样的,返回用户态之后executeCommand我们暂时也先不看。
题外话:做笔记的时候不可避免的贴了很多代码,这是我很不愿意做的,因为对不了解代码的同学来说,虽然有注释但是看来依旧非常困难。我更希望能够从全局的角度了解代码执行的流程,所以我可能会在每一节最后做一个总结,尽管可能会和前文内容有重复。
总结起来ServiceManager
监听binder驱动的动作如下:
- 用户态中,执行talkWithDriver与binder驱动做通信,首先构建
binder_write_read
,执行iotcl发送BINDER_WRITE_READ命令读取数据;内核态中,先将用户态的cmd参数拷贝到内核态,接着调用binder_thread_read读取数据,最后将数据拷贝回用户态; - 用户态中,收到返回的数据后执行executeCommand处理数据。
2 获取ServiceManager
使用ServiceManager
之前,我们肯定要从binder驱动中获取到它,最常用的获取方式如下:
// 获取servicemanager
sp<IServiceManager> sm(defaultServiceManager());
可以看到获取ServiceManager
代理的方式和获取普通服务代理有一些不一样,普通服务代理需要通过ServiceManager
来查询并获取,例如:
sp<IBinder> binder = sm->getService(String16("media.camera"));
那ServiceManager
的远程代理要通过什么来获得呢?那android自然是开了后门的,一起来看看吧。
sp<IServiceManager> defaultServiceManager()
{
std::call_once(gSmOnce, []() {
sp<AidlServiceManager> sm = nullptr;
while (sm == nullptr) {
sm = interface_cast<AidlServiceManager>(ProcessState::self()->getContextObject(nullptr));
}
gDefaultServiceManager = sp<ServiceManagerShim>::make(sm);
});
return gDefaultServiceManager;
}
通过ProcessState
的getContextObject方法,并且将参数设置为NULL
即可获取ServiceManager的代理。
sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& /*caller*/)
{
sp<IBinder> context = getStrongProxyForHandle(0);
return context;
}
sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{
sp<IBinder> result;
AutoMutex _l(mLock);
// 1. 查询是否有handle对应的远程代理,如果没有则创建一个
handle_entry* e = lookupHandleLocked(handle);
if (e != nullptr) {
IBinder* b = e->binder;
if (b == nullptr || !e->refs->attemptIncWeak(this)) {
// 这个判断中的代码可以不用看
if (handle == 0) {
IPCThreadState* ipc = IPCThreadState::self();
CallRestriction originalCallRestriction = ipc->getCallRestriction();
ipc->setCallRestriction(CallRestriction::NONE);
Parcel data;
status_t status = ipc->transact(
0, IBinder::PING_TRANSACTION, data, nullptr, 0);
ipc->setCallRestriction(originalCallRestriction);
if (status == DEAD_OBJECT)
return nullptr;
}
// 用Handle创建一个远程代理对象BpXXX
sp<BpBinder> b = BpBinder::PrivateAccessor::create(handle);
e->binder = b.get();
if (b) e->refs = b->getWeakRefs();
result = b;
} else {
result.force_set(b);
e->refs->decWeak(this);
}
}
return result;
}
这里贴了比较长的代码,核心就是getStrongProxyForHandle,从名字来看意为,用从binder驱动获取的handle来创建一个强引用对象。
为ServiceManager
创建这个强引用对象时,用的handle为0,这是预先设定好的,意思是handle为0的代理就是ServiceManager
。这要如何理解呢?我们要看看一个方法lookupHandleLocked。
ProcessState::handle_entry* ProcessState::lookupHandleLocked(int32_t handle)
{
const size_t N=mHandleToObject.size();
if (N <= (size_t)handle) {
handle_entry e;
e.binder = nullptr;
e.refs = nullptr;
status_t err = mHandleToObject.insertAt(e, N, handle+1-N);
if (err < NO_ERROR) return nullptr;
}
return &mHandleToObject.editItemAt(handle);
}
getStrongProxyForHandle一开始就调用了这个方法lookupHandleLocked,有一个比较有意思的点,里面使用handle和mHandleToObject
中元素的数量做对比。为什么可以这样对比,解释大概如下:每个进程中的handle都是从0开始计数增加的,mHandleToObject
中元素的数量代表的是该进程中引用了几个服务,由于获取其他服务前需要先获取ServiceManager
的代理,所以ServiceManager
必须是第一个被引用的服务,所以handle值为0。
handle值计数的设定由是如何设定的这里暂不讨论,另外这里还要留下一个问题,创建Bpxxx时引用计数需要如何处理?后面会继续研究。
到这里ServiceManager的远程服务代理就获取结束了。
天气越来越热,但是心要保持平静~