Android Binder跨进程与非跨进程的传输异同源码分析
Posted _houzhi
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android Binder跨进程与非跨进程的传输异同源码分析相关的知识,希望对你有一定的参考价值。
前两天看到Service的onBind函数返回值的三种情况(扩展Binder对象,Messenger,aidl),一直在想他们内部的实现有什么不一样的,网上很多文章都介绍了Service的绑定过程,但是并没有介绍对于跨进程与非跨进程,对于不同的返回值,其具体有什么区别,以及具体是怎么实现的。这篇文章就根据源码分析android究竟是在哪部分来控制跨进程与非跨进程Binder的传输的,Binder究竟是怎么传输的。
首先看一下Service的绑定中,Binder跨进程与非跨进程的区别代码中的体现。
Service跨进程绑定与非跨进程绑定的表象
我们都知道如果使用aidl跨进程在onServiceConnection函数中需要这样使用new TestInterface.Stub.Proxy(binder)
(TestInterface是aidl声明的接口,并且在Service的onBind中返回new TestInterface.Stub()
)。如果是普通扩展Binder对象,那么直接强制类型转换就可以了TestBinder(binder)
(TestBinder是Binder子类,并且在Service的onBind中返回new TestBinder()
)。如果想知道onServiceConnection的参数binder具体是怎么样的,其实直接在ServiceConnection的onServiceConnection回调函数中,以及在Service的onBind中分别打印一下Binder就好了,如下代码
//Service onBind
public Binder onBind()
Binder binder ; //这里并没有初始化,可以使扩展Binder,也可以是aidl的Stub
Log.i("TestLog","onBind: "+binder.hashCode()+","+binder.toString());
return binder;
//ServiceConnection
ServiceConnection connection = new ServiceConnection()
public void onServiceConnection(ComponentName component, Binder binder)
Log.i("TestLog","onServiceConnection: "+binder.hashCode()+","+binder.toString());//跨进程时会binder是一个BinderProxy对象,非跨进程时跟onBind返回的对象一模一样。
通过设置Service的android:process,可以将Service远程进程与相同进程进行实验。
通过log可以发现,如果是跨进程的话,在ServiceConnection的onServiceConnection返回的结果是一个BinderProxy对象,与onBind中打印的结果是不一致的。如果是非跨进程,在onServiceConnection与onBind的打印的内容是一模一样的。使用aidl跨进程与非跨进程,使用扩展Binder非跨进程都是一样的。这里跟onBind是返回扩展Binder,返回扩展aidl自动产生Stub类还是Messenger是没有关系的,只跟是否跨进程有关。
从这里可以看出,如果是跨进程则返回BinderProxy,非跨进程则返回原来在onBind中返回的对象。我困惑的问题是,Android是怎么实现这个根据不同的进程来返回不同的结果呢?因为是Service绑定,所以先看一下Service的绑定过程。
Service绑定过程
绑定过程
首先简单介绍一下Service的绑定过程:
1. Activity通过bindService请求绑定相应的Service(由Intent指定),并设置了ServiceConnection回调接口。
2. 实际上会调用ContextImpl.bindService,然后调用ActivityManagerNative.getDefault().bindService,也就是通过IPC调用ActivityManagerService的bindService函数,之后会调用ActiveServices的bindServiceLocked。
3. 在bindServiceLocked中,先通过retrieveServiceLocked创建ServiceRecord,这个就是对应着我们想要绑定的Service。
4. 然后根据结果创建ConnectionRecord,将ServiceConnection保存在这里面。
5. 判断指定的Service是否启动,如果没有则启动服务,通过ApplicationThreadProxy(app.thread)远程调用ApplicationThread的scheduleCreateService来发送Message给主线程消息循环(ActivityThread,ActivityThread.H),启动服务。如果是远程进程服务,ActivityManagerService会开一个新的进程(startProcessLocked),然后将Service运行在新的Process中。
6. 绑定服务,通过ApplicationThreadProxy(app.thread)远程调用ApplicationThread的scheduleBindService来发送Message给主线程消息循环(ActivityThread,ActivityThread.H),绑定服务(handleBindService)。这里看一下源码:
try
if (!data.rebind)
IBinder binder = s.onBind(data.intent);
ActivityManagerNative.getDefault().publishService(
data.token, data.intent, binder);
else
s.onRebind(data.intent);
ActivityManagerNative.getDefault().serviceDoneExecuting(
data.token, 0, 0, 0);
ensureJitEnabled();
catch (RemoteException ex)
- 在绑定服务中,调用Service的onBind方法得到Binder,并且通过ActivityManagerNative.getDefault().publishService发布服务。
- 在publishServcie中使用ServiceRecord获取ConnectionRecord,最终调用保存在ConnectionRecord里面的ServiceConnection的onServiceConnection函数。其中ServiceRecord,ServiceConnection都是Binder类,可以通过IPC访问。
这里只是简单介绍一下绑定Service的流程,也并没有把具体对象名称写出来。如果想要更详细的了解整个过程可以去看一下老罗的博客,其实最好是照着别人的博客看一下源码。
绑定过程是否处理了不同onBind返回值
我一直觉得是在绑定服务的过程中会针对不同的onBind返回值有不同的处理,但实际上并没有。看第6步中的源码就知道了,只是把onBind的返回值当作IBinder来看。另外上面的绑定过程中,并没有针对不同的IBinder类型进行特殊处理。我想实际上在onBind函数中返回一个BinderProxy也是OK的。
既然绑定过程中没有对不同进程进行处理,那么只能看看更底层的传输过程了,Android中传输都是用Parcel类型。所以只能看看Parcel传输数据的过程中是否有特殊处理。而实际上确实是在Parcel中有体现出来了跨进程与非跨进程的区别。下面看看Parcel读取和写入Binder。
Parcel中写入读取Binder
看一下Parcel关于Binder的接口:
public final void writeStrongBinder(IBinder val)
nativeWriteStrongBinder(mNativePtr, val);
public final IBinder readStrongBinder()
return nativeReadStrongBinder(mNativePtr);
Parcel写入Binder
Parcel写入Binder是通过writeStrongBinder的,Java层的nativeWriteStrongBinder是一个native函数,对应的JNI实现为:
static void android_os_Parcel_writeStrongBinder(JNIEnv* env, jclass clazz, jlong nativePtr, jobject object)
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
if (parcel != NULL)
const status_t err = parcel->writeStrongBinder(ibinderForJavaObject(env, object));
if (err != NO_ERROR)
signalExceptionForError(env, clazz, err);
其中ibinderForJavaObject是将java层的对象转换成native层的Binder对象,实际上对应的是JavaBBinder。
然后native层的parcel会调用它的writeStrognBinder:
status_t Parcel::writeStrongBinder(const sp<IBinder>& val)
return flatten_binder(ProcessState::self(), val, this);
最终调用flatten_binder:
status_t flatten_binder(const sp<ProcessState>& /*proc*/,
const sp<IBinder>& binder, Parcel* out)
flat_binder_object obj;
obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
if (binder != NULL)
IBinder *local = binder->localBinder(); //JavaBBinder返回的是this,也就是自己
if (!local)
BpBinder *proxy = binder->remoteBinder();
if (proxy == NULL)
ALOGE("null proxy");
const int32_t handle = proxy ? proxy->handle() : 0;
obj.type = BINDER_TYPE_HANDLE;
obj.binder = 0; /* Don't pass uninitialized stack data to a remote process */
obj.handle = handle;
obj.cookie = 0;
else // 写入JavaBBinder将会对应这一段。
obj.type = BINDER_TYPE_BINDER;
obj.binder = reinterpret_cast<uintptr_t>(local->getWeakRefs());
obj.cookie = reinterpret_cast<uintptr_t>(local); //把对应的JavaBBinder的指针转换成整形uintptr_t
else
obj.type = BINDER_TYPE_BINDER;
obj.binder = 0;
obj.cookie = 0;
return finish_flatten_binder(binder, obj, out); //finish_flatten_binder是将obj写入到out里面。
这里就根据是本地binder还是远程binder,对Binder的写入采取了两种不同的方式。Binder如果是JavaBBinder,则它的localBinder会返回localBinder,如果是BpBinder则localBinder会为null。我们写入的时候,ibinderForJavaObject就返回的是JavaBBinder。flat_binder_object是Binder写入的时候的对象,它对应着Binder。handle表示Binder对象在Binder驱动中的标志,比如ServiceManager的handle为0。type表示当前传输的Binder是本地的(同进程),还是一个proxy(跨进程)。binder,cookie保存着Binder对象的指针。finish_flatten_binder会将obj写入到out里面,最终写入到Binder驱动中,写入部分也就完了。从上面的流程可以看出,在写入的时候,Parcel会针对不同的Binder(BBinder/JavaBBinder,BpBinder)有不同的处理,而他们确实就是对应着跟Service端Binder是同一个进程的还是不同进程的情况。
Parcel读取Binder
接下来就是读取了,同样的,Java层的Parcel也是通过native函数来读取的。在这里我们从最底层开始分析,首先从unflatten_binder开始:
unflatten_binder:
status_t unflatten_binder(const sp<ProcessState>& proc,
const Parcel& in, sp<IBinder>* out)
const flat_binder_object* flat = in.readObject(false);
if (flat)
switch (flat->type)
case BINDER_TYPE_BINDER:
*out = reinterpret_cast<IBinder*>(flat->cookie);
return finish_unflatten_binder(NULL, *flat, in);
case BINDER_TYPE_HANDLE:
*out = proc->getStrongProxyForHandle(flat->handle);
return finish_unflatten_binder(
static_cast<BpBinder*>(out->get()), *flat, in);
return BAD_TYPE;
首先从Binder驱动中读取一个flat_binder_object—flat。flat的处理是关键,它会根据flat->type的值分别处理,如果是BINDER_TYPE_BINDER,则使用cookie中的值强制转换成指针。如果是BINDER_TYPE_HANDLE,则使用Proxy,getStringProxyForHandle会返回BpBinder。而调用unflatten_binder的是native层的Parcel的readStringBinder。
sp<IBinder> Parcel::readStrongBinder() const
sp<IBinder> val;
unflatten_binder(ProcessState::self(), *this, &val);
return val;
调用readStrongBinder的是jni函数的实现:
static jobject android_os_Parcel_readStrongBinder(JNIEnv* env, jclass clazz, jlong nativePtr)
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
if (parcel != NULL)
return javaObjectForIBinder(env, parcel->readStrongBinder());
return NULL;
javaObjectForIBinder与ibinderForJavaObject相对应,把IBinder对象转换成对应的Java层的Object。这个函数是关键。看看它的实现:
jobject javaObjectForIBinder(JNIEnv* env, const sp<IBinder>& val)
if (val == NULL) return NULL;
if (val->checkSubclass(&gBinderOffsets)) //如果是本地的,那么会直接进入这部分代码,因为这个val是写入的时候同一个对象,gBinderOffsets也是一致。如果val是一种proxy对象,则不然,会继续往下执行找到一个Proxy对象。
// One of our own!
jobject object = static_cast<JavaBBinder*>(val.get())->object();
LOGDEATH("objectForBinder %p: it's our own %p!\\n", val.get(), object);
return object;
// For the rest of the function we will hold this lock, to serialize
// looking/creation of Java proxies for native Binder proxies.
AutoMutex _l(mProxyLock);
// Someone else's... do we know about it?
jobject object = (jobject)val->findObject(&gBinderProxyOffsets);
if (object != NULL)
jobject res = jniGetReferent(env, object);
if (res != NULL)
ALOGV("objectForBinder %p: found existing %p!\\n", val.get(), res);
return res;
LOGDEATH("Proxy object %p of IBinder %p no longer in working set!!!", object, val.get());
android_atomic_dec(&gNumProxyRefs);
val->detachObject(&gBinderProxyOffsets);
env->DeleteGlobalRef(object);
object = env->NewObject(gBinderProxyOffsets.mClass, gBinderProxyOffsets.mConstructor);//gBinderProxyOffsets保存的是Java层BinderProxy的信息,这里也是创建BinderProxy。
if (object != NULL)
LOGDEATH("objectForBinder %p: created new proxy %p !\\n", val.get(), object);
// The proxy holds a reference to the native object.
env->SetLongField(object, gBinderProxyOffsets.mObject, (jlong)val.get());
val->incStrong((void*)javaObjectForIBinder);
// The native object needs to hold a weak reference back to the
// proxy, so we can retrieve the same proxy if it is still active.
jobject refObject = env->NewGlobalRef(
env->GetObjectField(object, gBinderProxyOffsets.mSelf));
val->attachObject(&gBinderProxyOffsets, refObject,
jnienv_to_javavm(env), proxy_cleanup);
// Also remember the death recipients registered on this proxy
sp<DeathRecipientList> drl = new DeathRecipientList;
drl->incStrong((void*)javaObjectForIBinder);
env->SetLongField(object, gBinderProxyOffsets.mOrgue, reinterpret_cast<jlong>(drl.get()));
// Note that a new object reference has been created.
android_atomic_inc(&gNumProxyRefs);
incRefsCreated(env);
return object;
//IBinder的checkSubclass。
checkSubclass(const void* subclassID) const
return subclassID == &gBinderOffsets;
上面的处理中,如果是跟Service是同一个进程,也就是val是JavaBBinder。那么在checkSubclass中,它所包含的gBinderOffsets指针与参数传入的gBinderOffsets的指针必然是同一个值,则满足if条件。直接将指针强制转换成JavaBBinder,返回对应的jobject。如果是不同的进程,那么val也就会是BpBinder,最终会返回一个BinderProxy。不同的进程这一部分网上很多介绍Binder的文章都介绍了,可以参阅老罗或邓凡平的书。
Parcel读取写入总结
上面介绍了Parcel整个写入读取的流程,最后代替Binder传输的是flat_binder_object。在native的Parcel中,根据跨进程还是非跨进程,flat_binder_object的值是不一样的:
1. 跨进程的时候flat_binder_object的type为BINDER_TYPE_HANDLE,
2. 非跨进程的时候flat_binder_object的type为BINDER_TYPE_BINDER。
在这里已经可以发现跨进程与非跨进程的时候传输的数据的区别了。客户端的Parcel在读取Binder的时候,根据是flat_binder_object的type的值进行区分对待,返回不同的内容。而写入的时候也是一样的,根据是否是Proxy,来决定写入HANDLE还是BINDER。
最终这些内容都会通过ioctl与Binder驱动进行数据通信。所以最终处理不同进程之间的Binder数据传输处理的也只能是Binder驱动了。
Binder驱动的处理
关于如何判断读取的时候是跟Service同一个进程还是不同的进程,脑子里根据操作系统的知识想想基本就能够想到怎么去实现了。因为每个进程进行系统调用陷入内核的时候,内核的当然是可以知道当前进入内核空间的进程的信息了啦,这样就可以判断当前请求读取信息的是跟Service同一个进程还是不同的进程了。
实际上Binder驱动保存着Service端的Binder地址和handle的信息,将两者相互映射,根据不同的服务端进程和客户端进程来区别处理。这篇文章详细介绍了Binder驱动深入分析Android Binder 驱动。
总结
实际上真正控制跨进程与非跨进程返回Binder类型的,不是绑定服务的过程,而是Binder驱动。如果是与Service端同一个进程则返回Binder(在底层是Binder指针),如果是不同的进程则返回handle。Binder对象传入Binder驱动最底层是转化为flat_binder_object对象传递的。Parcel是根据从驱动中读取的数据作出不同的处理,如果从Binder驱动中读出的flat_binder_object的type为BINDER_TYPE_HANDLE,则创建BpBinder,在JAVA层创建BinderProxy返回,如果读出的flat_binder_object的type为BINDER_TYPE_BINDER则直接使用cookie的指针,将它强制转化为JavaBBinder,在JAVA层为原来Service的Binder对象(相同进程)。
这样每次从Binder驱动中读取IBinder对象,都会这样:不同进程则用handle,同进程则用Service端的Binder地址。每个Binder对象的接口参数里面可能还有Binder变量,但同样按照上面的方式传输。其实如果是handle的话,通信就需要通过Binder驱动去做。
虽然是从Service的绑定过程中想到这个问题,但是Service的绑定过程并没有针对跨进程与非跨进程对Binder传输有过处理,所以Android中任何地方的Binder跨进程与非跨进程的传输都是这样的。从Binder驱动读取Binder对象的时候,Binder驱动判断请求的进程与Service端的进程是否相同,如果相同则Binder驱动传出的数据为Service端Binder的指针(flat_binder_object的type为BINDER),如果不同则Binder驱动传出的数据为Service端Binder的handle(flat_binder_object的type为HANDLE)。
积跬步,至千里;积小流,成江海
以上是关于Android Binder跨进程与非跨进程的传输异同源码分析的主要内容,如果未能解决你的问题,请参考以下文章
Android 跨进程通信-Binder机制之ServiceManager对系统Service的管理