经过前面几节的铺垫,我们对Binder有了个整体认识,这节主要从使用的角度看下本地进程调用远程服务的流程是怎么样的?中间经过了那些步骤,为什么我们平常使用远程服务就像在本进程中一样,是什么让我们模糊了远程服务在多个进程之间的边界?答案就是Android的Binder跨进程传输机制。
Binder作为android各种组件的粘合剂,其核心作用毋庸置疑,下面我们就从四大组件的bindService来具体说下调用远程服务的详细流程。注意我们提到远程服务,这里的服务都是实现了IBinder接口的,准确说是继承了Binder类的。
在详细讲解之前,先看下客户端调用AMS的bindService远程接口的整体流程:
在Activity里调用bindService时,实际调用的是ContextWrapper类的bindService,其实现如下:
frameworks/base/core/java/android/content/ContextWrapper.java
681 @Override
682 public boolean bindService(Intent service, ServiceConnection conn,
683 int flags) {
684 return mBase.bindService(service, conn, flags);
685 }
这里调用是mBase的 bindService方法,这里的mBase其实就是ContextImpl,mBase具体创建是在创建Activity时候生成的,大家有兴趣可以看看Activity的启动流程就知道了,Android的设计还是比较好的,真正的Context的实现是独立于具体Activity的,这带来的直接好处就是可以替换Context的实现,说了点题外话,回归正题,接着看下ContextImpl的bindService。
frameworks/base/core/java/android/app/ContextImpl.java
1554 public boolean bindService(Intent service, ServiceConnection conn,
1555 int flags) {
1556 warnIfCallingFromSystemProcess();
1557 return bindServiceCommon(service, conn, flags, mMainThread.getHandler(),
1558 Process.myUserHandle());
1559 }
接着看bindServiceCommon这个方法。
1597 private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handler
1598 handler, UserHandle user) {
1599 // Keep this in sync with DevicePolicyManager.bindDeviceAdminServiceAsUser.
1600 IServiceConnection sd; // (1)
1601 if (conn == null) {
1602 throw new IllegalArgumentException("connection is null");
1603 }
1604 if (mPackageInfo != null) {
1605 sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags); // (2)
1606 } else {
1607 throw new RuntimeException("Not supported in system context");
1608 }
我们只看重点,从上面的bindService我们知道,其有个参数就是ServiceConnection,这个参数就是客户端用来接收远程服务代理对象的一个接口而已,由客户端实现,为什么要这样做,因为这里的远程服务是一个匿名服务,客户端没办法查询,只能通过bindService的时候经由参数ServiceConnection来被动接收远程服务代理对象。
所以,从这个角度来看,客户端提供了ServiceConnection,因此这里客户端充当了服务,但是这个ServiceConnection并不是一个IBinder,还不具备远程通信的能力。我们看下代码(2),这里通过LoadApk(代码中mPackageInfo 就是一个LoadApk类型的变量)对象来生成一个IServiceConnection的IBinder对象,我们的ServiceConnection对象也保存到了LoadApk对象里了。
接下来看看LoadApk里的方法getServiceDispatcher
frameworks/base/core/java/android/app/LoadedApk.java
1414 public final IServiceConnection getServiceDispatcher(ServiceConnection c,
1415 Context context, Handler handler, int flags) {
1416 synchronized (mServices) {
1417 LoadedApk.ServiceDispatcher sd = null;
......
1423 if (sd == null) {
1424 sd = new ServiceDispatcher(c, context, handler, flags);
......
1434 return sd.getIServiceConnection();
1435 }
1436 }
我们这里要说的是Binder,所以直奔主题,从代码看出IServiceConnection是由LoadApk的内部类ServiceDispatcher的getIServiceConnection()返回的,进入ServiceDispatcher看看。
1489 static final class ServiceDispatcher {
1490 private final ServiceDispatcher.InnerConnection mIServiceConnection;
1491 private final ServiceConnection mConnection; // (1)
......
1506 private static class InnerConnection extends IServiceConnection.Stub { // (2)
1507 final WeakReference mDispatcher;
1508
1509 InnerConnection(LoadedApk.ServiceDispatcher sd) {
1510 mDispatcher = new WeakReference(sd);
1511 }
1512
到这里明白了,(1)处保存了客户端实现的ServiceConnection 对象;(2)处InnerConnection 就是客户端的IBinder实体,其将会被传到远程服务端,然后远程服务端通过它把远程服务对象设置给客户端。具体怎么把远程服务通过客户端实现的ServiceConnection 接口设置这里不会讲解,我们只说Binder的调用过程。
好了,接下来回到ContextImpl的bindServiceCommon方法,看下InnerConnection 这个IBinder是怎么样通过远程接口传给AMS服务的。再看下bindServiceCommon方法。
1597 private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handler
1598 handler, UserHandle user) {
1599 // Keep this in sync with DevicePolicyManager.bindDeviceAdminServiceAsUser.
1600 IServiceConnection sd;
......
1618 int res = ActivityManager.getService().bindService(
1619 mMainThread.getApplicationThread(), getActivityToken(), service,
1620 service.resolveTypeIfNeeded(getContentResolver()),
1621 sd, flags, getOpPackageName(), user.getIdentifier());
......
1630 }
从上面代码我们看到,获取的sd对象(其实就是InnerConnection )通过AMS的服务接口被调到远程SystemServer进程(AMS所在进程),接下来我们主要看下这个传输的过程了。其中ActivityManager.getService()返回的是IActivityManager接口,这是由AIDL语言生成,细节我们不去看,但是我们知道返回的IActivityManager接口其实是个代理,前面章节介绍过,其在本地的代理对象就是BpBinder,记下这个就可以,因为对IActivityManager调用的transact都会转发给BpBinder对象的transact方法。
在IActivityManager的代理里面会调用parcel.writeStrongBinder(sd)把InnerConnection压扁:)其实就是把InnerConnection 转换到flat_binder_object对象,这个对象前面章节介绍过,记录了Binder的信息。下面我们看下parcel.writeStrongBinder的实现。JAVA层就不看了,最终会调到native层的Parcel的writeStrongBinder方法。
1081status_t Parcel::writeStrongBinder(const sp& val)
1082{
1083 return flatten_binder(ProcessState::self(), val, this);
1084}
接下来看看flatten_binder方法。
248status_t flatten_binder(const sp& /*proc*/,
249 const wp& binder, Parcel* out)
250{
251 flat_binder_object obj;
252
253 obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
254 if (binder != NULL) {
255 sp real = binder.promote();
256 if (real != NULL) {
257 IBinder *local = real->localBinder(); // (1)
258 if (!local) {
259 BpBinder *proxy = real->remoteBinder(); // (2)
260 if (proxy == NULL) {
261 ALOGE("null proxy");
262 }
263 const int32_t handle = proxy ? proxy->handle() : 0;
264 obj.type = BINDER_TYPE_WEAK_HANDLE; // (3)
265 obj.binder = 0; /* Don't pass uninitialized stack data to a remote process */
266 obj.handle = handle; // (4)
267 obj.cookie = 0;
268 } else {
269 obj.type = BINDER_TYPE_WEAK_BINDER; // (5)
270 obj.binder = reinterpret_cast(binder.get_refs());
271 obj.cookie = reinterpret_cast(binder.unsafe_get()); // (6)
272 }
273 return finish_flatten_binder(real, obj, out);
274 }
......
295}
这里我们标注了6处关键代码,下面进行说明。
1)(1)这里获取本地IBinder,很显然,这里的sd其实就是 InnerConnection,它是从Binder继承来的(JAVA层的Binder类其实缓存的是本地的JavaBBinderHolder,而JavaBBinderHolder.get返回的对象是JavaBBinder,这其实是一个BBinder,因为JavaBBinder是BBinder的子类),因此返回的IBinder是不为空的(再不明白看看前面章节Binder说明),所以,逻辑转入(5)。
2)(5) 这里首先把flat_binder_object的type赋值为BINDER_TYPE_WEAK_BINDER,表示这是一个实体Binder(本地)。
3)(6) 这里把实体IBinder的对象地址直接赋值给flat_binder_object的cookie成员,注意,这个cookie成员很重要,在驱动把请求转到客户端进程的时候会直接把它转换为JavaBBinder对象。
到这里客户端的对象InnerConnection已经被保存在结构flat_binder_object里了(其它普通字段这里就不介绍了),接下来调用IActivityManager远程接口代理对象BpBinder的transact方法(为什么?不明白查看BpBinder介绍),下面看下BpBinder对象的transact方法实现。
frameworks/native/libs/binder/BpBinder.cpp
160status_t BpBinder::transact(
161 uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
162{
163 // Once a binder has died, it will never come back to life.
164 if (mAlive) {
165 status_t status = IPCThreadState::self()->transact(
166 mHandle, code, data, reply, flags);
167 if (status == DEAD_OBJECT) mAlive = 0;
168 return status;
169 }
170
171 return DEAD_OBJECT;
172}
BpBinder实际的调用转到IPCThreadState::self()->transact,继续查看IPCThreadState的transact方法。
frameworks/native/libs/binder/IPCThreadState.cpp
559status_t IPCThreadState::transact(int32_t handle,
560 uint32_t code, const Parcel& data,
561 Parcel* reply, uint32_t flags)
562{
563 status_t err = data.errorCheck();
......
577 err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL); // (1)
578 }
......
593 if (reply) {
594 err = waitForResponse(reply);// (2)
595 } else {
596 Parcel fakeReply;
597 err = waitForResponse(&fakeReply);
598 }
......
618 return err;
619}
首先再强调下IPCThreadState::transact的第一个参数handle,这个handle其实就是IActivityManager远程服务的句柄(这个很重要,Binder驱动层就是通过这个handle来查询到是哪个进程提供服务的以及具体的服务,因为需要把客户端请求转到目标进程执行,我们知道这里的IActivityManager对应的目标进程就是SystemServer)。
在(1)这里把客户端的请求做一次封装,命令码为BC_TRANSACTION,进去IPCThreadState::writeTransactionData看看。
914status_t IPCThreadState::writeTransactionData(int32_t cmd, uint32_t binderFlags,
915 int32_t handle, uint32_t code, const Parcel& data, status_t* statusBuffer)
916{
917 binder_transaction_data tr;
......
944 mOut.writeInt32(cmd);
945 mOut.write(&tr, sizeof(tr));
946
947 return NO_ERROR;
948}
请求数据封装在binder_transaction_data结构里,同时把命令码和此结构写入mOut(这也是一个Parcel对象)。紧接着调用IPCThreadState::waitForResponse把客户端的请求送到Binder的内核驱动。
722status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
723{
......
727 while (1) {
728 if ((err=talkWithDriver()) < NO_ERROR) break;
通过系统调用发送客户到请求到内核是通过talkWithDriver方法里调用ioctl实现的,往下看。
813status_t IPCThreadState::talkWithDriver(bool doReceive)
......
861 do {
......
865#if defined(__ANDROID__)
866 if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
867 err = NO_ERROR;
868 else
869 err = -errno;
终于看到了实质性代码ioctl,通过此系统调用转入内核层,命令码是BINDER_WRITE_READ(让Binder驱动完成一次写入和读取)。现在是由客户端通过系统调用转到Binder的内核层,接下来就是Binder驱动干的事情了。Binder驱动主要完成如下事情:
1)为IBinder类型的对象生成Binder_node节点信息,同时生成handle保存引用到内核;
2)碰到结构flag_binder_object里的type是BINDER_TYPE_BINDER类型的,更改为BINDER_TYPE_HANDLE;
3)通过代理的handle找到服务IBinder实体,把其放到binder_transaction_data结构的target里;
4)通过代理的handle找到其宿主进程,把请求投送到宿主进程的todo队列,唤醒对应的宿主进程的binder线程执行;
这里要特别注意,因为Binder驱动实现mmap系统调用,其会把内核地址映射到用户进程地址空间,因此当内核往这个地址空间写入数据时,在用户空间会同时反应出来,这也是为什么Binder机制只实现一次copy的原因所在。
经过Binder驱动层处理,流程转到宿主进程,这里就是SystemServer进程,前面我们讲过每个进程都会启动Binder进程来处理客户端发来的请求,最后逻辑由IPCThreadState::executeCommand执行。
957status_t IPCThreadState::executeCommand(int32_t cmd)
958{
959 BBinder* obj;
960 RefBase::weakref_type* refs;
......
1036 case BR_TRANSACTION:
1037 {
1038 binder_transaction_data tr;
1039 result = mIn.read(&tr, sizeof(tr));
......
1075 if (tr.target.ptr) {
......
1078 if (reinterpret_cast(
1079 tr.target.ptr)->attemptIncStrong(this)) {
1080 error = reinterpret_cast(tr.cookie)->transact(tr.code, buffer,
1081 &reply, tr.flags);
1082 reinterpret_cast(tr.cookie)->decStrong(this);
最终会调用BBinder的transact方法,而BBinder最终会调用虚方法onTransact。
118status_t BBinder::transact(
119 uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
120{
......
124 switch (code) {
125 case PING_TRANSACTION:
......
128 default:
129 err = onTransact(code, data, reply, flags);
130 break;
131 }
132......
138}
我们知道这里的BBinder其实就是JavaBBinder,从上节我们知道,其会调用JAVA层服务对象的onTransact,这样处理转到实际的服务去执行了,这里的服务就是IActivieyManager,在SystemServer进程里实现类就是ActivityManagerService。
到这里就完成了一次Binder请求。好吧,如果还不明白的话,大家可以回到开头看看整体调用流程图再结合源码就会明白了,这里大家要重点注意Parce类的writeStrongBinder和readStrongBinder这两个方法,它们其实就是把Binder对象(本地或远程)压扁成flat_binder_object结构,因为在Binder驱动层这个结构其实就代表了一个IBinder对象,内核会为它生成一个handle,而客户端持有的远程代理对象实质上就是持有实体IBinder对应的handler而已(这个handle有本地对象BpBinder来封装)。
最后还得提下IBinder对象就是通过flat_binder_object这个结构经由Binder驱动层实现了IBinder对象在进程之间传递,这给调用者造成一个假象,感觉远程对象就真的能在进程间传递,事实当然不是这样,因为任何IBinder对象实体经由Binder驱动层处理后都变成了对IBinder对象的句柄handle的持有,前面已经介绍过,实体IBinder在flat_binder_object结构里的的type变量值是BINDER_TYPE_BINDER或BINDER_TYPE_WEAK_BINDER,其cookie就是实体IBinder对象地址,其handle变量是0(这时还没有经过Binder驱动处理,其对应的handle可能还没有生成),当通过ioctl把这些数据传递给Binder驱动后,Binder驱动会对所有的IBinder对象进行转换处理(生成IBinder的handle,在驱动层插入节点binder_node记录下这个IBinder对象)。
大家可以看下驱动层源码,对这部分描述就会明白了,随便提一点,Parcel里记录了所有的IBinder对象在内存中的偏移位置和IBinder对象的个数,在通过命令码BC_TRANSACTION把数据格式封装为binder_transaction_data结构时,这个结构里同时也记录了IBinder的信息(对象偏移和字节数),看下IPCThreadState::writeTransactionData代码片段就明白了。
914status_t IPCThreadState::writeTransactionData(int32_t cmd, uint32_t binderFlags,
915 int32_t handle, uint32_t code, const Parcel& data, status_t* statusBuffer)
916{
917 binder_transaction_data tr;
918
919 tr.target.ptr = 0; /* Don't pass uninitialized stack data to a remote process */
920 tr.target.handle = handle;
921 tr.code = code;
922 tr.flags = binderFlags;
923 tr.cookie = 0;
924 tr.sender_pid = 0;
925 tr.sender_euid = 0;
926
927 const status_t err = data.errorCheck();
928 if (err == NO_ERROR) {
929 tr.data_size = data.ipcDataSize();
930 tr.data.ptr.buffer = data.ipcData();
931 tr.offsets_size = data.ipcObjectsCount()*sizeof(binder_size_t);
932 tr.data.ptr.offsets = data.ipcObjects();
933 } else if (statusBuffer) {
......
944 mOut.writeInt32(cmd);
945 mOut.write(&tr, sizeof(tr));
946
947 return NO_ERROR;
948}
上面标红的代码段就是记录了IBinder对象的信息。
本系列文章均为原创,主要总结作者多年在软件行业的一些经验,和大家共同学习、进步,转载请注明出处,谢谢!