Android 13(T) - binder阅读(2)- ServiceManager的启动与获取

news/2024/10/23 5:47:16/

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驱动,mmapsp<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,我就是servicemanagerps->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_procstruct binder_proc *proc = filp->private_data;// 2. 获取binder_proc 中保存的 binder_contextstruct binder_context *context = proc->context;// 创建一个binder_nodestruct binder_node *new_node;kuid_t curr_euid = current_euid();mutex_lock(&context->context_mgr_node_lock);// 判断是否已经注册有servicemanagerif (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_nodenew_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主要做了以下几件事情:

  1. fd中获取binder_procbinder_proc指代的是ServiceManager所在进程;
  2. 获取binder_proc 中的 binder_context成员,从上一篇我们可以知道binder_context 指向的是binder_device中的binder_context成员(ServiceManager),只不过在ServiceManager进程起来前该binder_context成员为空;
  3. binder_proc实例化binder_node
  4. 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,如果有事件则调用IPCThreadStatehandlePolledCommands方法来处理。

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为trueif (doReceive && needRead) {bwr.read_size = mIn.dataCapacity();// 将binder_write_read的buffer指向mIn的bufferbwr.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_readif (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)err = NO_ERROR;elseerr = -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主要做了如下几件事情:

  1. 构建并初始化binder_write_read,如果是发送(doReceive=true),则将binder_write_read中的参数用mIn初始化,否则用mOut初始化;
  2. 调用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_readif (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,做了如下事情:

  1. 创建binder_write_read对象bwr,并且从将用户态的数据拷贝到该对象中;
  2. 调用binder_thread_read读取数据到bwr;
  3. 将bwr中的数据重新拷贝会用户态。

因为还没有数据传过来,所以binder_thread_read我们暂不分析,后面看到数据发送后我们再一起看。

同样的,返回用户态之后executeCommand我们暂时也先不看。

题外话:做笔记的时候不可避免的贴了很多代码,这是我很不愿意做的,因为对不了解代码的同学来说,虽然有注释但是看来依旧非常困难。我更希望能够从全局的角度了解代码执行的流程,所以我可能会在每一节最后做一个总结,尽管可能会和前文内容有重复。

总结起来ServiceManager监听binder驱动的动作如下:

  1. 用户态中,执行talkWithDriver与binder驱动做通信,首先构建binder_write_read,执行iotcl发送BINDER_WRITE_READ命令读取数据;内核态中,先将用户态的cmd参数拷贝到内核态,接着调用binder_thread_read读取数据,最后将数据拷贝回用户态;
  2. 用户态中,收到返回的数据后执行executeCommand处理数据。

2 获取ServiceManager

使用ServiceManager之前,我们肯定要从binder驱动中获取到它,最常用的获取方式如下:

    // 获取servicemanagersp<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;
}

通过ProcessStategetContextObject方法,并且将参数设置为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创建一个远程代理对象BpXXXsp<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的远程服务代理就获取结束了。


天气越来越热,但是心要保持平静~

请添加图片描述


http://www.ppmy.cn/news/535641.html

相关文章

node.js--vue仓库进销存管理信息系统whkb8

随着社会的发展&#xff0c;系统的管理形势越来越严峻。越来越多的用户利用互联网获得信息&#xff0c;但各种信息鱼龙混杂&#xff0c;信息真假难以辨别。为了方便用户更好的获得仓库管理信息&#xff0c;因此&#xff0c;设计一种安全高效的仓库管理信息系统极为重要。 为设计…

Linux模块文件编译到内核与独立编译成.ko文件的方法

很多粉丝在群里提问&#xff0c;如何把一个模块文件编译到内核中或者独立变异成ko文件。本文给大家详解讲解。 1. 内核目录 Linux内核源代码非常庞大&#xff0c;随着版本的发展不断增加。它使用目录树结构&#xff0c;并且使用Makefile组织配置、编译。 初次接触Linux内核&…

LeetCode 力扣477. 汉明距离总和 最易理解解法

两个整数的 汉明距离 指的是这两个数字的二进制数对应位不同的数量。 给你一个整数数组 nums&#xff0c;请你计算并返回 nums 中任意两个数之间 汉明距离的总和 。 class Solution {public int totalHammingDistance(int[] nums) {int ans 0;//遍历32位for (int i 0; i &…

LeetCode-473

火柴拼正方形 你将得到一个整数数组 matchsticks &#xff0c;其中 matchsticks[i] 是第 i 个火柴棒的长度。你要用 所有的火柴棍 拼成一个正方形。你 不能折断 任何一根火柴棒&#xff0c;但你可以把它们连在一起&#xff0c;而且每根火柴棒必须 使用一次 。如果你能使这个正…

leetcode 477. Total Hamming Distance | 477. 汉明距离总和

题目 https://leetcode.com/problems/total-hamming-distance/ 题解 class Solution {public int totalHammingDistance(int[] nums) {int N nums.length;int[] count new int[32];for (int n : nums) {for (int i 0; i < 32; i) {count[i] (n >> i) & 1;}…

477-82(236、61、47、74、240、93)

236. 二叉树的最近公共祖先 class Solution { public:TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {if (root p || root q || root nullptr) return root;TreeNode* left lowestCommonAncestor(root->left, p, q);TreeNode* right l…

LeetCode 477 汉明距离总和

题目链接 两个整数的 汉明距离 指的是这两个数字的二进制数对应位不同的数量。 计算一个数组中&#xff0c;任意两个数之间汉明距离的总和。 示例: 输入: 4, 14, 2 输出: 6 解释: 在二进制表示中&#xff0c;4表示为0100&#xff0c;14表示为1110&#xff0c;2表示为0010。…

477. 汉明距离总和(中等,位运算)

題目&#xff1a; 分析1&#xff0c;统计每一位的1个数&#xff1a;T了。 class Solution:def totalHammingDistance(self, nums: List[int]) -> int:a len(nums) # 总个数if len(nums)0 :return 0a2 max(nums)c [0 for i in range(0,a2)]for i in nums:if i0:continues…