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

news2024/11/16 1:33:03

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主要做了以下几件事情:

  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为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主要做了如下几件事情:

  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_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,做了如下事情:

  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驱动中获取到它,最常用的获取方式如下:

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

通过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创建一个远程代理对象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的远程服务代理就获取结束了。


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

请添加图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/677195.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

django中发送get post请求并获得数据

django中发送get post请求并获得数据 项目结构如下注册路由 urls.py在处理函数中处理请求 views.py进行 get的请求01浏览器 get请求传参数02服务器django get参数解析获取01浏览器 post的发送浏览器get 请求 获取页面返回的 form 发送post请求 带参数 02服务器django的post请求…

【Unity3D】平面光罩特效

1 前言 屏幕深度和法线纹理简介中对深度和法线纹理的来源、使用及推导过程进行了讲解&#xff0c;激光雷达特效中讲述了一种重构屏幕像素点世界坐标的方法&#xff0c;本文将沿用激光雷达特效中重构像素点世界坐标的方法&#xff0c;实现平面光罩特效。 假设平面光罩的高度为 s…

SpringCloud Alibaba入门7之引入服务网关Gateway

我们需要在客户端和服务端之间加一个统一的入口&#xff0c;来作为请求的统一接入&#xff0c;而在微服务的体系中&#xff0c;承担这个角色的就是网关。我们只需要将网关的机器IP配置到DNS,或者接入负载&#xff0c;那么客户端的服务最终通过我们的网关&#xff0c;再转发到对…

GEE:欧几里得距离——计算目标图像中每个像素到目标像素的距离

作者:CSDN @ _养乐多_ 利用欧几里得距离计算目标图像中每个像素到目标像素的距离,以量化像素与目标的接近程度。 结果如下图所示, 文章目录 一、欧几里得距离简介二、代码一、欧几里得距离简介 欧几里得距离(Euclidean distance)是在数学中常用的一种距离度量方式,用于…

Android PMS APP安装流程

仓库网址&#xff1a;http://androidxref.com/9.0.0_r3/xref/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java 一、PMS安装APP流程图 二、文件复制 PMS处理安装HandlerParams安装参数流程图 PackageManagerService.java#installStage…

职场求生记|唐朝打工人如何绝地求生

&#x1f4da;书名&#xff1a;《长安的荔枝》 ✏️作者&#xff1a;马伯庸 作为“见微”系列神作&#xff0c;其在微信读书总榜的第一名位置持续一段时间了&#xff0c;其讲述的内容和每个人都息息相关&#xff0c;更是能引起职场人的无限共鸣&#xff0c;值得深思。 ⭐故事…

使用networkx查看某一个节点的一阶/二阶/三阶邻居

文章目录 前言手动高级 前言 一般情况下&#xff0c;貌似这些图之类的包&#xff0c;只提供查询一个节点的一阶邻居&#xff0c;但是有的时候我们需要二阶甚至三阶&#xff0c;那么该如何做呢&#xff1f; 注意一下&#xff0c;本文的方法仅可以针对二阶或者三阶&#xff0c;…

一分钟 帮你搞懂什么是柔性数组!

文章目录 什么是柔性数组&#xff1f;柔性数组的特点柔性数组的使用模拟实现柔性数组的功能柔性数组的优势 什么是柔性数组&#xff1f; 柔性数组这个概念相信大多数人博友都没有听说过&#xff0c;但是它确实存在。 在C99中&#xff0c;结构&#xff08;结构体&#xff09;的…

【雕爷学编程】Arduino动手做(121)---夏普粉尘传感器模块

37款传感器与执行器的提法&#xff0c;在网络上广泛流传&#xff0c;其实Arduino能够兼容的传感器模块肯定是不止这37种的。鉴于本人手头积累了一些传感器和执行器模块&#xff0c;依照实践出真知&#xff08;一定要动手做&#xff09;的理念&#xff0c;以学习和交流为目的&am…

EMC学习笔记(十一)过孔

过孔 1.过孔模型1.1 过孔的数学模型1.2 对过孔模块的影响因素 2.过孔对信号传导与辐射发射影响2.2 过孔对阻抗控制的影响2.2 过孔数量对信号质量的影响 1.过孔模型 从过去设计的一些PCB板效果来看&#xff0c;过孔对于低频&#xff0c;低速信号的影响是很小的&#xff0c;但是…

Android 窗口实现原理

一、基本概念 1、窗口显示架构图 多窗口的核心原理其实就是分栈和设置栈边界2、Android的窗口分类 Android应用程序窗口,这个是最常见的&#xff08;拥有自己的WindowToken)譬如&#xff1a;Activity与Dialog Android应用程序子窗口&#xff08;必须依附到其他非子窗口才能存…

深度学习-第T11周——优化器对比实验

深度学习-第T11周——优化器对比实验 深度学习-第T11周——优化器对比实验一、前言二、我的环境三、前期工作1、导入数据集2、查看图片数目3、查看数据 四、数据预处理1、 加载数据1、设置图片格式2、划分训练集3、划分验证集4、查看标签 2、数据可视化3、检查数据4、配置数据集…

6月份读书学习好文记录

看看CHATGPT在最近几个月的发展趋势 https://blog.csdn.net/csdnnews/article/details/130878125?spm1000.2115.3001.5927 这是属于 AI 开发者的好时代&#xff0c;有什么理由不多去做一些尝试呢。 北大教授陈钟谈 AI 未来&#xff1a;逼近 AGI、融进元宇宙&#xff0c;开源…

06-浏览器渲染原理

什么是渲染&#xff1f; render&#xff0c;HTML字符串 --渲染--> 像素信息 URL地址是一个字符串&#xff0c;HTML、css、js都在里面 可以把渲染想象成一个函数&#xff0c;上代码&#xff1a; function render (html) {/* 第一行第二行*/return pixels; } 渲染时间点 …

【深入浅出 Spring Security(十二)】使用第三方(Github)授权登录

使用第三方&#xff08;Github&#xff09;授权登录 一、OAuth2 简单概述二、OAuth2 四种授权模式之授权码模式三、Github 授权登录准备工作创建 Spring Boot 项目Vue 测试代码测试效果 &#xff08;Github授权登录的具体操作在目录第三“章”&#xff09; 一、OAuth2 简单概述…

Spring Boot 优雅集成 Spring Security 5.7(安全框架)与 JWT(双令牌机制)

Spring Boot 集成 Spring Security &#xff08;安全框架&#xff09; 本章节将介绍 Spring Boot 集成 Spring Security 5.7&#xff08;安全框架&#xff09;。 &#x1f916; Spring Boot 2.x 实践案例&#xff08;代码仓库&#xff09; 介绍 Spring Security 是一个能够为基…

【CSDN创作纪念日】——博客小梦的“256”鸭~

博客小梦的创作纪念日&#x1f60e; 前言&#x1f64c;与CSDN的相遇浑水摸鱼的日常CSDN上的小小收获收获了 一群热爱编程&#xff0c;热爱创作的CSDN挚友创作上的小荣誉 憧憬未来 总结撒花&#x1f49e; &#x1f60e;博客昵称&#xff1a;博客小梦 &#x1f60a;最喜欢的座右铭…

【Spring】— Spring MVC简单数据绑定(一)

目录 Spring MVC数据绑定1.数据绑定概述2.简单数据绑定2.1 绑定默认数据类型2.2 绑定简单数据类型 Spring MVC数据绑定 1.数据绑定概述 在执行程序时&#xff0c;Spring MVC根据客户端请求参数的不同将请求消息中的信息以一定的方式转换并绑定到控制器类的方法参数中。这种将…

基于VMD-LSTM-IOWA-RBF的碳排放混合预测研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

第J3-1周:DenseNet算法 实现乳腺癌识别

目录 一、理论基础1.前言2.设计理念3.网络结构4.与其他算法效果对比 二、 前期准备1. 设置GPU2. 导入数据3. 划分数据集 三、搭建网络模型1. DenseLayer模块2. DenseBlock模块3. Transition模块4. 构建DenseNet5. 构建densenet121 四、 训练模型1. 编写训练函数2. 编写测试函数…