Android IPC机制4-ServiceManager的addService与getService实现

2024-06-05 16:18

本文主要是介绍Android IPC机制4-ServiceManager的addService与getService实现,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

普通client或者server在获得servicemanger的proxy对象后,肯定就要使用了。对于server来说,主要是调用addService,向serivceManager注册。而client则是通过serivcemanager查询所需server的信息,然后得到server的proxy对象。

注册服务-addService

以Native层的服务mediaservice为例,我们先来分析下server是如何向SerivceManager注册的吧

先来看入口 main_mediaserver.cpp的main函数中的与ServiceManager相关的代码:

int main(int argc __unused, char** argv)
{...//获得ProcessState实例对象sp<ProcessState> proc(ProcessState::self());     //获取ServiceManager实例对象 【既BpServiceManager】sp<IServiceManager> sm = defaultServiceManager(); AudioFlinger::instantiate(); //多媒体服务            MediaPlayerService::instantiate();               ResourceManagerService::instantiate(); CameraService::instantiate();         AudioPolicyService::instantiate();  SoundTriggerHwService::instantiate(); RadioService::instantiate(); registerExtensions();//创建Binder线程,并加入线程池ProcessState::self()->startThreadPool();  //当前线程加入到线程池    IPCThreadState::self()->joinThreadPool();    }

proc(ProcessState::self())

首先调用的函数是ProcessState::self(),获得ProcessState对象,ProcessState位置在framework\base\libs\binder\ProcessState.cpp,在上一篇文章里有过讲述。其内部有Binder驱动的一些配置,每个进程只有一个。

获取到ProcessState对象后赋值给了proc变量,程序运行完,proc会自动delete内部的内容,所以就自动释放了先前分配的资源。

MediaPlayerService:instantiate

void MediaPlayerService::instantiate() {defaultServiceManager()->addService(String16("media.player"), new MediaPlayerService()); 
}

MediaPlayerService的初始化其实就是调用defaultServiceManager的addserivce方法向servicemanager注册,而上一篇已经讲过了defaultServiceManager,此处不在赘述。defaultServiceManager最终得到的是BpServiceManager。
下面来看看BpServiceManager的addService方法:

virtual status_t addService(const String16& name, const sp<IBinder>& service,bool allowIsolated)
{Parcel data, reply; //Parcel是数据通信包data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());   //先把Interface名字写进去,也就是什么android.os.IServiceManagerdata.writeString16(name);        // name为 "media.player"data.writeStrongBinder(service); // MediaPlayerService对象data.writeInt32(allowIsolated ? 1 : 0); // allowIsolated= falsestatus_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply); //return err == NO_ERROR ? reply.readExceptionCode() : 
}

上面函数的核心就是status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply); ,而remote()就是创建bpservicemanager时的bpbinder(handle=0)

BpBinder::transact()

来看看BpBinder的Transact函数:

status_t BpBinder::transact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{if (mAlive) {status_t status = IPCThreadState::self()->transact(mHandle, code, data, reply, flags); if (status == DEAD_OBJECT) mAlive = 0;return status;}return DEAD_OBJECT;
}

可以发现,transact函数还不是在这个执行的,而是调用的IPCThreadState的transact.

IPCThreadState::self

看到这个形式就知道肯定是单例模式了,确实也是这样

IPCThreadState* IPCThreadState::self()
{if (gHaveTLS) { 
restart:const pthread_key_t k = gTLS;IPCThreadState* st = (IPCThreadState*)pthread_getspecific(k);if (st) return st;return new IPCThreadState;  //初始IPCThreadState }if (gShutdown) return NULL;pthread_mutex_lock(&gTLSMutex);if (!gHaveTLS) { //首次进入gHaveTLS为falseif (pthread_key_create(&gTLS, threadDestructor) != 0) { //创建线程的TLSpthread_mutex_unlock(&gTLSMutex);return NULL;}gHaveTLS = true;}pthread_mutex_unlock(&gTLSMutex);goto restart;
}IPCThreadState::IPCThreadState(): mProcess(ProcessState::self()),mMyThreadId(gettid()),    mStrictModePolicy(0),mLastTransactionBinderFlags(0)
{pthread_setspecific(gTLS, this);clearCaller();mIn.setDataCapacity(256);mOut.setDataCapacity(256);
}

TLS是指Thread local storage(线程本地储存空间),每个线程都拥有自己的TLS,并且是私有空间,线程之间不会共享,,和java中的ThreadLocal是差不多的概念。通过pthread_getspecific/pthread_setspecific函数可以获取/设置这些空间中的内容。从线程本地存储空间中获得保存在其中的IPCThreadState对象。

IPCThreadState的构造函数中则是对所属进程,现成id,优先级等做了一些设置。需要注意的是mInmOut,后面会用到。

IPCThreadState::transact

回到IPCThreadState::transact这个方法,看看是怎么实现的。

status_t IPCThreadState::transact(int32_t handle,uint32_t code, const Parcel& data,Parcel* reply, uint32_t flags)
{status_t err = data.errorCheck(); //数据错误检查      flags |= TF_ACCEPT_FDS;     ....if (err == NO_ERROR) {//调用writeTransactionData 发送数据err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);}if (err != NO_ERROR) {if (reply) reply->setError(err);return (mLastError = err);}if ((flags & TF_ONE_WAY) == 0) { //flgs=0进入该分支if (reply) {//等待响应  err = waitForResponse(reply);} else {Parcel fakeReply;err = waitForResponse(&fakeReply);}} else {//不需要响应消息的binder则进入该分支err = waitForResponse(NULL, NULL); }return err;
}

上面代码的核心就是writeTransactionData 发送数据,waitForResponse等待响应,来看看这两个函数
writeTransactionData:

status_t IPCThreadState::writeTransactionData(int32_t cmd, uint32_t binderFlags,int32_t handle, uint32_t code, const Parcel& data, status_t* statusBuffer)
{binder_transaction_data tr;tr.target.ptr = 0;tr.target.handle = handle; // handle=0tr.code = code;            // ADD_SERVICE_TRANSACTIONtr.flags = binderFlags;    // 0 tr.cookie = 0;tr.sender_pid = 0;tr.sender_euid = 0;const status_t err = data.errorCheck();if (err == NO_ERROR) {tr.data_size = data.ipcDataSize();  tr.data.ptr.buffer = data.ipcData();tr.offsets_size = data.ipcObjectsCount()*sizeof(binder_size_t);tr.data.ptr.offsets = data.ipcObjects();} else if (statusBuffer) {tr.flags |= TF_STATUS_CODE;*statusBuffer = err;tr.data_size = sizeof(status_t);tr.data.ptr.buffer = reinterpret_cast<uintptr_t>(statusBuffer);tr.offsets_size = 0;tr.data.ptr.offsets = 0;} else {return (mLastError = err);}
//上面把命令数据封装成binder_transaction_data,然后
//写到mOut中,mOut是命令的缓冲区,也是一个ParcelmOut.writeInt32(cmd);         //cmd = BC_TRANSACTIONmOut.write(&tr, sizeof(tr));  //写入binder_transaction_data数据return NO_ERROR;
}

其中handle的值用来标识目的端,注册服务过程的目的端为service manager,此处handle=0所对应的是binder_context_mgr_node对象,正是service manager所对应的binder实体对象。binder_transaction_data结构体是binder驱动通信的数据结构,该过程最终是把Binder请求码BC_TRANSACTION和binder_transaction_data结构体写入到mOut。

再来看看waitForResponse函数

status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{int32_t cmd;int32_t err;while (1) {if ((err=talkWithDriver()) < NO_ERROR) break; // err = mIn.errorCheck();if (err < NO_ERROR) break;if (mIn.dataAvail() == 0) continue;//这里开始操作mIn了,看来talkWithDriver中
//把mOut发出去,然后从driver中读到数据放到mIn中了。cmd = mIn.readInt32();switch (cmd) {case BR_TRANSACTION_COMPLETE:if (!reply && !acquireResult) goto finish;break;case BR_DEAD_REPLY:err = DEAD_OBJECT;goto finish;case BR_FAILED_REPLY:err = FAILED_TRANSACTION;goto finish;case BR_ACQUIRE_RESULT:{const int32_t result = mIn.readInt32();if (!acquireResult) continue;*acquireResult = result ? NO_ERROR : INVALID_OPERATION;}goto finish;case BR_REPLY:{binder_transaction_data tr;err = mIn.read(&tr, sizeof(tr));if (err != NO_ERROR) goto finish;if (reply) {if ((tr.flags & TF_STATUS_CODE) == 0) {reply->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);} else {err = *reinterpret_cast<const status_t*>(tr.data.ptr.buffer);freeBuffer(NULL,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), this);}} else {freeBuffer(NULL,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), this);continue;}}goto finish;default:err = executeCommand(cmd);  //if (err != NO_ERROR) goto finish;break;}}finish:if (err != NO_ERROR) {if (acquireResult) *acquireResult = err;if (reply) reply->setError(err);mLastError = err;}

上面代码的细节就不深究了,到这里,我们发送addService的流程就彻底走完了。
其实就是BpServiceManager发送了一个addService命令到BnServiceManager,然后收到回复。

回到开始的mediaservice的main函数,在将MediaPlayerService、CameraService、SoundTriggerHwService等服务都初始化后,就会走到下面的语句:

ProcessState::self()->startThreadPool();  //当前线程加入到线程池    
IPCThreadState::self()->joinThreadPool(); 

进入两个函数内部看看

...看看startThreadPool吧void ProcessState::startThreadPool(){...spawnPooledThread(true);}void ProcessState::spawnPooledThread(bool isMain){sp<Thread> t = new PoolThread(isMain);isMain是TRUE//创建线程池,然后run起来,和java的Threadf非常像t->run(buf);}//PoolThread从Thread类中派生,那么此时会产生一个线程吗?看看PoolThread和Thread的构造吧PoolThread::PoolThread(bool isMain): mIsMain(isMain){}
//再来看看父类
Thread::Thread(bool canCallJava)//canCallJava默认值是true:   mCanCallJava(canCallJava),mThread(thread_id_t(-1)),mLock("Thread::mLock"),mStatus(NO_ERROR),mExitPending(false), mRunning(false){}//喔,这个时候还没有创建线程呢。然后调用PoolThread::run,实际调用了基类的run。status_t Thread::run(const char* name, int32_t priority, size_t stack){bool res;if (mCanCallJava) {res = createThreadEtc(_threadLoop,//线程函数是_threadLoopthis, name, priority, stack, &mThread);}//终于,在run函数中,创建线程了。从此//主线程执行IPCThreadState::self()->joinThreadPool();新开的线程执行_threadLoop//我们先看看_threadLoopint Thread::_threadLoop(void* user){Thread* const self = static_cast<Thread*>(user);sp<Thread> strong(self->mHoldSelf);wp<Thread> weak(strong);self->mHoldSelf.clear();do {...if (result && !self->mExitPending) {result = self->threadLoop();哇塞,调用自己的threadLoop}}//我们是PoolThread对象,所以调用PoolThread的threadLoop函数virtual bool PoolThread ::threadLoop(){//mIsMain为true。//而且注意,这是一个新的线程,所以必然会创建一个//新的IPCThreadState对象(记得线程本地存储吗?TLS),然后      IPCThreadState::self()->joinThreadPool(mIsMain);return false;}//主线程和工作线程都调用了joinThreadPool,看看这个干嘛了!void IPCThreadState::joinThreadPool(bool isMain){mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER);status_t result;do {int32_t cmd;result = talkWithDriver();result = executeCommand(cmd);}} while (result != -ECONNREFUSED && result != -EBADF);mOut.writeInt32(BC_EXIT_LOOPER);talkWithDriver(false);}//看到没?有loop了,但是好像是有两个线程都执行了这个啊!这里有两个消息循环?//下面看看executeCommandstatus_t IPCThreadState::executeCommand(int32_t cmd){BBinder* obj;RefBase::weakref_type* refs;status_t result = NO_ERROR;case BR_TRANSACTION:{binder_transaction_data tr;result = mIn.read(&tr, sizeof(tr));//来了一个命令,解析成BR_TRANSACTION,然后读取后续的信息Parcel reply;if (tr.target.ptr) {//这里用的是BBinder。(因为自己是做server端)sp<BBinder> b((BBinder*)tr.cookie);const status_t error = b->transact(tr.code, buffer, &reply, 0);}//让我们看看BBinder的transact函数干嘛了status_t BBinder::transact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags){//就是调用自己的onTransact函数嘛      err = onTransact(code, data, reply, flags);return err;}

BnMediaPlayerService从BBinder派生,所以会调用到它的onTransact函数
终于水落石出了,让我们看看BnMediaPlayerServcice的onTransact函数。

status_t BnMediaPlayerService::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags){// BnMediaPlayerService从BBinder和IMediaPlayerService派生,所有IMediaPlayerService//看到下面的switch没?所有IMediaPlayerService提供的函数都通过命令类型来区分//switch(code) {case CREATE_URL: {CHECK_INTERFACE(IMediaPlayerService, data, reply);create是一个虚函数,由MediaPlayerService来实现!!sp<IMediaPlayer> player = create(pid, client, url, numHeaders > 0 ? &headers : NULL);reply->writeStrongBinder(player->asBinder());return NO_ERROR;} break;

其实,到这里,我们就明白了。BnXXX的onTransact函数收取命令,然后派发到派生类的函数,由他们完成实际的工作。
说明:
这里有点特殊,startThreadPool和joinThreadPool完后确实有两个线程,主线程和工作线程,而且都在做消息循环。

小节

过程分析:

  1. MediaPlayerService进程获得bpservicemanager,通过bpservicemanager对象的bpbinder成员(handl=0)中转到IPCThreadState执行transact

  2. IPCThreadState调用writeTransactionData函数向mout缓冲区写入数据,然后调用waitForResponse等待响应。

  3. mediaservice开启一个工作线程,和主线程一起开始做消息循环,不断的与binder驱动通信,故后续更加需求Binder驱动会增加binder线程个数。

获取服务-getService

再来看看client如何通过servicemanager获得server的bpbinder的。

getService

首先来看看IServiceManager的getService函数:

virtual sp<IBinder> getService(const String16& name) const{unsigned n;for (n = 0; n < 5; n++){sp<IBinder> svc = checkService(name);if (svc != NULL) return svc;sleep(1);}return NULL;}

核心是checkService(name),进入代码看看

virtual sp<IBinder> checkService( const String16& name) const
{Parcel data, reply;//写入RPC头data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());//写入服务名data.writeString16(name); remote()->transact(CHECK_SERVICE_TRANSACTION, data, &reply); return reply.readStrongBinder();
}

remote()->transact(CHECK_SERVICE_TRANSACTION, data, &reply);,这句和之前addService的非常类似,肯定又是通过bpservicemanager最终流转到IPCThreadState的transact,只是传递的参数有所不同罢了。 这里就不再讲了。

总结

本片文章说是讲ServiceManager的addServicegetService,其实主要还是在分析transact这个函数的执行流转过程,上面有些代码细节我也没有完全弄懂,但我觉得也够了。简单的说ServiceMnager通信的核心有以下几个:

  1. IPCProcessState和IPCThreadState这两个类,他们分别记录了进程和线程中IPC通信需要的相关要素。这两个类都是单例模式,IPCThreadState是最终通信的执行者。
  2. BpBinder:通过handl=0可以直接与ServiceManager的实体Binder通信
  3. Server端会在主线程和一个工作线程开始消息循环,来处理和binder驱动的交互,如有需求再开启新的线程。
  4. 函数调用的流转过程:通过一次次的流转,每个部分都做了需要自己做的事,然后由一个最终的执行者去执行。

文章里很多部分都来自与gityuan 和innost,在此表示感谢!

这篇关于Android IPC机制4-ServiceManager的addService与getService实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1033552

相关文章

C#实现将Excel表格转换为图片(JPG/ PNG)

《C#实现将Excel表格转换为图片(JPG/PNG)》Excel表格可能会因为不同设备或字体缺失等问题,导致格式错乱或数据显示异常,转换为图片后,能确保数据的排版等保持一致,下面我们看看如何使用C... 目录通过C# 转换Excel工作表到图片通过C# 转换指定单元格区域到图片知识扩展C# 将 Excel

Android Mainline基础简介

《AndroidMainline基础简介》AndroidMainline是通过模块化更新Android核心组件的框架,可能提高安全性,本文给大家介绍AndroidMainline基础简介,感兴趣的朋... 目录关键要点什么是 android Mainline?Android Mainline 的工作原理关键

基于Java实现回调监听工具类

《基于Java实现回调监听工具类》这篇文章主要为大家详细介绍了如何基于Java实现一个回调监听工具类,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录监听接口类 Listenable实际用法打印结果首先,会用到 函数式接口 Consumer, 通过这个可以解耦回调方法,下面先写一个

使用Java将DOCX文档解析为Markdown文档的代码实现

《使用Java将DOCX文档解析为Markdown文档的代码实现》在现代文档处理中,Markdown(MD)因其简洁的语法和良好的可读性,逐渐成为开发者、技术写作者和内容创作者的首选格式,然而,许多文... 目录引言1. 工具和库介绍2. 安装依赖库3. 使用Apache POI解析DOCX文档4. 将解析

Qt中QGroupBox控件的实现

《Qt中QGroupBox控件的实现》QGroupBox是Qt框架中一个非常有用的控件,它主要用于组织和管理一组相关的控件,本文主要介绍了Qt中QGroupBox控件的实现,具有一定的参考价值,感兴趣... 目录引言一、基本属性二、常用方法2.1 构造函数 2.2 设置标题2.3 设置复选框模式2.4 是否

C++使用printf语句实现进制转换的示例代码

《C++使用printf语句实现进制转换的示例代码》在C语言中,printf函数可以直接实现部分进制转换功能,通过格式说明符(formatspecifier)快速输出不同进制的数值,下面给大家分享C+... 目录一、printf 原生支持的进制转换1. 十进制、八进制、十六进制转换2. 显示进制前缀3. 指

springboot整合阿里云百炼DeepSeek实现sse流式打印的操作方法

《springboot整合阿里云百炼DeepSeek实现sse流式打印的操作方法》:本文主要介绍springboot整合阿里云百炼DeepSeek实现sse流式打印,本文给大家介绍的非常详细,对大... 目录1.开通阿里云百炼,获取到key2.新建SpringBoot项目3.工具类4.启动类5.测试类6.测

pytorch自动求梯度autograd的实现

《pytorch自动求梯度autograd的实现》autograd是一个自动微分引擎,它可以自动计算张量的梯度,本文主要介绍了pytorch自动求梯度autograd的实现,具有一定的参考价值,感兴趣... autograd是pytorch构建神经网络的核心。在 PyTorch 中,结合以下代码例子,当你

如何解决idea的Module:‘:app‘platform‘android-32‘not found.问题

《如何解决idea的Module:‘:app‘platform‘android-32‘notfound.问题》:本文主要介绍如何解决idea的Module:‘:app‘platform‘andr... 目录idea的Module:‘:app‘pwww.chinasem.cnlatform‘android-32

SpringBoot集成Milvus实现数据增删改查功能

《SpringBoot集成Milvus实现数据增删改查功能》milvus支持的语言比较多,支持python,Java,Go,node等开发语言,本文主要介绍如何使用Java语言,采用springboo... 目录1、Milvus基本概念2、添加maven依赖3、配置yml文件4、创建MilvusClient