再学安卓 - binder之Framework通信流程

253 阅读23分钟

9_0.png

本文分析基于Android14(aosp分支android-14.0.0_r28)

前言

在上一篇中,SystemServer和普通APP进程的通信已经出现了binder的身影,这是Android领域的热门话题,应用工程师、Framework工程师,甚至内核工程师都在谈论它。我们工作中用它,面试时还要考它。如此重要的“基建”模块,在探索更广大的业务逻辑之前,值得我们深入研究。网络上已有众多优秀的文章阐述它的特点和优势,在这里就不复述了,我的思路是从源码层面理解它,再回头去看这些文字一定会有自己的感受和想法。

9_1.png

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为示例代码,直接省略):

9_2.png

  • 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。

9_3.png

调试得知,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请求发送和接收的流程如下图所示:

9_4.png

匿名Binder的传递

上一小节我们探讨通信流程是建立在Client端已拿到ICount接口的基础上,那么本小节就来看看这条所谓的匿名通道是怎么建立的。本文开头提到Binder通道可以传输Binder本身,因此可以大胆猜测从传输流程上来看,传输基本数据类型和传输Binder对象没有区别,只是Binder对象作为一种特殊的数据,从数据写入读出以及Binder驱动对待它的方式都会有所不同。

通常情况下,我们都是在bindService的回调中获得S端的IBinder,那么我们就先看看binderService的大致流程:

9_5.png

橙色通信就是我们要分析的目标。需要明确一点,这次通信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层之间通过互相持有其对象或对象指针产生联系。

9_6.png

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是如何被打包的:

  1. JavaBBinder的信息会被分解重组成结构体flat_binder_object
  2. flat_binder_object被写入Parcel的数据区mData中
  3. 对端Binder信息(handle值、code等)+ mData组装成binder_transaction_data结构体
  4. 通信指令 + binder_transaction_data,写入mOut中
  5. mOut存入binder_write_read结构体中
  6. 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们的类结构图如下:

9_7.png

总结

本篇我们从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层获取或调用所需要的值或方法。