Binder 机制 2

894 阅读28分钟

四、Android Binder系统-Native层

Framework是一个中间层,它对接了底层实现,封装了复杂的内部逻辑,并提供供外部使用的接口。Framework层是应用程序开发的基础。

Binder Framework层分为C++和Java两个部分,为了达到功能的复用,中间通过JNI进行衔接。

Binder Framework的C++部分,头文件位于这个路径:/frameworks/native/include/binder/,实现位于这个路径:/frameworks/native/libs/binder/ 。Binder库最终会编译成一个动态链接库:libbinder.so,供其他进程链接使用。

为了便于说明,下文中我们将Binder Framework 的C++部分称之为libbinder。首先说一下ServiceManager,然后详细介绍。

(1)、ServiceManager类图(Native层)

IServiceManager相关类如下图所示:

IServiceManager是表示servicemanager的接口,有如下方法:

  1. getService获得binder service引用,

  2. checkService获得binder service引用,

  3. addService添加binder service,

  4. listServices 列举所有binder service。

servicemanager的binder service服务端其实是在frameworks/base/cmds/servicemanager 里实现,BnServiceMananger实际上并未使用。BpServiceMananger就是利用获得的IBinder指针建立的IServiceMananger对象的实际类型。

(2)、Binder框架Native层

libbinder中,将实现分为Proxy和Native两端。Proxy对应了上文提到的Client端,是服务对外提供的接口。而Native是服务实现的一端,对应了上文提到的Server端。类名中带有小写字母p的(例如BpInterface),就是指Proxy端。类名带有小写字母n的(例如BnInterface),就是指Native端。

Proxy代表了调用方,通常与服务的实现不在同一个进程,因此下文中,我们也称Proxy端为”远程”端。Native端是服务实现的自身,因此下文中,我们也称Native端为”本地”端。

这里,我们先对libbinder中的主要类做一个简要说明,了解一下它们的关系,然后再详细的讲解。

下图描述了这些类之间的关系:

另外说明一下,Binder服务的实现类(图中紫色部分)通常都会遵守下面的命名规则:

☯ 服务的接口使用I字母作为前缀 ☯ 远程接口使用Bp作为前缀 ☯ 本地接口使用Bn作为前缀

先来看看IBinder这个类。这个类描述了所有在Binder上传递的对象,它既是Binder本地对象BBinder的父类,也是Binder远程对象BpBinder的父类。这个类中的主要方法说明如下:

BpBinder的实例代表了远程Binder,这个类的对象将被客户端调用。其中handle方法会返回指向Binder服务实现者的句柄,这个类最重要就是提供了transact方法,这个方法会将远程调用的参数封装好发送的Binder驱动。

由于每个Binder服务通常都会提供多个服务接口,而这个方法中的uint32_t code参数就是用来对服务接口进行编号区分的。Binder服务的每个接口都需要指定一个唯一的code,这个code要在Proxy和Native端配对好。当客户端将请求发送到服务端的时候,服务端根据这个code(onTransact方法中)来区分调用哪个接口方法。

BBinder的实例代表了本地Binder,它描述了服务的提供方,所有Binder服务的实现者都要继承这个类(的子类),在继承类中,最重要的就是实现onTransact方法,因为这个方法是所有请求的入口。因此,这个方法是和BpBinder中的transact方法对应的,这个方法同样也有一个uint32_t code参数,在这个方法的实现中,由服务提供者通过code对请求的接口进行区分,然后调用具体实现服务的方法。

IBinder中定义了uint32_t code允许的范围:

FIRST_CALL_TRANSACTION  = 0x00000001,
LAST_CALL_TRANSACTION   = 0x00ffffff,

Binder服务要保证自己提供的每个服务接口有一个唯一的code,例如hello服务:

#define HELLO_SVR_CMD_SAYHELLO     1
#define HELLO_SVR_CMD_SAYHELLO_TO  2
#define HELLO_SVR_CMD_GET_FD       3

讲完了IBinder,BpBinder和BBinder三个类,我们再来看看BpReBase,IInterface,BpInterface和BnInterface。

每个Binder服务都是为了某个功能而实现的,因此其本身会定义一套接口集(通常是C++的一个类)来描述自己提供的所有功能。而Binder服务既有自身实现服务的类,也要有给客户端进程调用的类。为了便于开发,这两中类里面的服务接口应当是一致的,例如:假设服务实现方提供了一个接口为sayhello(void)的服务方法,那么其远程接口中也应当有一个sayhello(void)方法。因此为了实现方便,本地实现类和远程接口类需要有一个公共的描述服务接口的基类(即上图中的IXXXService)来继承。而这个基类通常是IInterface的子类,IInterface的定义如下:

class IInterface : public virtual RefBase
{
public:
            IInterface();
            static sp<IBinder>  asBinder(const IInterface*);
            static sp<IBinder>  asBinder(const sp<IInterface>&);

protected:
    virtual                     ~IInterface();
    virtual IBinder*            onAsBinder() = 0;
};

之所以要继承自IInterface类是因为这个类中定义了onAsBinder让子类实现。onAsBinder在本地对象的实现类中返回的是本地对象,在远程对象的实现类中返回的是远程对象。onAsBinder方法被两个静态方法asBinder方法调用。有了这些接口之后,在代码中便可以直接通过IXXX::asBinder方法获取到不用区分本地还是远程的IBinder对象。这个在跨进程传递Binder对象的时候有很大的作用(因为不用区分具体细节,只要直接调用和传递就好)。

下面,我们来看一下BpInterface和BnInterface的定义:

template<typename INTERFACE>
class BnInterface : public INTERFACE, public BBinder
{
public:
    virtual sp<IInterface>      queryLocalInterface(const String16& _descriptor);
    virtual const String16&     getInterfaceDescriptor() const;

protected:
    virtual IBinder*            onAsBinder();
};

// ----------------------------------------------------------------------

template<typename INTERFACE>
class BpInterface : public INTERFACE, public BpRefBase
{
public:
                                BpInterface(const sp<IBinder>& remote);

protected:
    virtual IBinder*            onAsBinder();
};

这两个类都是模板类,它们在继承自INTERFACE的基础上各自继承了另外一个类。这里的INTERFACE便是我们Binder服务接口的基类。另外,BnInterface继承了BBinder类,由此可以通过复写onTransact方法来提供实现。BpInterface继承了BpRefBase,通过这个类的remote方法可以获取到指向服务实现方的句柄。在客户端接口的实现类中,每个接口在组装好参数之后,都会调用remote()->transact来发送请求,而这里其实就是调用的BpBinder的transact方法,这样请求便通过Binder到达了服务实现方的onTransact中。这个过程如下图所示:

基于Binder框架开发的服务,除了满足上文提到的类名规则之外,还需要遵守其他一些共同的规约:

☯为了进行服务的区分,每个Binder服务需要指定一个唯一的标识,这个标识通过getInterfaceDescriptor返回,类型是一个字符串。通常,Binder服务会在类中定义static const android::String16 descriptor;这样一个常量来描述这个标识符,然后在getInterfaceDescriptor方法中返回这个常量。 ☯为了便于调用者获取到调用接口,服务接口的公共基类需要提供一个android::spasInterface方法来返回基类对象指针。 由于上面提到的这两点对于所有Binder服务的实现逻辑都是类似的。为了简化开发者的重复工作,在libbinder中,定义了两个宏来简化这些重复工作,它们是:

#define DECLARE_META_INTERFACE(INTERFACE)                            \
    static const android::String16 descriptor;                       \
    static android::sp<I##INTERFACE> asInterface(                    \
            const android::sp<android::IBinder>& obj);               \
    virtual const android::String16& getInterfaceDescriptor() const; \
    I##INTERFACE();                                                  \
    virtual ~I##INTERFACE();                                         \


#define IMPLEMENT_META_INTERFACE(INTERFACE, NAME)                    \
    const android::String16 I##INTERFACE::descriptor(NAME);          \
    const android::String16&                                         \
            I##INTERFACE::getInterfaceDescriptor() const {           \
        return I##INTERFACE::descriptor;                             \
    }                                                                \
    android::sp<I##INTERFACE> I##INTERFACE::asInterface(             \
            const android::sp<android::IBinder>& obj)                \
    {                                                                \
        android::sp<I##INTERFACE> intr;                              \
        if (obj != NULL) {                                           \
            intr = static_cast<I##INTERFACE*>(                       \
                obj->queryLocalInterface(                            \
                        I##INTERFACE::descriptor).get());            \
            if (intr == NULL) {                                      \
                intr = new Bp##INTERFACE(obj);                       \
            }                                                        \
        }                                                            \
        return intr;                                                 \
    }                                                                \
    I##INTERFACE::I##INTERFACE() { }                                 \
    I##INTERFACE::~I##INTERFACE() { }                                \

有了这两个宏之后,开发者只要在接口基类(IXXX)头文件中,使用DECLARE_META_INTERFACE宏便完成了需要的组件的声明。然后在cpp文件中使用IMPLEMENT_META_INTERFACE便完成了这些组件的实现。

2.1、Binder的初始化ProcessState

在讲解Binder驱动的时候我们就提到:任何使用Binder机制的进程都必须要对/dev/binder设备进行open以及mmap之后才能使用,这部分逻辑是所有使用Binder机制进程共同的。对于这种共同逻辑的封装便是Framework层的职责之一。libbinder中,ProcessState类封装了这个逻辑,相关代码见下文。

这里是ProcessState构造函数,在这个函数中,初始化mDriverFD的时候调用了open_driver方法打开binder设备,然后又在函数体中,通过mmap进行内存映射。

ProcessState::ProcessState()
    : mDriverFD(open_driver())
    , mVMStart(MAP_FAILED)
    , mThreadCountLock(PTHREAD_MUTEX_INITIALIZER)
    , mThreadCountDecrement(PTHREAD_COND_INITIALIZER)
    , mExecutingThreadsCount(0)
    , mMaxThreads(DEFAULT_MAX_BINDER_THREADS)
    , mStarvationStartTimeMs(0)
    , mManagesContexts(false)
    , mBinderContextCheckFunc(NULL)
    , mBinderContextUserData(NULL)
    , mThreadPoolStarted(false)
    , mThreadPoolSeq(1)
{
    if (mDriverFD >= 0) {
        mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
        ...
    }
}

open_driver的函数实现如下所示。在这个函数中完成了三个工作:

☯首先通过open系统调用打开了dev/binder设备 ☯然后通过ioctl获取Binder实现的版本号,并检查是否匹配 ☯最后通过ioctl设置进程支持的最大线程数量 关于这部分逻辑背后的处理,在讲解Binder驱动的时候,我们已经讲解过了。

static int open_driver()
{
    int fd = open("/dev/binder", O_RDWR | O_CLOEXEC);
    if (fd >= 0) {
        int vers = 0;
        status_t result = ioctl(fd, BINDER_VERSION, &vers);
        ...
        size_t maxThreads = DEFAULT_MAX_BINDER_THREADS;
        result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads);
    } else {
        ...
    }
    return fd;
}

ProcessState是一个Singleton(单例)类型的类,在一个进程中,只会存在一个实例。通过ProcessState::self()接口获取这个实例。一旦获取这个实例,便会执行其构造函数,由此完成了对于Binder设备的初始化工作。

2.2、关于Binder传递数据的大小限制

由于Binder的数据需要跨进程传递,并且还需要在内核上开辟空间,因此允许在Binder上传递的数据并不是无无限大的。mmap中指定的大小便是对数据传递的大小限制:

#define BINDER_VM_SIZE ((1*1024*1024) - (4096 *2)) // 1M - 8k
mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);

这里我们看到,在进行mmap的时候,指定了最大size为BINDER_VM_SIZE,即 1M - 8k的大小。 因此我们在开发过程中,一次Binder调用的数据总和不能超过这个大小。

对于这个区域的大小,我们也可以在设备上进行确认。这里我们还之前提到的system_server为例。上面我们讲解了通过procfs来获取映射的内存地址,除此之外,我们也可以通过showmap命令,来确定这块区域的大小,相关命令如下:

angler:/ # ps  | grep system_server                                            
system    1889  526   2353404 135968 SyS_epoll_ 72972eeaf4 S system_server
angler:/ # showmap 1889 | grep "/dev/binder"                                   
    1016        4        4        0        0        4        0        0    1 /dev/binder

这里可以看到,这块区域的大小正是 1M - 8K = 1016k。

Tips: 通过showmap命令可以看到进程的详细内存占用情况。在实际的开发过程中,当我们要对某个进程做内存占用分析的时候,这个命令是相当有用的。建议读者尝试通过showmap命令查看system_server或其他感兴趣进程的完整map,看看这些进程都依赖了哪些库或者模块,以及内存占用情况是怎样的。

2.3、与驱动的通信IPCThreadState

上文提到ProcessState是一个单例类,一个进程只有一个实例。而负责与Binder驱动通信的IPCThreadState也是一个单例类。但这个类不是一个进程只有一个实例,而是一个线程有一个实例。

IPCThreadState负责了与驱动通信的细节处理。这个类中的关键几个方法说明如下:

BpBinder::transact方法在发送请求的时候,其实就是直接调用了IPCThreadState对应的方法来发送请求到Binder驱动的,相关代码如下:

status_t BpBinder::transact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    if (mAlive) {
        status_t status = IPCThreadState::self()->transact(
            mHandle, code, data, reply, flags);
        if (status == DEAD_OBJECT) mAlive = 0;
        return status;
    }

    return DEAD_OBJECT;
}

而IPCThreadState::transact方法主要逻辑如下:

status_t IPCThreadState::transact(int32_t handle,
                                  uint32_t code, const Parcel& data,
                                  Parcel* reply, uint32_t flags)
{
    status_t err = data.errorCheck();

    flags |= TF_ACCEPT_FDS;

    if (err == NO_ERROR) {
        err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
    }

    if (err != NO_ERROR) {
        if (reply) reply->setError(err);
        return (mLastError = err);
    }

    if ((flags & TF_ONE_WAY) == 0) {
        if (reply) {
            err = waitForResponse(reply);
        } else {
            Parcel fakeReply;
            err = waitForResponse(&fakeReply);
        }
    } else {
        err = waitForResponse(NULL, NULL);
    }

    return err;
}

这段代码应该还是比较好理解的:首先通过writeTransactionData写入数据,然后通过waitForResponse等待返回结果。TF_ONE_WAY表示此次请求是单向的,即:不用真正等待结果即可返回。

而writeTransactionData方法其实就是在组装binder_transaction_data数据:

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.ptr = 0; /* Don't pass uninitialized stack data to a remote process */
    tr.target.handle = handle;
    tr.code = code;
    tr.flags = binderFlags;
    tr.cookie = 0;
    tr.sender_pid = 0;
    tr.sender_euid = 0;

    const status_t err = data.errorCheck();
    if (err == NO_ERROR) {
        tr.data_size = data.ipcDataSize();
        tr.data.ptr.buffer = data.ipcData();
        tr.offsets_size = data.ipcObjectsCount()*sizeof(binder_size_t);
        tr.data.ptr.offsets = data.ipcObjects();
    } else if (statusBuffer) {
        tr.flags |= TF_STATUS_CODE;
        *statusBuffer = err;
        tr.data_size = sizeof(status_t);
        tr.data.ptr.buffer = reinterpret_cast<uintptr_t>(statusBuffer);
        tr.offsets_size = 0;
        tr.data.ptr.offsets = 0;
    } else {
        return (mLastError = err);
    }

    mOut.writeInt32(cmd);
    mOut.write(&tr, sizeof(tr));

    return NO_ERROR;
}

对于binder_transaction_data在讲解Binder驱动的时候我们已经详细讲解过了。而这里的Parcel我们还不了解,那么接下来我们马上就来看一下这个类。

数据包装器:Parcel

Binder上提供的是跨进程的服务,每个服务包含了不同的接口,每个接口的参数数量和类型都不一样。那么当客户端想要调用服务端的接口,参数是如何跨进程传递给服务端的呢?除此之外,服务端想要给客户端返回结果,结果又是如何传递回来的呢?

这些问题的答案就是:Parcel。Parcel就像一个包装器,调用者可以以任意顺序往里面放入需要的数据,所有写入的数据就像是被打成一个整体的包,然后可以直接在Binde上传输。

Parcel提供了所有基本类型的写入和读出接口,下面是其中的一部分:

...
status_t            writeInt32(int32_t val);
status_t            writeUint32(uint32_t val);
......
status_t            readUtf8FromUtf16(std::string* str) const;
status_t            readUtf8FromUtf16(std::unique_ptr<std::string>* str) const;

const char*         readCString() const;
...

因此对于基本类型,开发者可以直接调用接口写入和读出。而对于非基本类型,需要由开发者将其拆分成基本类型然后写入到Parcel中(读出的时候也是一样)。 Parcel会将所有写入的数据进行打包,Parcel本身可以作为一个整体在进程间传递。接收方在收到Parcel之后,只要按写入同样的顺序读出即可。

这个过程,和我们现实生活中寄送包裹做法是一样的:我们将需要寄送的包裹放到硬纸盒中交给快递公司。快递公司将所有的包裹进行打包,然后集中放到运输车中送到目的地,到了目的地之后然后再进行拆分。

Parcel既包含C++部分的实现,也同时提供了Java的接口,中间通过JNI衔接。Java层的接口其实仅仅是一层包装,真正的实现都是位于C++部分中。 特别需要说明一下的是,Parcel类除了可以传递基本数据类型,还可以传递Binder对象:

status_t Parcel::writeStrongBinder(const sp<IBinder>& val)
{
    return flatten_binder(ProcessState::self(), val, this);
}

这个方法写入的是sp类型的对象,而IBinder既可能是本地Binder,也可能是远程Binder,这样我们就不可以不用关心具体细节直接进行Binder对象的传递。 这也是为什么IInterface中定义了两个asBinder的static方法

static sp<IBinder>  asBinder(const IInterface*);
static sp<IBinder>  asBinder(const sp<IInterface>&);

而对于Binder驱动,我们前面已经讲解过:Binder驱动并不是真的将对象在进程间序列化传递,而是由Binder驱动完成了对于Binder对象指针的解释和翻译,使调用者看起来就像在进程间传递对象一样。

2.4、Framework层的线程管理 在讲解Binder驱动的时候,我们就讲解过驱动中对应线程的管理。这里我们再来看看,Framework层是如何与驱动层对接进行线程管理的。

ProcessState::setThreadPoolMaxThreadCount 方法中,会通过BINDER_SET_MAX_THREADS命令设置进程支持的最大线程数量:

#define DEFAULT_MAX_BINDER_THREADS 15

status_t ProcessState::setThreadPoolMaxThreadCount(size_t maxThreads) {
    status_t result = NO_ERROR;
    if (ioctl(mDriverFD, BINDER_SET_MAX_THREADS, &maxThreads) != -1) {
        mMaxThreads = maxThreads;
    } else {
        result = -errno;
        ALOGE("Binder ioctl to set max threads failed: %s", strerror(-result));
    }
    return result;
}

由此驱动便知道了该Binder服务支持的最大线程数。驱动在运行过程中,会根据需要,并在没有超过上限的情况下,通过BR_SPAWN_LOOPER命令通知进程创建线程:

IPCThreadState在收到BR_SPAWN_LOOPER请求之后,便会调用ProcessState::spawnPooledThread来创建线程:

status_t IPCThreadState::executeCommand(int32_t cmd)
{
    ...
    case BR_SPAWN_LOOPER:
        mProcess->spawnPooledThread(false);
        break;
    ...
}

ProcessState::spawnPooledThread方法负责为线程设定名称并创建线程:

void ProcessState::spawnPooledThread(bool isMain)
{
    if (mThreadPoolStarted) {
        String8 name = makeBinderThreadName();
        ALOGV("Spawning new pooled thread, name=%s\n", name.string());
        sp<Thread> t = new PoolThread(isMain);
        t->run(name.string());
    }
}

线程在run之后,会调用threadLoop将自身添加的线程池中:

virtual bool threadLoop()
{
   IPCThreadState::self()->joinThreadPool(mIsMain);
   return false;
}

而IPCThreadState::joinThreadPool方法中,会根据当前线程是否是主线程发送BC_ENTER_LOOPER或者BC_REGISTER_LOOPER命令告知驱动线程已经创建完毕。整个调用流程如下图所示:

(3)、Android Binder系统-Native层添加hello服务

3.1、Client构造数据,发送数据给驱动

首先看一下Native ServiceManager架构图

只讲数据构造过程。。

构造: [-> IServiceManager.cpp ::BpServiceManager]

virtual status_t addService(const String16& name, const sp<IBinder>& service, bool allowIsolated) {
    Parcel data, reply; //Parcel是数据通信包
    //写入头信息"android.os.IServiceManager"
    data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());   
    data.writeString16(name);        // name为 "hello"
    data.writeStrongBinder(service); // HelloService对象,把一个binder实体“打扁”并写入parcel
    data.writeInt32(allowIsolated ? 1 : 0); // allowIsolated= false
    //remote()指向的是BpBinder对象
    status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);
    return err == NO_ERROR ? reply.readExceptionCode() : err;
}

服务注册过程:向ServiceManager注册服务hello Service,服务名为”hello”; 请大家注意上面data.writeStrongBinder()一句,它专门负责把一个binder实体”打扁”并写入parcel。其代码如下:

3.2.1、* writeStrongBinder()

[-> parcel.cpp]

status_t Parcel::writeStrongBinder(const sp<IBinder>& val)
{
    return flatten_binder(ProcessState::self(), val, this);
}

3.2.2、flatten_binder()

[-> parcel.cpp]

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(); //本地Binder不为空
        if (!local) {
            BpBinder *proxy = binder->remoteBinder();
            const int32_t handle = proxy ? proxy->handle() : 0;
            obj.type = BINDER_TYPE_HANDLE;
            obj.binder = 0;
            obj.handle = handle;
            obj.cookie = 0;
        } else { //进入该分支
            obj.type = BINDER_TYPE_BINDER;
            obj.binder = reinterpret_cast<uintptr_t>(local->getWeakRefs());
            obj.cookie = reinterpret_cast<uintptr_t>(local);
        }
    } else {
        ...
    }
    return finish_flatten_binder(binder, obj, out);
}

将Binder对象扁平化,转换成flat_binder_object对象。 看到了吗?”打扁”的意思就是把binder对象整理成flat_binder_object变量,如果打扁的是binder实体,那么flat_binder_object用cookie域记录binder实体的指针,即BBinder指针,而如果打扁的是binder代理,那么flat_binder_object用handle域记录的binder代理的句柄值。

总结:Parcel的数据区域分两个部分:mData和mObjects,所有的数据不管是基础数据类型还是对象实体,全都追加到mData里,mObjects是一个偏移量数组,记录所有存放在mData中的flat_binder_object实体的偏移量。

3.2.3、finish_flatten_binder()

将flat_binder_object写入out。

inline static status_t finish_flatten_binder(
    const sp<IBinder>& , const flat_binder_object& flat, Parcel* out)
{
    return out->writeObject(flat, false);
}

然后flatten_binder()调用了一个关键的finish_flatten_binder()函数。这个函数内部会记录下刚刚被扁平化的flat_binder_object在parcel中的位置。说得更详细点儿就是,parcel对象内部会有一个buffer,记录着parcel中所有扁平化的数据,有些扁平数据是普通数据,而另一些扁平数据则记录着binder对象。所以parcel中会构造另一个mObjects数组,专门记录那些binder扁平数据所在的位置,示意图如下:

一旦到了向驱动层传递数据的时候,IPCThreadState::writeTransactionData()会先把Parcel数据整理成一个binder_transaction_data数据

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.ptr = 0; /* Don't pass uninitialized stack data to a remote process */
    tr.target.handle = handle;
    tr.code = code;
    tr.flags = binderFlags;
    tr.cookie = 0;
    tr.sender_pid = 0;
    tr.sender_euid = 0;

    const status_t err = data.errorCheck();
    if (err == NO_ERROR) {
        tr.data_size = data.ipcDataSize();
        tr.data.ptr.buffer = data.ipcData();
        tr.offsets_size = data.ipcObjectsCount()*sizeof(binder_size_t);
        tr.data.ptr.offsets = data.ipcObjects();
    } else if (statusBuffer) {
        tr.flags |= TF_STATUS_CODE;
        *statusBuffer = err;
        tr.data_size = sizeof(status_t);
        tr.data.ptr.buffer = reinterpret_cast<uintptr_t>(statusBuffer);
        tr.offsets_size = 0;
        tr.data.ptr.offsets = 0;
    } else {
        return (mLastError = err);
    }

    mOut.writeInt32(cmd);
    mOut.write(&tr, sizeof(tr));

    return NO_ERROR;
}

3.2.4 、waitForResponse()

status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
    int32_t cmd;
    int32_t err;

    while (1) {
        if ((err=talkWithDriver()) < NO_ERROR) break;//目的就是把上面打包的mOut数据给kernel,接着看taklWithDriver();
        cmd = mIn.readInt32();
        }
        switch (cmd) {
        case BR_REPLY:
            ......
            goto finish;

        default:
            err = executeCommand(cmd);
            break;
        }
    }
finish:
    .....
    return err;
}

该函数是与serviceManager通信的主要函数,首先会调用talkWithDriver()方法,将之前的打包在mOut中的数据打包成struct binder_write_read 对象,并通过ioctrl发送给kernel。

3.2.5、 IPCThreadState::talkWithDriver

status_t IPCThreadState::talkWithDriver(bool doReceive)
{
    binder_write_read bwr;
    const bool needRead = mIn.dataPosition() >= mIn.dataSize();

    const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0;
    //doReceive参数,默认是为true,上面我们看到没有传参数,那么doReceive = 1;
    bwr.write_size = outAvail;
    bwr.write_buffer = (uintptr_t)mOut.data(); //将mOut数据指针存放到这里,这就是我们上面打包的数据。

    // This is what we'll read.
    if (doReceive && needRead) {
        bwr.read_size = mIn.dataCapacity(); //注意这里数据的大小,在我们new IPCThreadState对象时,已经初始化为256.
        bwr.read_buffer = (uintptr_t)mIn.data(); //mIn数据指针,放到这里
    } else {
        bwr.read_size = 0;
        bwr.read_buffer = 0;
    }
    //......
    bwr.write_consumed = 0;
    bwr.read_consumed = 0;
    status_t err;
    do {
        if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0) //这里通过ioctl将数据写给kernel
      .....
    } while (err == -EINTR);
    return err;
}

该函数的作用就是将之前打包的数据通过系统调用ioctl发送给kernel,最终发送给kernel的数据是struct binder_write_read对象。该对象已经被打包了3次,它们的包含关系如下所示。

3.2.6、Client获取服务、处理回复数据过程

内核会唤醒Client进程处理回复消息。

status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
    int32_t cmd;
    int32_t err;
    while (1) {
        if ((err=talkWithDriver()) < NO_ERROR) break;
        ...
        cmd = mIn.readInt32();
        switch (cmd) {
          case BR_REPLY:
          {
            binder_transaction_data tr;
            err = mIn.read(&tr, sizeof(tr));
            if (reply) {
                if ((tr.flags & TF_STATUS_CODE) == 0) {
                    //当reply对象回收时,则会调用freeBuffer来回收内存
                    reply->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, this);
                }
          ...     
    }
    ...
    return err;
}

3.2.7、Parcel::ipcSetDataReference

void Parcel::ipcSetDataReference(const uint8_t* data, size_t dataSize,
    const binder_size_t* objects, size_t objectsCount, release_func relFunc, void* relCookie)
{
    binder_size_t minOffset = 0;
    freeDataNoInit();
    mError = NO_ERROR;
    mData = const_cast<uint8_t*>(data); //这是有4个字节的buffer。且存放的数据是0
    mDataSize = mDataCapacity = dataSize; //之前申请的大小就是4个字节。
    //ALOGI("setDataReference Setting data size of %p to %lu (pid=%d)", this, mDataSize, getpid());
    mDataPos = 0;
    ALOGV("setDataReference Setting data pos of %p to %zu", this, mDataPos);
    mObjects = const_cast<binder_size_t*>(objects); //binder对象其实地址
    mObjectsSize = mObjectsCapacity = objectsCount; //binder对象的个数。
    mNextObjectHint = 0;
    mOwner = relFunc; //释放内存的函数,后面我们就不进行了。
    mOwnerCookie = relCookie;
    for (size_t i = 0; i < mObjectsSize; i++) {
        binder_size_t offset = mObjects[i];
        if (offset < minOffset) {
            ALOGE("%s: bad object offset %"PRIu64" < %"PRIu64"\n",
                  __func__, (uint64_t)offset, (uint64_t)minOffset);
            mObjectsSize = 0;
            break;
        }
        minOffset = offset + sizeof(flat_binder_object);
    }
    scanForFds();
}

上面做的工作只是将事务数据分别安放到当前Parcel对象的相应位置。其中scanForFds()是为了查找返回来的数据中是否有binder对象,这个在获取代理对象时有用。

3.2.8、readStrongBinder() [-> Parcel.java]

readStrongBinder的过程基本是writeStrongBinder逆过程。

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 将native层BpBinder对象转换为Java层BinderProxy对象。

3.2.9、readStrongBinder(C++)

[-> Parcel.cpp]

sp<IBinder> Parcel::readStrongBinder() const
{
    sp<IBinder> val;
    unflatten_binder(ProcessState::self(), *this, &val);
    return val;
}

3.2.10、unflatten_binder()

[-> Parcel.cpp]

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);
                //创建BpBinder对象
                return finish_unflatten_binder(
                    static_cast<BpBinder*>(out->get()), *flat, in);
        }
    }
    return BAD_TYPE;
}

说明:readObject()的作用是从Parcel中读取出它所保存的flat_binder_object类型的对象。该对象的类型是BINDER_TYPE_HANDLE,因此会指向BINDER_TYPE_HANDLE对应的switch分支。 (01) 这里的proc是ProcessState对象,执行proc->getStrongProxyForHandle()会将句柄(MediaPlayerService的Binder引用描述)保存到ProcessState的链表中,然后再创建并返回该句柄的BpBinder对象(即Binder的代理)。在Android Binder机制(四) defaultServiceManager()的实现中有getStrongProxyForHandle()的详细说明,下面只给出getStrongProxyForHandle()代码。 (02) finish_unflatten_binder()中只有return NO_ERROR。

3.2.11、getStrongProxyForHandle()

[-> ProcessState.cpp]

sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{
    sp<IBinder> result;

    AutoMutex _l(mLock);
    //查找handle对应的资源项
    handle_entry* e = lookupHandleLocked(handle);

    if (e != NULL) {
        IBinder* b = e->binder;
        if (b == NULL || !e->refs->attemptIncWeak(this)) {
            ...
            //当handle值所对应的IBinder不存在或弱引用无效时,则创建BpBinder对象
            b = new BpBinder(handle);
            e->binder = b;
            if (b) e->refs = b->getWeakRefs();
            result = b;
        } else {
            result.force_set(b);
            e->refs->decWeak(this);
        }
    }
    return result;
}

经过该方法,最终创建了指向Binder服务端的BpBinder代理对象。

(4)、Android Binder系统-Native层获取hello服务

经过前面的分析,知道流程基本类似,这里不再继续分析获取hello服务

五、Android Binder系统-Framwork-Java层

(1)、Android Binder系统Java层

主要结构 Android应用程序使用Java语言开发,Binder框架自然也少不了在Java层提供接口。

前文中我们看到,Binder机制在C++层已经有了完整的实现。因此Java层完全不用重复实现,而是通过JNI衔接了C++层以复用其实现。

下图描述了Binder Framework Java层到C++层的衔接关系。

这里对图中Java层和JNI层的几个类做一下说明( 关于C++层的讲解请看这里 ):

这里的IInterface,IBinder和C++层的两个类是同名的。这个同名并不是巧合:它们不仅仅同名,它们所起的作用,以及其中包含的接口都是几乎一样的,区别仅仅在于一个是C++层,一个是Java层而已。

除了IInterface,IBinder之外,这里Binder与BinderProxy类也是与C++的类对应的,下面列出了Java层和C++层类的对应关系:

除了IInterface,IBinder之外,这里Binder与BinderProxy类也是与C++的类对应的,下面列出了Java层和C++层类的对应关系:

(2)、JNI的衔接 JNI全称是Java Native Interface,这个是由Java虚拟机提供的机制。这个机制使得native代码可以和Java代码互相通讯。简单来说就是:我们可以在C/C++端调用Java代码,也可以在Java端调用C/C++代码。

关于JNI的详细说明,可以参见Oracle的官方文档:Java Native Interface ,这里不多说明。

实际上,在Android中很多的服务或者机制都是在C/C++层实现的,想要将这些实现复用到Java层,就必须通过JNI进行衔接。AOSP源码中,/frameworks/base/core/jni/ 目录下的源码就是专门用来对接Framework层的JNI实现的。

看一下Binder.java的实现就会发现,这里面有不少的方法都是用native关键字修饰的,并且没有方法实现体,这些方法其实都是在C++中android_util_Binder.cpp实现的: 那么,那么,C++是如何调用Java的呢?最关键的,libbinder中的BBinder::onTransact是如何能够调用到Java中的Binder::onTransact的呢?

这段逻辑就是android_util_Binder.cpp中JavaBBinder::onTransact中处理的了。JavaBBinder是BBinder子类,其类结构如下:libbinder中的BBinder::onTransact是如何能够调用到Java中的Binder::onTransact的呢? JavaBBinder::onTransact关键代码如下:

virtual status_t onTransact(
   uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0)
{
   JNIEnv* env = javavm_to_jnienv(mVM);

   IPCThreadState* thread_state = IPCThreadState::self();
   const int32_t strict_policy_before = thread_state->getStrictModePolicy();

   jboolean res = env->CallBooleanMethod(mObject, gBinderOffsets.mExecTransact,
       code, reinterpret_cast<jlong>(&data), reinterpret_cast<jlong>(reply), flags);
   ...
}

请注意这段代码中的这一行:

jboolean res = env->CallBooleanMethod(mObject, gBinderOffsets.mExecTransact,
  code, reinterpret_cast<jlong>(&data), reinterpret_cast<jlong>(reply), flags);

这一行代码其实是在调用mObject上offset为mExecTransact的方法。这里的几个参数说明如下:

mObject 指向了Java端的Binder对象 gBinderOffsets.mExecTransact 指向了Binder类的execTransact方法 data 调用execTransact方法的参数 code, data, reply, flags都是传递给调用方法execTransact的参数 而JNIEnv.CallBooleanMethod这个方法是由虚拟机实现的。即:虚拟机会提供native方法来调用一个Java Object上的方法(关于Android上的Java虚拟机,今后我们会专门讲解)。

这样,就在C++层的JavaBBinder::onTransact中调用了Java层Binder::execTransact方法。而在Binder::execTransact方法中,又调用了自身的onTransact方法,由此保证整个过程串联了起来:

private boolean execTransact(int code, long dataObj, long replyObj,
       int flags) {
   Parcel data = Parcel.obtain(dataObj);
   Parcel reply = Parcel.obtain(replyObj);
   boolean res;
   try {
       res = onTransact(code, data, reply, flags);
   } catch (RemoteException|RuntimeException e) {
       if (LOG_RUNTIME_EXCEPTION) {
           Log.w(TAG, "Caught a RuntimeException from the binder stub implementation.", e);
       }
       if ((flags & FLAG_ONEWAY) != 0) {
           if (e instanceof RemoteException) {
               Log.w(TAG, "Binder call failed.", e);
           } else {
               Log.w(TAG, "Caught a RuntimeException from the binder stub implementation.", e);
           }
       } else {
           reply.setDataPosition(0);
           reply.writeException(e);
       }
       res = true;
   } catch (OutOfMemoryError e) {
       RuntimeException re = new RuntimeException("Out of memory", e);
       reply.setDataPosition(0);
       reply.writeException(re);
       res = true;
   }
   checkParcel(this, code, reply, "Unreasonably large binder reply buffer");
   reply.recycle();
   data.recycle();

   StrictMode.clearGatheredViolations();

   return res;
}

(3)、Java层的ServiceManager

通过这个类图我们看到,Java层的ServiceManager和C++层的接口是一样的。

然后我们再选取addService方法看一下实现:

public static void addService(String name, IBinder service, boolean allowIsolated) {
   try {
       getIServiceManager().addService(name, service, allowIsolated);
   } catch (RemoteException e) {
       Log.e(TAG, "error in addService", e);
   }
}

   private static IServiceManager getIServiceManager() {
   if (sServiceManager != null) {
       return sServiceManager;
   }

   // Find the service manager
   sServiceManager = ServiceManagerNative.asInterface(BinderInternal.getContextObject());
   return sServiceManager;
}

很显然,这段代码中,最关键就是下面这个调用:

ServiceManagerNative.asInterface(BinderInternal.getContextObject());

然后我们需要再看一下BinderInternal.getContextObject()和ServiceManagerNative.asInterface两个方法。

BinderInternal.getContextObject()是一个JNI方法,其实现代码在android_util_Binder.cpp中:

static jobject android_os_BinderInternal_getContextObject(JNIEnv* env, jobject clazz)
{
    sp<IBinder> b = ProcessState::self()->getContextObject(NULL);
    return javaObjectForIBinder(env, b);
}

而ServiceManagerNative.asInterface的实现和其他的Binder服务是一样的套路:

static public IServiceManager asInterface(IBinder obj)
{
   if (obj == null) {
       return null;
   }
   IServiceManager in =
       (IServiceManager)obj.queryLocalInterface(descriptor);
   if (in != null) {
       return in;
   }

   return new ServiceManagerProxy(obj);
}

先通过queryLocalInterface查看能不能获得本地Binder,如果无法获取,则创建并返回ServiceManagerProxy对象。

而ServiceManagerProxy自然也是和其他Binder Proxy一样的实现套路:

public void addService(String name, IBinder service, boolean allowIsolated)
       throws RemoteException {
   Parcel data = Parcel.obtain();
   Parcel reply = Parcel.obtain();
   data.writeInterfaceToken(IServiceManager.descriptor);
   data.writeString(name);
   data.writeStrongBinder(service);
   data.writeInt(allowIsolated ? 1 : 0);
   mRemote.transact(ADD_SERVICE_TRANSACTION, data, reply, 0);
   reply.recycle();
   data.recycle();
}

六、Android Binder系统-AIDL

开发一个基于AIDL的Service需要三个步骤:

定义一个.aidl文件 实现接口 暴露接口给客户端使用 aidl文件使用Java语言的语法来定义,每个.aidl文件只能包含一个interface,并且要包含interface的所有方法声明。

默认情况下,AIDL支持的数据类型包括:

基本数据类型(即int,long,char,boolean等) String CharSequence List(List的元素类型必须是AIDL支持的) Map(Map中的元素必须是AIDL支持的) 对于AIDL中的接口,可以包含0个或多个参数,可以返回void或一个值。所有非基本类型的参数必须包含一个描述是数据流向的标签,可能的取值是:in,out或者inout。

下面是一个aidl文件的示例:

// IRemoteService.aidl
package com.example.android;

// Declare any non-default types here with import statements

/** Example service interface */
interface IRemoteService {
    /** Request the process ID of this service, to do evil things with it. */
    int getPid();

    /** Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);
}

这个文件中包含了两个接口 :

getPid 一个无参的接口,返回值类型为int basicTypes,包含了几个基本类型作为参数的接口,无返回值 对于包含.aidl文件的工程,Android IDE(以前是Eclipse,现在是Android Studio)在编译项目的时候,会为aidl文件生成对应的Java文件。

针对上面这个aidl文件生成的java文件中包含的结构如下图所示:

在这个生成的Java文件中,包括了:

一个名称为IRemoteService的interface,该interface继承自android.os.IInterface并且包含了我们在aidl文件中声明的接口方法 IRemoteService中包含了一个名称为Stub的静态内部类,这个类是一个抽象类,它继承自android.os.Binder并且实现了IRemoteService接口。这个类中包含了一个onTransact方法 Stub内部又包含了一个名称为Proxy的静态内部类,Proxy类同样实现了IRemoteService接口 仔细看一下Stub类和Proxy两个中包含的方法,是不是觉得很熟悉?是的,这里和前面介绍的服务实现是一样的模式。这里我们列一下各层类的对应关系:

为了整个结构的完整性,最后我们还是来看一下生成的Stub和Proxy类中的实现逻辑。

Stub是提供给开发者实现业务的父类,而Proxy的实现了对外提供的接口。Stub和Proxy两个类都有一个asBinder的方法。

Stub类中的asBinder实现就是返回自身对象:

@Override
public android.os.IBinder asBinder() {
    return this;
}

而Proxy中asBinder的实现是返回构造函数中获取的mRemote对象,相关代码如下:

private android.os.IBinder mRemote;

Proxy(android.os.IBinder remote) {
    mRemote = remote;
}

@Override
public android.os.IBinder asBinder() {
    return mRemote;
}

而这里的mRemote对象其实就是远程服务在当前进程的标识。

上文我们说了,Stub类是用来提供给开发者实现业务逻辑的父类,开发者者继承自Stub然后完成自己的业务逻辑实现,例如这样:

private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
   public int getPid(){
       return Process.myPid();
   }
   public void basicTypes(int anInt, long aLong, boolean aBoolean,
       float aFloat, double aDouble, String aString) {
       // Does something
   }
};

而这个Proxy类,就是用来给调用者使用的对外接口。我们可以看一下Proxy中的接口到底是如何实现的:

Proxy中getPid方法实现如下所示:

@Override
public int getPid() 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);
        mRemote.transact(Stub.TRANSACTION_getPid, _data, _reply, 0);
        _reply.readException();
        _result = _reply.readInt();
    } finally {
        _reply.recycle();
        _data.recycle();
    }
    return _result;
}

这里就是通过Parcel对象以及transact调用对应远程服务的接口。而在Stub类中,生成的onTransact方法对应的处理了这里的请求:

@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)
        throws android.os.RemoteException {
    switch (code) {
    case INTERFACE_TRANSACTION: {
        reply.writeString(DESCRIPTOR);
        return true;
    }
    case TRANSACTION_getPid: {
        data.enforceInterface(DESCRIPTOR);
        int _result = this.getPid();
        reply.writeNoException();
        reply.writeInt(_result);
        return true;
    }
    case TRANSACTION_basicTypes: {
        data.enforceInterface(DESCRIPTOR);
        int _arg0;
        _arg0 = data.readInt();
        long _arg1;
        _arg1 = data.readLong();
        boolean _arg2;
        _arg2 = (0 != data.readInt());
        float _arg3;
        _arg3 = data.readFloat();
        double _arg4;
        _arg4 = data.readDouble();
        java.lang.String _arg5;
        _arg5 = data.readString();
        this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
        reply.writeNoException();
        return true;
    }
    }
    return super.onTransact(code, data, reply, flags);
}

onTransact()所要做的就是:

根据code区分请求的是哪个接口 通过data来获取请求的参数 调用由子类实现的抽象方法了。

完整框架: