引言
本博客将讲解本地服务的注册过程,为了方便大家更好地理解,选择了MediaPlayer Service作为例子。
启动并注册MediaPlayer Service的代码在frameworks/base/media/mediaserver/main_mediaserver.cpp中,如下:
表面上看,启动服务的代码异常简单,实际上只是代码封装得好,里面的调用非常复杂。下面我们将逐一剖析。
1.spproc(ProcessState::self());
首先我们看ProcessState::self();这个方法,如下所示
显然,gProcess是一个全局变量,由于ProcessState的构造函数为私有构造函数,所以只能采用static函数生成ProcessState实例。再来看它的构造函数,如下所示:
构造函数的实体比较简单,主要就是调用了mmap()方法,但是它在成员列表中进行了很多的操作,除了为成员变量设置初始值之外,主要就是open_driver()返回文件描述符了。
open_driver()将打开”dev/binder”,并且返回一个文件描述符(其实就是一个整型数)给mDriverFD;其代码如下:
open_drvier()的代码量虽然不少,但是其实主要就是做了两件事:第一,打开/dev/binder这个binder设备,这个是android在内核中设置的一个专门用于完成进程间通讯的虚拟设备;第二,result=ioctl(fd,BINDER_SET_MAX_THREADS,&maxThreads);的作用是通过ioctl()的方式告诉内核,这个fd支持的最大线程数是15个。
下面再回到ProcessState的构造函数中分析mmap(),其实非常简单,就是根据返回的文件描述符,将用户空间的特定区域映射到内核空间的特定区域中。由于用户空间中的进程不能直接访问内核空间,所以只能通过内核空间的特定映射区域来访问内核空间。当调用mmap()函数时,将从0x40000000地址开始开辟一块指定大小的空间,而后调用内核的binder_mmap()函数。
在Android系统中,内核空间以及由mmap()函数映射出的区域都事先被定义好,Android系统采用Prelinked方式预先确定各个库将被连接的地址。这些连接信息在/build/core/prelink-linux-arm.map中可以查看到,如下所示:
0000000 - 0xFFFFFFFF Kernel
# 0xB0100000 - 0xBFFFFFFF Thread 0 Stack
# 0xB0000000 - 0xB00FFFFF Linker
# 0xA0000000 - 0xBFFFFFFF Prelinked System Libraries
# 0x90000000 - 0x9FFFFFFF Prelinked App Libraries
# 0x80000000 - 0x8FFFFFFF Non-prelinked Libraries
# 0x40000000 - 0x7FFFFFFF mmap'd stuff
# 0x10000000 - 0x3FFFFFFF Thread Stacks
# 0x00000000 - 0x0FFFFFFF .text / .data / heap
其中mmap’d stuff即为mmap()函数的起始映射地址。
至此,main()函数中的第一行代码就分析完了,总结一下,主要做了以下两件事:
- 打开/dev/binder设备,并且根据返回的文件描述符将用户空间的特定区域映射到内核空间的特定区域中;
- 新建了一个ProcessState对象
整个创建过程可以用一个简单的示意图表示如下:
2.spsm=defaultServiceManager();
其中defaultServiceManager()的代码如下:
显然这里采用了单例设计模式,gDefaultServiceManager只会创建一次,而ProcessState::self()在前面已经分析过,下面看ProcessState::getContextObject()方法:
在真机上supportsProcesses()为true,所以进入getStrongProxyForHandle()方法:
首先分析被调用的lookupHandleLocked()方法:
其中mHandleToObject是一个Vector
所以,gDefaultServiceManager=interface_cast
下面我们看一下BpBinder的构造函数:
成员初始化列表比较简单,下面重点讲一下IPCThreadState::self()->incWeakHandle(handle),下面是IPCThreadState::self()的代码:
显然IPCThreadState
注意gHaveTLS的意思是是否含有Thread Local Storage,首次进入时没有,所以代码走到下面,pthread_key_create(&gTLS,threadDestructor)的主要作用就是新建一个TLS key并且保存相应的析构函数指针。显然,后面再进入IPCThreadState::self()这个函数时,就只需要通过pthread_getspecific(k)获取相应的TLS key即可。
下面看一下IPCThreadState的构造方法:
首先是在成员初始化列表中为mProcess和mMyThreadId赋值。mIn,mOut是用于与Binder Driver通信的Parcel对象。然后pthread_setspecific()方法如下:
显然thread_setspecific(gTLS,this);的作用就是为map中key为gTLS赋值为当前IPCThreadState对象。
至此,总结一下,在defaultServiceManager()中我们主要做了以下几个工作:
创建了IPCThreadState,并且为其创建了TLS;
创建了handle值为0的BpBinder
那 gDefaultServiceManager=interface_cast
先看一下interface_cast模板函数:
但是我们会发现asInterface()的代码是不存在的,不过有如下两段宏:
其中前者是宏定义,后者是宏实现,将IMPLEMENT_META_INTERFACE宏扩展后,得到如下asInterface()的代码:
IBinder的queryLocalInterface()函数将根据obj是BBinder或BpBinder而采取不同的行为动作,当参数obj是BBinder对象时,转换类型为服务对象;当参数obj是BpBinder对象时,则返回NULL.显然,这里返回的是NULL.所以会新建一个BpServiceManager对象。下面我们看一下BpServiceManager的构造函数:
非常简单,就是将传入的BpBinder对象传给BpInterface
这里又再一次将BpBinder对象传递给了BpRefBase,再看一下BpRefBase的构造函数:
显然,这里将BpBinder对象传递给了BpBinder对象传递到了mRemote,其中o.get()是模板类sp中的方法,sp是Google定义的用于处理指针的模板类,可将sp理解为Strong Pointer,与之相对的是wp(Weak Pointer),此处不展开讨论。
这里BpServiceManager,BpInterface,BpRefBase的继承关系如下:
至此,sp
3.MediaPlayerService::instantiate();
该方法的代码如下:
3.1 MediaPlayerService
首先看一下MediaPlayerService这个类,它的构造函数非常简单,就不展开说了。关键是注意到MediaPlayerService继承自BnMediaPlayerService,BnMediaPlayerSerivce中重写了virtual函数onTransact(),如下所示:
显然是根据不同的命令(code值)进行相应的回调操作。而BnMediaPlayerService又继承自BnInterface
3.2 addService()
再回到MediaPlayerService::instantiate()这个方法中,defaultServiceManager()返回的是全局变量gDefaultServiceManager,也就是刚刚创建的BpServiceManager,而BpServiceManager::addService()方法的代码如下:
其中service是刚刚创建的MediaPlayerService对象。Parcel对象data的作用是保存传入的数据,在这里data中保存的数据如下所示:
至于flat_binder_object会在后面说明。
3.2.1 writeStrongBinder()
reply的作用很明显,是用于保存返回的数据的。下面进入Parcel::writeStrongBinder()方法:
前面分析过,ProcessState::self()返回的是它的单例对象,而此处的val其实是MediaPlayerService对象,由于它间接继承于BBinder,而BBinder继承于IBinder,所以可作用IBinder对象使用。下面进入flatten_binder()方法:
由于当前的binder其实是MediaPlayerService实例,而MediaPlayerService间接继承自BBinder,而BBinder::localBinder()返回的是this,所以local不为NULL,从而执行else部分的代码。下面看一下flat_binder_object的数据结构:
所以执行完else部分的代码之后,其type值为BINDER_TYPE_BINDER,binder成员值则为MediaPlayerService对象的弱引用,cookie则指向MediaPlayerService对象。然后,调用finish_flatten_binder()函数,将flat_binder_object对象保存到data中。
至于finish_flatten_binder(binder,obj,out);这个语句,展开来讲的话非常长,所以将它单独放在一篇博客中进行分析,博客链接为Android Binder机制分析(4) Parcel类分析。
至此,data.writeStrongBinder(service);就分析完毕。下面进行remote()->transact(ADD_SERVICE_TRANSACTION,data,&reply)的讲解。
3.2.2 IPCThreadState::self()->transact()
再回到BpServiceManager::addService()方法中,前面讲过,remote()返回的是handle值为0的BpBinder对象,所以这里的remote()->transact(ADD_SERVICE_TRANSACTION,data,&reply)本质上是调用BpBinder的transact()方法,其代码如下:
其中code值为ADD_SERVICE_TRANSACTION,data为上面分析过的Parcel对象引用,reply用于放置返回值,flags为默认值0;
下面进入IPCThreadState::transact()方法,其主要代码如下:
3.2.2.1 writeTransactionData()分析
下面是writeTransactionData()方法的代码如下:
首先我们看一下binder_transaction_data的定义:
所以writeTransactionData()方法就好理解了:
- 首先将handle(此处值为0)赋值给tr.target.handle;
- 然后将code值(ADD_SERVICE_TRANSACTION)赋给tr.code;
- 之后将binderFlags(值为0)赋值给tr.flags;
- data_size中保存的是data.ipcDataSize()的返回值,ipcDataSize()方法如下:
Parcel::ipcDataSize() const 1234size_t Parcel::ipcDataSize() const{return (mDataSize>mDataPos?mDataSize:mDataPos);}
由博客Android Binder机制(3) Parcel类分析的分析可知,写入每个字符串之前会先写入字符串长度(32位整型数,所以是4字节),而且写入的字符串中每个字符占2个字节(而不是一个),再加上flat_binder_object对象,所以此处data_size=4+262+4+122+16=100
此时binder_transaction_data对象中的内容如下图所示:
- buffer保存着data(送信Parcel)成员变量mData的指针
- offset_size保存着数据4,它是data(送信Parcel)成员变量mObject中的数据大小
- offsets保存着data成员变量mObject的指针
3.2.2.2 IPCThreadState::waitForResponse()
该方法的主要代码如下:
由于talkWithDriver()展开来讲的话非常复杂,在后面的博客中会给出。这里直接给结论:调用talkWithDriver()函数后,将保存在mOut中的Binder IPC数据传递给Binder Driver,并将来自Binder Driver的Binder Driver的Binder IPC保存在mIn中。另外,新建binder_node对象也是在talkWithDriver()这个调用中发生的,后面会有博客进行详细讲解。
之后,调用mIn.readInt32()读取Binder协议,在从Binder Driver接收到Binder协议中保存着BR_REPLY,所以继续执行switch语句中与BR_REPLY相匹配的部分。
调用mIn.read(&tr,sizeof(tr)),读取binder_transaction_data数据结构
IPCThreadState从Binder Driver接收Binder IPC数据后,保存在mIn中,所保存的数据内容如下:
如图所示,binder_transaction_data的buffer与offsets指向Binder mmap区域中的Binder RPC数据。data_size表示buffer中存储的有效数据的大小。在Context Manager处理服务注册时,若成功,则返回0,否则返回-1.
之后调用ipcSetDataReference()方法,该方法的主要代码如下:
在ipcSetDataReference()中,以接收到的binder_transaction_data数据结构为基础,设置reply主要的成员变量:
buffer保存在mData中,且该buffer持有接收的Binder RPC数据的起始地址
mDataSize保存着data_size,data_size指接收的Binder RPC数据的大小
mObjects保存着flat_binder_object结构体在Binder RPC中的存储位置offsets
mObjectsSize保存Binder RPC中flat_binder_object结构体的个数
至此,MediaPlayerService::instantiate();讲解完毕.
4. ProcessState::self()->startThreadPool();分析
从函数名称就能够知道函数的作用是启动线程池,其代码如下:
开始时,线程池尚未启动,所以mThreadPoolStarted==false,从而调用spawnPooledThread()方法,其代码如下:
注意传入的参数isMain为true,代表这是主线程。
代码也非常简单,由于此时mThreadPoolStared==true,所以新建PoolThread并运行,其中buf是利用sprintf方法得到的字符数组,其代表PoolThread的线程名称。实际上PoolThread并没有实现run()方法,它调用的其实是基类Thread的run方法,代码很简单,这里就不再讨论了。
5.IPCThreadState::self()->joinThreadPool(); 分析
由于joinThreadPool()函数中的isMain默认值为true,故这里isMain为true.该方法的主要代码如下:
由于isMain为true,所以这里mOut写入的是BC_ENTER_LOOPER;
之后调用androidSetThreadSchedulingGroup将当前线程设置在默认的线程组中
弱引用相关的处理,到讲解完了executeCommand()再讲解
得到talkWithDriver()的结果,之后执行executeCommand()方法
executeCommand()方法的代码如下:
如果从Binder Driver中读取到有事务需要处理,则返回结果为BR_TRANSACTION,所以这里只放了BR_TRANSACTION这种情况。显然,如果运行正常的话,会执行到b->transact(tr.code,buffer,&reply,0);这个语句,而前面在flatten_binder(const sp
由于BnMediaPlayerService重写了onTransaction()方法,所以这里会调用BnMediaPlayerService的onTransact()方法:
显然,在这里针对不同的code进行不同的动作,以DECODE_FD为例,调用了decode(fd, offset, length, &sampleRate, &numChannels, &format);方法,而这个decode()方法是在MediaPlayerService中实现了,这样就最终调用了MediaPlayerService的服务函数了。
到这里,executeCommand()方法就讲解完了,再回到joinThradPool()中,由于这是一个循环,所以会一直在这里循环地取出队列中的命令并调用MediaPlayerSerivce的相应方法进行处里。到这里,服务的注册就完成了。
题外话:这里对于不同的基类进行不同程度的实现,达到了非常好的抽象效果,这是值得我们学习的。