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

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

让树莓派智能语音助手实现定时提醒功能

最初的时候是想直接在rasa 的chatbot上实现,因为rasa本身是带有remindschedule模块的。不过经过一番折腾后,忽然发现,chatbot上实现的定时,语音助手不一定会有响应。因为,我目前语音助手的代码设置了长时间无应答会结束对话,这样一来,chatbot定时提醒的触发就不会被语音助手获悉。那怎么让语音助手也具有定时提醒功能呢? 我最后选择的方法是用threading.Time

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

C#实战|大乐透选号器[6]:实现实时显示已选择的红蓝球数量

哈喽,你好啊,我是雷工。 关于大乐透选号器在前面已经记录了5篇笔记,这是第6篇; 接下来实现实时显示当前选中红球数量,蓝球数量; 以下为练习笔记。 01 效果演示 当选择和取消选择红球或蓝球时,在对应的位置显示实时已选择的红球、蓝球的数量; 02 标签名称 分别设置Label标签名称为:lblRedCount、lblBlueCount

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影

Kubernetes PodSecurityPolicy:PSP能实现的5种主要安全策略

Kubernetes PodSecurityPolicy:PSP能实现的5种主要安全策略 1. 特权模式限制2. 宿主机资源隔离3. 用户和组管理4. 权限提升控制5. SELinux配置 💖The Begin💖点点关注,收藏不迷路💖 Kubernetes的PodSecurityPolicy(PSP)是一个关键的安全特性,它在Pod创建之前实施安全策略,确保P

Java ArrayList扩容机制 (源码解读)

结论:初始长度为10,若所需长度小于1.5倍原长度,则按照1.5倍扩容。若不够用则按照所需长度扩容。 一. 明确类内部重要变量含义         1:数组默认长度         2:这是一个共享的空数组实例,用于明确创建长度为0时的ArrayList ,比如通过 new ArrayList<>(0),ArrayList 内部的数组 elementData 会指向这个 EMPTY_EL