本文分析基于Android14(aosp分支android-14.0.0_r28)
前言
在上一篇中,SystemServer和普通APP进程的通信已经出现了binder的身影,这是Android领域的热门话题,应用工程师、Framework工程师,甚至内核工程师都在谈论它。我们工作中用它,面试时还要考它。如此重要的“基建”模块,在探索更广大的业务逻辑之前,值得我们深入研究。网络上已有众多优秀的文章阐述它的特点和优势,在这里就不复述了,我的思路是从源码层面理解它,再回头去看这些文字一定会有自己的感受和想法。
Binder通信是典型的C/S架构,Client端发起请求,Server端接收请求并响应,它们之间的数据传递,由kernel层中的Binder驱动负责。
整个架构覆盖了Java Framework、Native Framework和Kernel层。其中Client和Server在Java、native和kernel层都有代表它们的对象实例,各层次对象的交互串联起了完整的通信过程。
图中有一个叫ServiceManager的native层模块,它负责管理系统中通过Binder访问的很多Service。为什么不是全部?因为Binder通信有一大特点是,它可以通过已有Bidner通道传输另外的Binder接口,这样一来,不在ServiceManager中注册也可以完成通信。
大家喜欢将没在ServiceManager中注册的Binder接口称为匿名Binder,注册过的Binder接口称为实名Binder。(似乎Android官方并没有这样的说法,不过还挺贴切)AMS、WMS这些系统模块提供的接口都要在ServiceManager注册,因此是实名Binder。而APP开发中通过bindService获取的Binder接口,则都属于匿名Binder。
注意:上面提到的Service并不等同于APP开发中经常接触的四大组件Service。而是C/S结构中的Server端,由于它通常是面向Client端提供某些业务功能,因此叫做Service(服务)。
计划探讨以下Binder相关内容: Framework通信流程(包括Java和native层)、ServiceManager(启动以及注册获取Service)、线程管理、内存管理、通信模型、异常处理和DeathRecipient。暂时想到这些,内容很多,给自己挖的这个坑够大😂
本篇我们先通过一个简单的例子,从APP开发的日常场景出发,来了解Binder在Framework层的通信主体流程。
一个例子
我们在写应用的时候,若需要进程通信,一般都是通过AIDL文件描述我们的接口,然后IDE自动生成一个Java接口文件,在Server(S端)和Client(C端)都需要引用这个接口文件,然后分别实现各自的业务逻辑。
假设现在有一个CountServer负责做加法,在AIDL中定义了如下接口:
// ICount.aidl
package com.server.countserver;
// Declare any non-default types here with import statements
interface ICount {
int sum(int a, int b);
}
build一下,IDE将在这个目录下(build\generated\aidl_source_output_dir\debug\out\com\server\countserver\ICount.java)生成接口文件。
Server端我们通常这样写:
package com.server.countserver
class CountService : Service() {
private val mBinder: Binder = CountBinder()
override fun onBind(intent: Intent?): IBinder {
return mBinder
}
class CountBinder : ICount.Stub() {
override fun sum(a: Int, b: Int): Int {
return a + b
}
}
}
Client端我们通常这样用:
package com.client.countclient
class MainActivity : Activity() {
private var countInterface : ICount? = null
override fun onCreate(savedInstanceState: Bundle?) {
... ...
val intent = Intent("android.intent.action.count")
intent.setPackage("com.server.countserver")
bindService(intent, mConnection, BIND_AUTO_CREATE)
}
private val mConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName, service: IBinder) {
countInterface = ICount.Stub.asInterface(service)
}
override fun onServiceDisconnected(name: ComponentName) {}
}
override fun onDestroy() {
super.onDestroy()
unbindService(mConnection)
}
}
// 调用远程接口
countInterface?.run {
val sum : Int = this.sum(1, 2)
Toast.makeText(this@MainActivity, "sum is $sum", Toast.LENGTH_LONG).show()
}
AIDL生成类
来看看自动生成类ICount.java,其类结构如下图(内部类Default为示例代码,直接省略):
-
Stub类:一个抽象类,继承了ICount接口,但它并没有实现sum函数,只是在onTransact时(S端响应时)调用了sum函数进行计算。sum函数的真正逻辑是在我们自己的代码(CountBidner类)中继承Stub类去实现的。
-
Proxy类:实现了ICount接口,但它的sum函数内部也没有计算逻辑,只是调用了mRemote#transact发起通信请求。其中mRemote就是C端从onServiceConnected函数中获得的IBinder接口(来自S端)。
这样看来一个清晰的C/S架构就出现了。
-
S端使用Stub类实现计算逻辑,onTransact是响应函数,在此函数中会将C端发来的数据解包(若数据为自定义类型,解包动作就称为反序列化),获得请求参数。
-
C端使用Proxy类作为调用接口,接口函数的实现就是先打包请求数据(若数据为自定义类型,打包动作就称为序列化),然后调用IBinder的transact函数发起远程请求。
-
通信过程中的数据由Parcel类承载,若数据为自定义类型,则需要实现Parcelable接口,即实现自定义类型的序列化和反序列化方法,供打包解包时使用。
到这里,如果只是使用Binder通信,那么对于APP开发和Java Framework开发都基本够用了。我们甚至可以抛开AIDL,自己手写transact和onTransact等代码,事实上有些Java Framework工程师也是这么干的。不过,自己手写感觉挺麻烦的。
Binder通信流程
发送请求
首先明确一点,我们使用的ICount接口是匿名接口,它没有在ServiceManager中注册,在本篇的后半部分我们会探讨C端与CountServer是怎么建立通信通道的。我们先从使用接口的起点开始countInterface.sum(1, 2),根据AIDL生成类的分析,我们知道C端使用Proxy类调用接口,其实现如下:
public int sum(int a, int b) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(a);
_data.writeInt(b);
boolean _status = mRemote.transact(Stub.TRANSACTION_sum, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
首先从缓存池中取一个Parcel出来,然后调用writeInt写入int参数,这里最终会调用native层函数Parcel#writeAligned。
// frameworks/native/libs/binder/Parcel.cpp
status_t Parcel::writeAligned(T val) {
static_assert(PAD_SIZE_UNSAFE(sizeof(T)) == sizeof(T));
static_assert(std::is_trivially_copyable_v<T>);
if ((mDataPos+sizeof(val)) <= mDataCapacity) {
restart_write:
// 内存复制,写入数据到Parcel的字段mData指向的内存中
// 我们本次调用的参数1和2会被依次写入
// mData可以理解为Parcel的数据存储区
memcpy(mData + mDataPos, &val, sizeof(val));
return finishWrite(sizeof(val));
}
// 数据存储区容量不够,所以扩容之后,goto跳转到restart_write再次尝试写入
status_t err = growData(sizeof(val));
if (err == NO_ERROR) goto restart_write;
return err;
}
数据写入完毕,准备发起请求mRemote#transact。
调试得知,mRemote为BinderProxy类,因此看看它的transact函数。
// frameworks/base/core/java/android/os/BinderProxy.java
public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
... ...
final boolean result = transactNative(code, data, reply, flags);
... ...
return result;
}
// frameworks/base/core/jni/android_util_Binder.cpp
static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,
jint code, jobject dataObj, jobject replyObj, jint flags) {
... ...
// ① 通过gParcelOffsets存储的信息,从Parcel的Java对象中取出其在native层的指针
Parcel* data = parcelForJavaObject(env, dataObj);
... ...
Parcel* reply = parcelForJavaObject(env, replyObj);
... ...
// ② 取出BinderProxyNativeData.mObject中存放的BpBinder对象指针
IBinder* target = getBPNativeData(env, obj)->mObject.get();
... ...
// 调用BpBinder#transact
status_t err = target->transact(code, *data, reply, flags);
... ...
}
// frameworks/base/core/jni/android_os_Parcel.cpp
Parcel* parcelForJavaObject(JNIEnv* env, jobject obj) {
if (obj) {
Parcel* p = (Parcel*)env->GetLongField(obj, gParcelOffsets.mNativePtr);
if (p != NULL) {
return p;
}
jniThrowException(env, "java/lang/IllegalStateException", "Parcel has been finalized!");
}
return NULL;
}
① gParcelOffsets为存放Parcel Java类信息的结构体,其中gParcelOffsets.mNativePtr存放的是Parcel Java类对象中字段mNativePtr的Field ID,通过GetLongField函数取出obj对象实例中mNativePtr变量的值,也就是Parcel native对象的指针。gParcelOffsets的初始化也值得一看,若感兴趣可以参考附录中的介绍。
② BinderProxyNativeData是一个结构体,其中mObject字段保存了BpBinder的指针。BpBinder是BinerProxy类在native中的代言人。BinderProxyNativeData的指针取出过程与Parcel类似,使用到的类信息结构体是gBinderProxyOffsets。
// frameworks/native/libs/binder/BpBinder.cpp
status_t BpBinder::transact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
// Once a binder has died, it will never come back to life.
if (mAlive) {
... ...
status_t status;
if (CC_UNLIKELY(isRpcBinder())) {
... ...
} else {
... ...
// binderHandle()返回当前BpBinder的handle,handle为32位有符号整型,可以理解为代表S端Binder的编号
status = IPCThreadState::self()->transact(binderHandle(), code, data, reply, flags);
}
... ...
if (status == DEAD_OBJECT) mAlive = 0;
return status;
}
return DEAD_OBJECT;
}
// frameworks/native/libs/binder/IPCThreadState.cpp
status_t IPCThreadState::transact(int32_t handle,
uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags) {
... ...
status_t err;
... ...
// 写入传输数据,其中BC_TRANSACTION为通信指令,BC开头是C端或S端发给Binder驱动的,BR开头是Binder驱动回复给C或S端的
// 后面研究Binder通信模型时再全面的讨论这些通信指令
err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, nullptr);
... ...
if ((flags & TF_ONE_WAY) == 0) {
... ...
if (reply) {
// 发送数据
err = waitForResponse(reply);
} else {
Parcel fakeReply;
err = waitForResponse(&fakeReply);
}
} else {
err = waitForResponse(nullptr, nullptr);
}
return err;
}
看看写入了哪些传输数据。什么又写数据,刚才不是已经写到Parcel#mData中去了吗?仔细看以下代码,它将之前的Parcel即当前的data放到了tr.data.ptr.buffer字段中,且这个tr结构体中还放了handle(S端的Binder编号)、code(S端接口编号)等,同时在最前面加上了cmd(通信指令)。一起写入到mOut中,mOut类型为Parcel,是IPCThreadState中的一个字段。这下就清晰了,mData中写入的是通信数据本身:比如我们要传输的int参数1和2。mOut是通信协议数据(通信指令)+通信数据。像不像网络通信协议?协议头+数据包。发送方像打包套娃一样,层层包装,接收方像拆套娃一样,层层解开。
// frameworks/native/libs/binder/IPCThreadState.cpp
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.handle = handle;
tr.code = code;
... ...
const status_t err = data.errorCheck();
if (err == NO_ERROR) {
tr.data_size = data.ipcDataSize();
tr.data.ptr.buffer = data.ipcData();
... ...
}
... ...
mOut.writeInt32(cmd);
mOut.write(&tr, sizeof(tr));
return NO_ERROR;
}
接下来,我们看看waitForResponse,这次真的要发送数据了。waitForResponse函数中调用talkWithDriver函数与驱动交互,外层用while(1)循环包住,目的是持续调用,直到mIn中有驱动返回的数据。即使请求是非阻塞的(oneway情况)也需要等待驱动回复,通常情况下回复的是BR_TRANSACTION_COMPLETE。得到回复之后,读取通信指令,在switch中对应处理。
// frameworks/native/libs/binder/IPCThreadState.cpp
status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult) {
uint32_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;
cmd = (uint32_t)mIn.readInt32();
... ...
switch (cmd) {
... ...
case BR_TRANSACTION_COMPLETE:
if (!reply && !acquireResult) goto finish;
break;
... ...
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;
logExtendedError();
}
return err;
}
再来看看talkWithDriver。终于出现了系统调用ioctl的身影,这也就意味着Framework层与kernel交互的边界。我们注意到系统调用被包裹在一个while中。这是因为ioctl可能会被其他系统中断打断,此时全局变量errno会被系统赋值为EINTR。因此需要再次尝试ioctl。
// frameworks/native/libs/binder/IPCThreadState.cpp
// 与Binder驱动交互的函数
status_t IPCThreadState::talkWithDriver(bool doReceive) {
if (mProcess->mDriverFD < 0) {
return -EBADF;
}
// binder_write_read结构体存放写入和读出地址以及大小
binder_write_read bwr;
// Is the read buffer empty?
const bool needRead = mIn.dataPosition() >= mIn.dataSize();
// We don't want to write anything if we are still reading
// from data left in the input buffer and the caller
// has requested to read the next data.
const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0;
bwr.write_size = outAvail;
// 写入数据的地址存到write_buffer中
bwr.write_buffer = (uintptr_t)mOut.data();
// This is what we'll read.
if (doReceive && needRead) {
bwr.read_size = mIn.dataCapacity();
// 读出数据的地址存到read_buffer中
bwr.read_buffer = (uintptr_t)mIn.data();
} else {
bwr.read_size = 0;
bwr.read_buffer = 0;
}
... ...
// Return immediately if there is nothing to do.
if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR;
bwr.write_consumed = 0;
bwr.read_consumed = 0;
status_t err;
do {
... ...
#if defined(__ANDROID__)
// 系统调用ioctl请求Binder驱动读写数据
if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
err = NO_ERROR;
else
err = -errno;
#else
err = INVALID_OPERATION;
#endif
if (mProcess->mDriverFD < 0) {
err = -EBADF;
}
... ...
} while (err == -EINTR);
... ...
if (err >= NO_ERROR) {
if (bwr.write_consumed > 0) {
if (bwr.write_consumed < mOut.dataSize())
LOG_ALWAYS_FATAL("Driver did not consume write buffer. "
"err: %s consumed: %zu of %zu",
statusToString(err).c_str(),
(size_t)bwr.write_consumed,
mOut.dataSize());
else {
mOut.setDataSize(0);
processPostWriteDerefs();
}
}
if (bwr.read_consumed > 0) {
mIn.setDataSize(bwr.read_consumed);
mIn.setDataPosition(0);
}
... ...
return NO_ERROR;
}
... ...
return err;
}
到这里,C端的操作全部结束。
接收请求
内核Binder驱动一番操作猛如虎,而与此同时,S端的Binder线程也在等待处理Binder消息。这里我们先闪回到第7篇SystemServer启动流程,当时我们说以下函数中会创建线程池,其实就是创建的Binder线程池。
// frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
public static Runnable zygoteInit(int targetSdkVersion, long[] disabledCompatChanges,
String[] argv, ClassLoader classLoader) {
... ...
// 通过JNI调用native层函数,最终调用ProcessState::self()->startThreadPool();
// ProcessState是创建和保存进程相关数据的类,比如:启动Binder线程池、初始化Binder缓冲区等
ZygoteInit.nativeZygoteInit();
return RuntimeInit.applicationInit(targetSdkVersion, disabledCompatChanges, argv, classLoader);
}
启动线程池之后,经过一番调用,最终会来到IPCThreadState::joinThreadPool。可以看到,这里启动了无限循环,不停的调用getAndExecuteCommand。
// frameworks/native/libs/binder/IPCThreadState.cpp
void IPCThreadState::joinThreadPool(bool isMain) {
... ...
mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER);
mIsLooper = true;
status_t result;
do {
processPendingDerefs();
// 尝试从Binder驱动获取消息并处理
result = getAndExecuteCommand();
... ...
} while (result != -ECONNREFUSED && result != -EBADF);
... ...
mOut.writeInt32(BC_EXIT_LOOPER);
mIsLooper = false;
talkWithDriver(false);
... ...
}
// frameworks/native/libs/binder/IPCThreadState.cpp
status_t IPCThreadState::getAndExecuteCommand() {
status_t result;
int32_t cmd;
// 再次与驱动对话,前面我们已经分析过,talkWithDriver的作用就是写入和读取驱动数据
result = talkWithDriver();
if (result >= NO_ERROR) {
size_t IN = mIn.dataAvail();
if (IN < sizeof(int32_t)) return result;
cmd = mIn.readInt32();
... ...
// 依据返回中得到的cmd来执行命令
result = executeCommand(cmd);
... ...
}
return result;
}
驱动这里回复的指令是BR_TRANSACTION。
// frameworks/native/libs/binder/IPCThreadState.cpp
status_t IPCThreadState::executeCommand(int32_t cmd) {
BBinder* obj;
RefBase::weakref_type* refs;
status_t result = NO_ERROR;
switch ((uint32_t)cmd) {
... ...
case BR_TRANSACTION:
{
... ...
Parcel buffer;
// tr是binder_transaction_data结构体,将tr中的数据引用放到buffer中
buffer.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);
... ...
// 设置C端的信息,即S端通过驱动可以知道是谁在调用接口,这样的话,AMS、WMS这类系统服务可以做一些权限控制
mCallingPid = tr.sender_pid;
mCallingSid = reinterpret_cast<const char*>(tr_secctx.secctx);
mCallingUid = tr.sender_euid;
mHasExplicitIdentity = false;
mLastTransactionBinderFlags = tr.flags;
... ...
if (tr.target.ptr) {
if (reinterpret_cast<RefBase::weakref_type*>(
tr.target.ptr)->attemptIncStrong(this)) {
// tr.cookie存放的是JavaBBinder指针
error = reinterpret_cast<BBinder*>(tr.cookie)->transact(tr.code, buffer,
&reply, tr.flags);
reinterpret_cast<BBinder*>(tr.cookie)->decStrong(this);
} else {
error = UNKNOWN_TRANSACTION;
}
} else {
error = the_context_object->transact(tr.code, buffer, &reply, tr.flags);
}
}
break;
default:
ALOGE("*** BAD COMMAND %d received from Binder driver\n", cmd);
result = UNKNOWN_ERROR;
break;
}
if (result != NO_ERROR) {
mLastError = result;
}
return result;
}
下一步调用JavaBBinder#transact -> BBinder::transact -> JavaBBinder#onTransact。
// frameworks/base/core/jni/android_util_Binder.cpp
status_t onTransact(uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags = 0) override {
... ...
// native层调用Binder Java类中的execTransact函数
jboolean res = env->CallBooleanMethod(mObject, gBinderOffsets.mExecTransact,
code, reinterpret_cast<jlong>(&data), reinterpret_cast<jlong>(reply), flags);
... ...
return res != JNI_FALSE ? NO_ERROR : UNKNOWN_TRANSACTION;
}
Binder#execTransact -> Binder#execTransactInternal
// frameworks/base/core/java/android/os/Binder.java
private boolean execTransactInternal(int code, Parcel data, Parcel reply, int flags,
int callingUid) {
... ...
if ((flags & FLAG_COLLECT_NOTED_APP_OPS) != 0 && callingUid != -1) {
AppOpsManager.startNotedAppOpsCollection(callingUid);
try {
res = onTransact(code, data, reply, flags);
} finally {
AppOpsManager.finishNotedAppOpsCollection();
}
} else {
res = onTransact(code, data, reply, flags);
}
... ...
return res;
}
由于ICount.Stub是Binder的子类,所以这里就调用ICount.Stub#onTransact。整个流程最终来到了CountBinder#sum函数。
综上,一次Binder请求发送和接收的流程如下图所示:
匿名Binder的传递
上一小节我们探讨通信流程是建立在Client端已拿到ICount接口的基础上,那么本小节就来看看这条所谓的匿名通道是怎么建立的。本文开头提到Binder通道可以传输Binder本身,因此可以大胆猜测从传输流程上来看,传输基本数据类型和传输Binder对象没有区别,只是Binder对象作为一种特殊的数据,从数据写入读出以及Binder驱动对待它的方式都会有所不同。
通常情况下,我们都是在bindService的回调中获得S端的IBinder,那么我们就先看看binderService的大致流程:
橙色通信就是我们要分析的目标。需要明确一点,这次通信CountService是Client端,而AMS是Server端,传递的数据中包含另外一个Binder接口(CountBinder)。
Binder接口的序列化
我们的起点是CountService进程中的ActivityThread#handleBindService函数。至于为什么是这里,如果我们看过Service业务流程就会明白,这里就当开了一次天眼吧😂。
// frameworks/base/core/java/android/app/ActivityThread.java
private void handleBindService(BindServiceData data) {
// s就是android.app.service的实例
// 调用service的onBind获得binder实例,在我们这个例子中就是CountBinder类的实例
IBinder binder = s.onBind(data.intent);
// 调用AMS接口将CountBinder传过去
ActivityManager.getService().publishService(data.token, data.intent, binder);
}
基于CountBinder的继承关系,当new CountBinder时,会自动调用父类构造函数,因此,我们看看Binder的构造函数。
// frameworks/base/core/java/android/os/Binder.java
// 存放native层代理对象的指针
private final long mObject;
public Binder() {
this(null);
}
public Binder(@Nullable String descriptor) {
// 调用JNI函数创建native层代理对象,并存入mObject中
mObject = getNativeBBinderHolder();
// NativeAllocationRegistry,Android 8.0引入的辅助回收native内存的机制
// Java对象被GC回收时会自动释放与之相关的native对象内存
NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, mObject);
... ...
mDescriptor = descriptor;
}
// frameworks/base/core/jni/android_util_Binder.cpp
static jlong android_os_Binder_getNativeBBinderHolder(JNIEnv* env, jobject clazz) {
// 创建了一个JavaBBinderHolder对象实例
JavaBBinderHolder* jbh = new JavaBBinderHolder();
return (jlong) jbh;
}
JavaBBinderHolder看名字就像是一个空壳类,它没有显示构造函数,所以这里也就只是创建了一个类实例,啥也没干。
// frameworks/base/core/jni/android_util_Binder.cpp
class JavaBBinderHolder {
public:
sp<JavaBBinder> get(JNIEnv* env, jobject obj) {...}
... ...
private:
wp<JavaBBinder> mBinder;
}
到目前为止,我们只创建了Binder和JavaBBinderHolder,下一步就是调用远程接口publishService发送Binder了。根据Java层的分析,Client使用AIDL生成的Stub.Proxy类发送请求,那么我们这里就要看publishService接口对应的Proxy类实现。
ActivityManager.getService()获得的是IActivityManager接口,打开IActivityManager.java看看。
// IActivitManager.java
// 此Java文件在AOSP编译过程中通过AIDL生成到out目录
public interface IActivityManager extends android.os.IInterface {
public static abstract class Stub extends android.os.Binder
implements android.app.IActivityManager {
... ...
private static class Proxy implements android.app.IActivityManager {
public void publishService(IBinder token, Intent intent, IBinder service) throws RemoteException {
... ...
Parcel _data = Parcel.obtain(asBinder());
... ...
_data.writeStrongBinder(service);
boolean _status = mRemote.transact(Stub.TRANSACTION_publishService, _data, _reply, 0);
... ...
}
}
}
}
通过Parcel#writeStrongBinder写入Binder。
// frameworks/base/core/java/android/os/Parcel.java
public final void writeStrongBinder(IBinder val) {
// mNativePtr为Parcel在native层中的对象指针,
// 可见Parcel.java也是一个代理类,它只是native Parcel在Java层的代表
nativeWriteStrongBinder(mNativePtr, val);
}
// frameworks/base/core/jni/android_os_Parcel.cpp
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));
... ...
}
}
// frameworks/base/core/jni/android_util_Binder.cpp
// 此函数的作用就是通过Java对象,获取对应的native层IBinder对象指针
// 这里有两种Java对象:Binder.java和BinderProxy.java
sp<IBinder> ibinderForJavaObject(JNIEnv* env, jobject obj) {
if (obj == NULL) return NULL;
// 对obj做类型判断,看是否是Binder Java类
// 此时obj就是Java层onBind函数返回的CountBinder
if (env->IsInstanceOf(obj, gBinderOffsets.mClass)) {
// ① 将刚才创建的JavaBBinderHolder指针从Java类中取出来
// gBinderOffsets为存放Binder.java类信息的结构体
JavaBBinderHolder* jbh = (JavaBBinderHolder*)
env->GetLongField(obj, gBinderOffsets.mObject);
if (jbh == nullptr) {
ALOGE("JavaBBinderHolder null on binder");
return nullptr;
}
// 调用JavaBBinderHolder的get函数
return jbh->get(env, obj);
}
// 对obj做类型判断,看是否是BinderProxy Java类
if (env->IsInstanceOf(obj, gBinderProxyOffsets.mClass)) {
return getBPNativeData(env, obj)->mObject;
}
ALOGW("ibinderForJavaObject: %p is not a Binder object", obj);
return NULL;
}
① env->GetLongField函数是native层通过JNI获取Java层对象字段值的方法。同理,也会有native层调用Java对象方法的函数。总之,Java和native可以通过JNI层互相对调。
取出JavaBBinderHolder之后,调用了它的get函数。
// frameworks/base/core/jni/android_util_Binder.cpp
class JavaBBinderHolder {
public:
sp<JavaBBinder> get(JNIEnv* env, jobject obj) {
... ...
// 尝试从wp中取出JavaBBinder的指针
sp<JavaBBinder> b = mBinder.promote();
if (b == NULL) {
b = new JavaBBinder(env, obj);
... ...
// 将创建的JavaBBinder保存到mBinder中
mBinder = b;
}
return b;
}
... ...
private:
wp<JavaBBinder> mBinder;
}
mBinder此时为空,因此创建JavaBBinder实例。JavaBBinder继承自BBinder,且它的mObject字段存放Java Binder的native对象。
// frameworks/base/core/jni/android_util_Binder.cpp
class JavaBBinder : public BBinder {
public:
JavaBBinder(JNIEnv* env, jobject /* Java Binder */ object)
: mVM(jnienv_to_javavm(env)), mObject(env->NewGlobalRef(object)) {
... ...
}
private:
... ...
jobject const mObject; // GlobalRef to Java Binder
... ...
}
由以上分析,我们可以得出以下类结构:可以看出Java层和native层之间通过互相持有其对象或对象指针产生联系。
ibinderForJavaObject函数(返回新创建的JavaBBinder)结束后,返回到调用处Parcel#writeStrongBinder。
// frameworks/native/libs/binder/Parcel.cpp
status_t Parcel::writeStrongBinder(const sp<IBinder>& val) {
// val为JavaBBinder实例
return flattenBinder(val);
}
// flatten,展平、压扁,其实就是序列化binder,为跨进程传输做准备
status_t Parcel::flattenBinder(const sp<IBinder>& binder) {
BBinder* local = nullptr;
// BBinder::localBinder(),返回this指针 (JavaBBinder并没有实现localBinder函数)
// BpBinder没有实现localBinder函数,因此调用父类IBinder::localBinder(),返回nullptr
if (binder) local = binder->localBinder();
if (local) local->setParceled();
... ...
// 存放BBinder/BpBinder信息的结构体,它将被写入Parcel的数据区中
flat_binder_object obj;
... ...
if (binder != nullptr) {
if (!local) {// 若是BpBinder
BpBinder *proxy = binder->remoteBinder();
... ...
// BINDER_TYPE_HANDLE类型:obj保存的是BpBinder
obj.hdr.type = BINDER_TYPE_HANDLE;
obj.binder = 0;
obj.flags = 0;
obj.handle = handle;
obj.cookie = 0;
} else { // 若是BBinder(当前我们讨论的情况是走这个分支)
... ...
// BINDER_TYPE_BINDER类型:obj保存的是BBinder
obj.hdr.type = BINDER_TYPE_BINDER;
// binder字段存放一个BBinder的弱引用
obj.binder = reinterpret_cast<uintptr_t>(local->getWeakRefs());
// cookie字段存放BBinder对象指针(即JavaBBinder)
obj.cookie = reinterpret_cast<uintptr_t>(local);
}
} else {
obj.hdr.type = BINDER_TYPE_BINDER;
obj.flags = 0;
obj.binder = 0;
obj.cookie = 0;
}
... ...
// 将flat_binder_object写入Parcel的缓冲区
status_t status = writeObject(obj, false);
if (status != OK) return status;
return finishFlattenBinder(binder);
}
status_t Parcel::writeObject(const flat_binder_object& val, bool nullMetaData) {
... ...
const bool enoughData = (mDataPos+sizeof(val)) <= mDataCapacity;
const bool enoughObjects = kernelFields->mObjectsSize < kernelFields->mObjectsCapacity;
if (enoughData && enoughObjects) {
restart_write:
// 将flat_binder_object写入Parcel#mData+mDataPos指向的缓冲区中
// 注意最前面的*号,这里是通过指针解引用写入val到对应的内存地址中
*reinterpret_cast<flat_binder_object*>(mData+mDataPos) = val;
... ...
// 写入后,根据刚才数据的大小,移动mDataPos和增加mDataSize
return finishWrite(sizeof(flat_binder_object));
}
// 若缓冲区大小不足,则扩容,然后跳转到restart_write,再尝试写入
... ...
goto restart_write;
}
数据准备好了,接下来的发送流程就和上一小节一致了。
简单总结一下BBinder是如何被打包的:
- JavaBBinder的信息会被分解重组成结构体flat_binder_object
- flat_binder_object被写入Parcel的数据区mData中
- 对端Binder信息(handle值、code等)+ mData组装成binder_transaction_data结构体
- 通信指令 + binder_transaction_data,写入mOut中
- mOut存入binder_write_read结构体中
- Ready to Go!
Binder接口的反序列化
由之前的分析得知,AMS侧(也就是这次通信的Server端)的Binder线程收到驱动发来的消息后,会调用IPCThreadState::executeCommand处理:
// frameworks/native/libs/binder/IPCThreadState.cpp
status_t IPCThreadState::executeCommand(int32_t cmd) {
case BR_TRANSACTION:
... ...
result = mIn.read(&tr_secctx, sizeof(tr_secctx));
... ...
Parcel buffer;
// ipcSetDataReference函数中解包,取出数据,放入mData中
buffer.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);
}
Parcel已经准备好后,调用IActivityManager.Stub#onTransact。
// IActivitManager.java
// 此Java文件在AOSP编译过程中通过AIDL生成到out目录
public interface IActivityManager extends android.os.IInterface {
public static abstract class Stub extends android.os.Binder
implements android.app.IActivityManager {
... ...
case TRANSACTION_publishService:
{
android.os.IBinder _arg0;
_arg0 = data.readStrongBinder();
android.content.Intent _arg1;
_arg1 = data.readTypedObject(android.content.Intent.CREATOR);
android.os.IBinder _arg2;
_arg2 = data.readStrongBinder();
data.enforceNoDataAvail();
this.publishService(_arg0, _arg1, _arg2);
reply.writeNoException();
break;
}
... ...
}
}
data.readStrongBinder()读出IBinder。
// frameworks/base/core/java/android/os/Parcel.java
public final IBinder readStrongBinder() {
final IBinder result = nativeReadStrongBinder(mNativePtr);
... ...
return result;
}
// frameworks/base/core/jni/android_os_Parcel.cpp
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;
}
先来看看readStrongBinder。
// frameworks/native/libs/binder/Parcel.cpp
sp<IBinder> Parcel::readStrongBinder() const {
sp<IBinder> val;
readNullableStrongBinder(&val);
return val;
}
status_t Parcel::readNullableStrongBinder(sp<IBinder>* val) const {
return unflattenBinder(val);
}
status_t Parcel::unflattenBinder(sp<IBinder>* out) const {
... ...
// 从Parcel.mData中读出flat_binder_object
const flat_binder_object* flat = readObject(false);
if (flat) {
switch (flat->hdr.type) {
case BINDER_TYPE_BINDER: {
sp<IBinder> binder =
sp<IBinder>::fromExisting(reinterpret_cast<IBinder*>(flat->cookie));
return finishUnflattenBinder(binder, out);
}
// ① 此时会走BINDER_TYPE_HANDLE分支
case BINDER_TYPE_HANDLE: {
sp<IBinder> binder =
ProcessState::self()->getStrongProxyForHandle(flat->handle);
return finishUnflattenBinder(binder, out);
}
}
}
return BAD_TYPE;
}
① 这里可能会非常疑惑,在flattenBinder函数中hdr.type不是赋值为BINDER_TYPE_BINDER吗?这里怎么走了BINDER_TYPE_HANDLE?物理学又不存在了?其实这里的hdr.type其实是被Binder驱动修改的,具体内核代码如下:
// common/drivers/android/binder.c
static int binder_translate_binder(struct flat_binder_object *fp,
struct binder_transaction *t,
struct binder_thread *thread) {
... ...
if (fp->hdr.type == BINDER_TYPE_BINDER)
fp->hdr.type = BINDER_TYPE_HANDLE;
else
fp->hdr.type = BINDER_TYPE_WEAK_HANDLE;
... ...
}
其实也能想通,BBinder经过驱动传递给Client端使用,那么就必须转换为BpBinder,Binder中的代理类就是工作在Client端,代表Server端接口的类。不过,在没看内核代码之前真的是百思不得其解,或许这也是Binder一直比较晦涩难懂的原因:不追根溯源,就没办法看的很透彻。那Binder驱动岂不是要安排一篇?让我缓缓(掐人中)。。。
// frameworks/native/libs/binder/ProcessState.cpp
sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle) {
sp<IBinder> result;
// 尝试从集合中查找handle对应的handle_entry是否存在,若不存在则创建一个新的且空的handle_entry
// handle_entry是存放BpBinder的结构体
handle_entry* e = lookupHandleLocked(handle);
if (e != nullptr) {
IBinder* b = e->binder;
if (b == nullptr || !e->refs->attemptIncWeak(this)) {
if (handle == 0) {
// 针对handle值为0做了特殊处理
// 这个handle值为0的东西是我们后面一篇主角,这里先按下不表
... ...
}
// 创建BpBinder
sp<BpBinder> b = BpBinder::PrivateAccessor::create(handle);
e->binder = b.get();
if (b) e->refs = b->getWeakRefs();
result = b;
} else {
result.force_set(b);
e->refs->decWeak(this);
}
}
return result;
}
BpBinder创建完成之后,我们回到javaObjectForIBinder。从名字可以看出,这个函数和ibinderForJavaObject作用相反。
// frameworks/base/core/jni/android_util_Binder.cpp
jobject javaObjectForIBinder(JNIEnv* env, const sp<IBinder>& val) {
if (val == NULL) return NULL;
... ...
// 创建BinderProxyNativeData结构体,并将BpBinder指针存入mObject
BinderProxyNativeData* nativeData = new BinderProxyNativeData();
nativeData->mOrgue = new DeathRecipientList;
nativeData->mObject = val;
// 调用BinderProxy Java类的getInstance创建BinderProxy实例
// 调用参数是BinderProxyNativeData和BpBinder的指针
jobject object = env->CallStaticObjectMethod(gBinderProxyOffsets.mClass,
gBinderProxyOffsets.mGetInstance, (jlong) nativeData, (jlong) val.get());
... ...
return object;
}
终于,Client端所需的Proxy们建立完毕。剩下还有一小段流程是AMS调用IServiceConnection#connected将Proxy发送到Client APP。
Proxy们的类结构图如下:
总结
本篇我们从App开发日常代码出发,梳理了Binder通信从Java层到native,再从native回到Java的流程。同时,还重点关注了Binder本身在通信中的序列化反序列化过程。可以看出,代理类和本尊类在Java、native层都有自己的形态,其实在kernel层也有代理类和本尊类的内核形态。各级形态互相关联,最终打通了Binder的通信链路。
附录
gParcelOffsets的初始化
// frameworks/base/core/jni/android_os_Parcel.cpp
// 声明结构体parcel_offsets_t,同时声明了一个该结构体的变量gParcelOffsets
static struct parcel_offsets_t {
jclass clazz;
jfieldID mNativePtr;
jmethodID obtain;
jmethodID recycle;
} gParcelOffsets;
const char* const kParcelPathName = "android/os/Parcel";
int register_android_os_Parcel(JNIEnv* env) {
// 获取Parcel的Java类对象
jclass clazz = FindClassOrDie(env, kParcelPathName);
// 创建一个Parcel Java类对象的全局引用并赋给gParcelOffsets.clazz
gParcelOffsets.clazz = MakeGlobalRefOrDie(env, clazz);
// 获取Parcel Java类中的mNativePtr字段ID并赋给gParcelOffsets.mNativePtr
gParcelOffsets.mNativePtr = GetFieldIDOrDie(env, clazz, "mNativePtr", "J");
// 获取Parcel Java类中的obtain函数ID并赋给gParcelOffsets.obtain
gParcelOffsets.obtain = GetStaticMethodIDOrDie(env, clazz, "obtain", "()Landroid/os/Parcel;");
gParcelOffsets.recycle = GetMethodIDOrDie(env, clazz, "recycle", "()V");
return RegisterMethodsOrDie(env, kParcelPathName, gParcelMethods, NELEM(gParcelMethods));
}
看来gParcelOffsets是用来存放Parcel的Java类信息的结构体,那么这个结构体是在哪里初始化的呢?换句话说就是register_android_os_Parcel函数是什么时候调用的?还记得吗,本系列第6篇我们讲Zygote的时候提到在AndroidRuntime::start函数中会通过startReg注册JNI函数,如下:
// frameworks/base/core/jni/AndroidRuntime.cpp
// REG_JNI宏定义声明了一个结构体的初始化代码块,参数为name,一个函数名也是一个函数指针
#define REG_JNI(name) { name }
// RegJNIRec结构体包含一个函数指针变量,且该函数的参数为JNIEnv指针
struct RegJNIRec {
int (*mProc)(JNIEnv*);
};
// JNI初始化函数数组
static const RegJNIRec gRegJNI[] = {
... ...
REG_JNI(register_android_os_Binder),
REG_JNI(register_android_os_Parcel),
... ...
REG_JNI(register_android_os_ServiceManager),
REG_JNI(register_android_os_ServiceManagerNative),
... ...
};
// 将gRegJNI数组中的每一个函数都执行一遍,达到初始化JNI函数的目的
int AndroidRuntime::startReg(JNIEnv* env) {
... ...
if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) {
env->PopLocalFrame(NULL);
return -1;
}
... ...
return 0;
}
static int register_jni_procs(const RegJNIRec array[], size_t count, JNIEnv* env) {
for (size_t i = 0; i < count; i++) {
// 依次执行每一个注册JNI的函数
if (array[i].mProc(env) < 0) {
... ...
return -1;
}
}
return 0;
}
此外,frameworks/base/core/jni/android_util_Binder.cpp中还有很多这样的结构体,比如:gBinderProxyOffsets、gBinderOffsets,它们就像一份份说明书,指导native层代码去Java层获取或调用所需要的值或方法。