Binder死磕到底(二):进程间通信库libbinder.so-程序员宅基地

技术标签: Binder  IPCThreadState  android  BBinder/BpBinder  

目录

一、Binder框架库

1、BBinder vs BpBinder

1)、通用接口IBinder

2)、本地对象BBinder

3)、代理对象BpBinder

4)、总结

2、IPCThreadState

1)、与BpBinder交互

2)、与BBinder交互

3)、binder驱动处理BC_TRANSACTION消息

3、BnInterface vs BpInterface

1)、BnInterface

2)、BpInterface

4、IInterface vs INTERFACE

1)、IServiceManager的定义

2)、IPowerManager的定义

3)、IMediaPlayer的定义

5、Service组件 vs Client组件

1)、Client组件

2)、Service组件

二、service manager的客户端进程

1、业务接口IServiceManager

2、defaultServiceManager

1)、获取进程实例对象ProcessState

2)、创建进程间通信代理对象BpBinder

3)、转换代理对象为BpServiceManager

4)、总结

三、Binder通信服务端进程

1、进程mediaserver

2、服务注册

3、实现具体业务

四、Binder通信客户端进程

1、服务查询

2、自定义媒体播放进程

3、匿名Service

1)、IMediaPlayer的定义

2)、IMediaPlayer的创建

3)、BnMediaPlayer的创建

4)、BpMediaPlayer的生成

五、自定义Binder进程间通信示例

1、Binder库重要组件的定义

2、服务端进程实现

3、客户端进程实现


在第一篇中介绍了Android系统中进程间通信的基本原理,后在此基础上介绍了Android系统中进程间通信的专用驱动Binder,最后介绍了一个很重要的用户进程service manager。

其中service manager的逻辑其实算是比较复杂的,它手动打开/dev/binder设备节点,轮询向驱动程序读写数据。如果每个用户进程想要进行跨进程通信都使用这样的方式,是不是太过复杂了,能不能将公用的逻辑流程进行封装,提供一套通用机制,用户进程只要按照这样的机制编写代码就行了呢?答案是有的,libbinder.so动态库就是来干这件事情的,为了后面的方便,我们把这个动态库统称为Binder框架库。

一、Binder框架库

在第一篇介绍service manager的时候初识了I##INTERFACE、Bp##INTERFACE、BpBinder等对象,其实这些都是Android系统在应用程序框架层中将各种Binder驱动程序操作封装成一个Binder库,这样我们就可以很方便的调用Binder库提供的接口来实现进程间通信。Binder库并不是想象中那么简单,恰恰相反很庞大。如下:

在介绍庞大的Binder框架库之前,先梳理整个家族中几个元老级别人物:BpBinder、BBinder、IPCThreadState。

1、BBinder vs BpBinder

Binder库为整个Android系统提供了一个良好的进程间通信的生态环境。当然离不开前面多次提到的IPC思想,即两个进程之间的通信过程必然一个作为服务端进程,另一个作为客户端进程,因此引申了Service组件和Client组件。要利用Binder驱动程序为我们服务,那么必然需要实现类似Binder驱动程序中的一个本地对象(BBinder)和一个代理对象(BpBinder)。如下:

  • BBinder为进程间通信中的服务端提供了接口,即BBinder为系统提供服务的执行者,因此通常被称为Binder本地对象
  • BpBinder为进程间通信中的客户端提供接口,即可以通过BpBinder向本地对象发送命令让其执行对应指令。因此BpBinder并不是真正的执行者,代理本地对象完成了该项操作,所以通常被称为Binder代理对象

1)、通用接口IBinder

既然BBinder作为真正的执行者,BpBinder只是一个代理人,那么熟悉Java代理设计模式的同学就应该知道将还有一个接口类来约束他们的行为,IBinder接口就干了这样的事情。IBinder没有实现只有定义,如下:

//frameworks/native/include/binder/IBinder.h
namespace android {
class BBinder;
class BpBinder;
class IInterface;
class Parcel;
class IBinder : public virtual RefBase
{
public:
    IBinder();
    //服务描述 string16格式的字符串(例如"media.play"提供媒体播放服务)
    virtual const String16& getInterfaceDescriptor() const = 0;
    virtual bool            isBinderAlive() const = 0;
    //约束数据通信方式
    virtual status_t        transact(   uint32_t code,
                                        const Parcel& data,
                                        Parcel* reply,
                                        uint32_t flags = 0) = 0;
    //获取该服务描述的本地对象
    virtual BBinder*        localBinder();
    //获取该服务描述的代理对象
    virtual BpBinder*       remoteBinder();
protected:
    virtual          ~IBinder();
}
}

2)、本地对象BBinder

IBinder作为一个接口只对数据通信作了约束,即BBinder和BpBinder都要按照这种方式来进行通信。当一个Binder代理对象通过Binder驱动程序向一个Binder本地对象发出一个进程间通信请求时,Binder驱动程序就会调用该Binder本地对象的transact成员函数来处理请求,BBinder在transact函数中将绝大多数任务交给了onTransact函数来处理。实际上该函数作为业务相关的处理进程间通信请求,通常子类会重写onTransact函数来体现不同子类服务的多态性。代码如下:

//frameworks/native/include/binder/Binder.h
class BBinder : public IBinder
{
public:
                        BBinder();
    virtual status_t    transact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0);
    virtual BBinder*    localBinder();
protected:
    virtual             ~BBinder();
    virtual status_t    onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0);
}
//frameworks/native/libs/binder/Binder.cpp
//返回本地对象
BBinder* BBinder::localBinder() {
    return this;
}
//当binder驱动程序接收到代理对象通过IPCThreadState发送的命令的时候
//binder驱动程序将根据句柄找到正确的Binder本地对象实例且回调它的transact函数
status_t BBinder::transact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
    data.setDataPosition(0);
    status_t err = NO_ERROR;
    switch (code) {
        case PING_TRANSACTION:
            reply->writeInt32(pingBinder());
            break;
        default:
            //除ping命令,全部转发给onTransact处理
            err = onTransact(code, data, reply, flags);
            break;
    }
    if (reply != NULL) reply->setDataPosition(0);
    return err;
}
//具体的服务类将重写onTransact方法 且作为执行者根据不同的命令实现相应的功能
status_t BBinder::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t /*flags*/) {
    switch (code) {
        case INTERFACE_TRANSACTION:
            reply->writeString16(getInterfaceDescriptor());
            return NO_ERROR;
        case DUMP_TRANSACTION:
            //..添加参数...
            return dump(fd, args);
        case SHELL_COMMAND_TRANSACTION:
            int in = data.readFileDescriptor();
            int out = data.readFileDescriptor();
            int err = data.readFileDescriptor();
            int argc = data.readInt32();
            Vector<String16> args;
            for (int i = 0; i < argc && data.dataAvail() > 0; i++) {
               args.add(data.readString16());
            }
            sp<IShellCallback> shellCallback = IShellCallback::asInterface(
                    data.readStrongBinder());
            sp<IResultReceiver> resultReceiver = IResultReceiver::asInterface(
                    data.readStrongBinder());
            (void)in;
            (void)out;
            (void)err;
            // BBinder将指令透传给接收器,其派生类将具体实现接收器resultReceiver
            // 暂时理解BBinder具体执行移交给子类的onTransact方法
            if (resultReceiver != NULL) resultReceiver->send(INVALID_OPERATION);
            return NO_ERROR;
        case SYSPROPS_TRANSACTION: {
            report_sysprop_change();
            return NO_ERROR;
        }
        default: return UNKNOWN_TRANSACTION;
    }
}

3)、代理对象BpBinder

BpBinder代表Binder进程间通信的客户端代表(位于客户端进程),只有通过binder驱动程序向服务端进程的BBinder发起请求,让其完成自己想干的时候并且把结果送回来。为了能够标志哪一个Service组件能够为自己提供服务,因此BpBinder特地增加了一个句柄handle来区别他们的对应关系,代码如下:

//frameworks/native/include/binder/BpBinder.h
class BpBinder : public IBinder
{
public:
    static BpBinder*    create(int32_t handle);
    inline  int32_t     handle() const { return mHandle; }
    virtual status_t    transact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0);
    virtual BpBinder*   remoteBinder();
protected:
                        BpBinder(int32_t handle,int32_t trackedUid);
    virtual             ~BpBinder();
private:
    const   int32_t             mHandle;
}
//frameworks/native/libs/binder/BpBinder.cpp
//提供了一种安全方式创建BpBinder代理对象
//每一个BpBinder代理对象都必须有一个对应的BBinder
//因此使用handler句柄来作为他们对应关系
//handler值为0的服务就是BpServiceManager
//不同的组件其handle值不一样,BBinder与BpBinder根据该值建立关系
BpBinder* BpBinder::create(int32_t handle) {
    int32_t trackedUid = -1;
    if (sCountByUidEnabled) {
        trackedUid = IPCThreadState::self()->getCallingUid();
        //...引用计数...
    }
    return new BpBinder(handle, trackedUid);
}
BpBinder* BpBinder::remoteBinder() {
    return this;
}
//BpBinder不允许随便创建
//通常使用静态方法BpBinder::create传递句柄值来创建
BpBinder::BpBinder(int32_t handle, int32_t trackedUid)
    : mHandle(handle)
    , mTrackedUid(trackedUid) {
    IPCThreadState::self()->incWeakHandle(handle, this);
}
//BpBinder为客户端进程提供的接口
//客户端进程可通过函数向服务端发送请求执行命令
//例如可向提供"media.play"的服务发送START和STOP等命令来实现媒体播放器视频播放和结束功能
status_t BpBinder::transact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
    if (mAlive) {
        //BpBinder其实也是个骗子,就连发送命令的操作都需要让IPCThreadState来帮忙
        status_t status = IPCThreadState::self()->transact(mHandle, code, data, reply, flags);
        if (status == DEAD_OBJECT) mAlive = 0;
        return status;
    }
    return DEAD_OBJECT;
}

4)、总结

这里很好的诠释了代理设计模式。举个例子,现在需要实现一个可以拍照的应用程序,该应用程序作为客户端进程,Android系统已经提供了一个可以拍照的Service组件("media.camera"),该组件运行在MediaServer进程中。这个客户端进程拍照需要经过哪些艰辛历程呢:

  • 那客户端进程通过defaultServiceManager得到service manager进程的代理对象IServiceManager
  • 通过代理对象IServiceManager.getService("media.camera")得到MediaServer进程注册的句柄handle
  • 通过上面获取到的句柄创建media.camera组件的代理对象BpBinder
  • 通过代理对象BpBinder向media.camera组件的本地对象发送请求,其中代理对象位于这个应用程序进程里面,本地对象位于MediaServer进程中,那么BpBinder代理对象通过transact函数获取到当前进程实例并向binder驱动程序发送请求
  • binder驱动程序收到请求之后根据句柄值找到对应的BBinder本地对象并回调它的transact函数
  • MediaServer进程中的BBinder本地对象将任务分发给onTransact函数,其派生类具体实现解析命令并处理命令

2、IPCThreadState

由第一小节已经知道了代表Binder服务端的本地对象BBinder,和代表Binder客户端的代理对象BpBinder,但是他们具体如何进行通信我们却不得而知,不过已经有迹可循BpBinder通过IPCThreadState来完成。IPCThreadState跟前面讲解的能够代表进程实例对象ProcessState一样,个人认为从进程的维度上ProcessState唯一实例对象初始化打开Binder驱动程序,从线程的维度上唯一实例对象IPCThreadState实现了与Binder驱动程序的通信(包括数据和命令)。ProcessState和IPCThreadState合作分工,ProcessState负责打开binder驱动程序文件描述符已经进行内存映射等初始化工作,IPCThreadState负责向binder驱动程序发送数据/命令或者接收来自binder驱动程序返回的数据/命令IPCThreadState代码如下:

//frameworks/native/include/binder/IPCThreadState.h
class IPCThreadState
{
public:
    //单例模式 保证进程唯一
    static  IPCThreadState*     self();
    //内部持有了ProcessState对象
            sp<ProcessState>    process();
            void                joinThreadPool(bool isMain = true);
    //BpBinder调用该方法向BBinder发送数据
            status_t            transact(int32_t handle, uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags);
private:
                                IPCThreadState();
                                ~IPCThreadState();
            status_t            sendReply(const Parcel& reply, uint32_t flags);
            status_t            waitForResponse(Parcel *reply, status_t *acquireResult=NULL);
            status_t            talkWithDriver(bool doReceive=true);
            status_t            writeTransactionData(int32_t cmd, uint32_t binderFlags, int32_t handle, uint32_t code, const Parcel& data, status_t* statusBuffer);
            status_t            getAndExecuteCommand();
            status_t            executeCommand(int32_t command);
    //进程实例对象 通过它创建线程实例唯一
    const   sp<ProcessState>    mProcess;
    //mIn和mOut仅仅作了数据缓存,不涉及任何系统调用
            Parcel              mIn;
            Parcel              mOut;
}
//frameworks/native/libs/binder/IPCThreadState.cpp
IPCThreadState* IPCThreadState::self()
{
    if (gHaveTLS) {
restart:
        const pthread_key_t k = gTLS;
        IPCThreadState* st = (IPCThreadState*)pthread_getspecific(k);
        if (st) return st;
        return new IPCThreadState;
    }
    //...唯一标志判断...
    goto restart;
}
IPCThreadState::IPCThreadState()
    //初始化mProcess 建立IPCThreadState与ProcessState之间关系
    //ProcessState::self()会打开binder驱动程序和内核空间映射等操作
    : mProcess(ProcessState::self())
{
    pthread_setspecific(gTLS, this);
    clearCaller();
    //mIn和mOut用于与binder驱动程序进行通信的缓冲区
    mIn.setDataCapacity(256);    //binder驱动程序返回的数据
    mOut.setDataCapacity(256);   //binder驱动程序请求的数据(发送给binder驱动程序)
}
IPCThreadState::~IPCThreadState()
{
}

1)、与BpBinder交互

BpBinder利用IPCThreadState向BBinder发送命令,理所当然IPCThreadState将透传命令给Binder驱动程序,Binder驱动程序最终通过IPC机制传递给了BBinder。IPCThreadState的transact函数如下:

//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;
    flags |= TF_ACCEPT_FDS;
    //将发送给BBinder的数据保存到mOut缓冲区(talkWithDriver函数将mOut的数据发送给Binder驱动程序)
    err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
    if ((flags & TF_ONE_WAY) == 0) {
        if (reply) {
            //等待BBinder的回应并将返回数据保存在reply
            err = waitForResponse(reply);
        } else {
            Parcel fakeReply;
            err = waitForResponse(&fakeReply);
        }
    } else  err = waitForResponse(NULL, NULL);
    return err;
}
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
    mOut.writeInt32(cmd);
    mOut.write(&tr, sizeof(tr));
    return NO_ERROR;
}
status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
    uint32_t cmd;
    int32_t err;
    while (1) {
        //talkWithDriver函数真实实现了与binder驱动程序的交互
        //talkWithDriver将执行系统调用ioctl
        //talkWithDriver从mOut中获取数据并将通过ioctl发送给binder驱动程序
        //talkWithDriver将ioctl返回的数据保存在mIn
        if ((err=talkWithDriver()) < NO_ERROR) break;
        //检测mIn数据是否正确
        err = mIn.errorCheck();
        if (err < NO_ERROR) break;
        if (mIn.dataAvail() == 0) continue;
        //从mIn中获取binder驱动程序返回来的命令
        cmd = (uint32_t)mIn.readInt32();
        switch (cmd) {
        case BR_TRANSACTION_COMPLETE:  //....
        case BR_DEAD_REPLY:            //....
        case BR_FAILED_REPLY:          //....
        case BR_ACQUIRE_RESULT:        //....
        case BR_REPLY:                //存储bwr中的reply返回数据
            {
                binder_transaction_data tr;
                //读取mIn数据(binder驱动程序返回的数据)
                err = mIn.read(&tr, sizeof(tr));
                ALOG_ASSERT(err == NO_ERROR, "Not enough command data for brREPLY");
                if (err != NO_ERROR) goto finish;
                if (reply) {
                    if ((tr.flags & TF_STATUS_CODE) == 0) {
                        //将返回数据存储到reply指向的地址
                        //BpBinder传递的是指针,因此BpBinder通过reply作为返回数据
                        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);
                    } else {
                        err = *reinterpret_cast<const status_t*>(tr.data.ptr.buffer);
                        freeBuffer(NULL, 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), this);
                    }
                }
            }
            goto finish;
        //执行其他命令
        default:
            err = executeCommand(cmd);
            if (err != NO_ERROR) goto finish;
            break;
        }
    }
finish:
    if (err != NO_ERROR) { //...错误处理... }
    return err;
}
status_t IPCThreadState::talkWithDriver(bool doReceive)
{
    //判断mProcess是否正确打开binder驱动程序的文件描述符
    if (mProcess->mDriverFD <= 0) return -EBADF;
    //与binder驱动程序进行数据通信的缓冲区bwr
    binder_write_read bwr;
    //bwr的write数据缓冲区指定为mOut
    bwr.write_buffer = (uintptr_t)mOut.data();
    //bwr的read数据缓冲区指定为mIn
    if (doReceive && needRead) {
        bwr.read_size = mIn.dataCapacity();
        bwr.read_buffer = (uintptr_t)mIn.data();
    } else {
        bwr.read_size = 0;
        bwr.read_buffer = 0;
    }
    do {
        //系统调用binder驱动程序的ioctl IPCThreadState最核心所在
        if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0);
    } while (err == -EINTR);
    if (err >= NO_ERROR) {
        //与binder驱动程序进行ioctl交互完毕后清除mOut缓冲区
        if (bwr.write_consumed > 0) {
            if (bwr.write_consumed < mOut.dataSize())
                mOut.remove(0, bwr.write_consumed);
            else {
                mOut.setDataSize(0);
                processPostWriteDerefs();
            }
        }
        //与binder驱动程序进行ioctl交互完毕读取返回数据并设置到mIn
        if (bwr.read_consumed > 0) {
            mIn.setDataSize(bwr.read_consumed);
            mIn.setDataPosition(0);
        }
        return NO_ERROR;
    }
    return err;
}

BpBinder通过IPCThreadState::self()->transact函数向BBinder发送命令/数据,如上代码只诠释了该流程的一半,如下:

  • IPCThreadState将BpBinder传递的数据存储在mOut缓冲区(writeTransactionData函数实现)
  • IPCThreadState进行系统调用binder驱动程序的ioctl,以mOut为请求数据,并将返回数据存储在mIn(talkWithDriver函数实现)
  • IPCThreadState从mIn获取数据并进行解析,其中BR_REPLY命令为该次请求的返回值命令,在收到BR_REPLY命令的时候即将其保存在BpBinder参数传递过来的指针里面(waitForResponse函数实现)

       BpBinder ------> IPCThreadState::self()

                       ------> transact -------> writeTransactionData

                                               -------> waitForResponse --------> talkWithDriver -------> ioctl(向binder驱动文件描述符控制操作)

2)、与BBinder交互

前面讲解的是IPCThreadState所在进程作为客户端进程,BpBinder代理对象获取当前所在进程的单例对象IPCThreadState,并通过它的transact函数向BBinder本地对象发送命令。IPCThreadState并不能直接与对应的BBinder本地对象进行通信(此时的IPCThreadState属于客户端进程,BBinder位于服务端进程),因此需要通过binder驱动程序与BBinder进行交互。

BBinder本地对象所在的服务端进程也运行在android系统的后台,同样它也拥有一个单例对象IPCThreadState(注意与上面的不是同一个)。服务端进程通常会在main函数中执行

ProcessState:self()->startThreadPool()
IPCThreadState:self()->joinThreadPool()

启动binder线程池,并接收来自binder驱动程序发送过来的数据/命令。ProcessState:self()->startThreadPool()如下:

//frameworks/native/libs/binder/ProcessState.cpp
void ProcessState::startThreadPool() {
    AutoMutex _l(mLock);
    if (!mThreadPoolStarted) {
        mThreadPoolStarted = true;
        spawnPooledThread(true);
    }
}
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());
    }
}
class PoolThread : public Thread {
protected:
    virtual bool threadLoop(){
        IPCThreadState::self()->joinThreadPool(mIsMain);
        return false;
    }
    const bool mIsMain;
};

上面代码创建了线程池PoolThread,该线程池核心代码最终还是回到了IPCThreadState的joinThreadPool函数,如下代码:

//frameworks/native/libs/binder/IPCThreadState.cpp
void IPCThreadState::joinThreadPool(bool isMain)
{
    //向mOut缓冲区写入BC_ENTER_LOOPER命令告诉binder驱动程序进入轮询模式
    //这里只是向缓冲区中写入命令getAndExecuteCommand函数中会调用talkWithDriver将BC_ENTER_LOOPER指令发送给binder驱动程序
    mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER);
    status_t result;
    //进入死循环与binder驱动程序轮询交互(包括命令/数据读写操作)
    do {
        //判断缓冲区mOut和mIn是否有数据 否则阻塞
        processPendingDerefs();
        //命令处理
        result = getAndExecuteCommand();
        if(result == TIMED_OUT && !isMain) {
            break;
        }
    } while (result != -ECONNREFUSED && result != -EBADF);
    //向mOut缓冲区写入BC_EXIT_LOOPER命令告诉binder驱动程序终止轮询模式
    //因为后面没有getAndExecuteCommand调用所以主动调用talkWithDriver将该指令发送给binder驱动程序
    mOut.writeInt32(BC_EXIT_LOOPER);
    talkWithDriver(false);
}
status_t IPCThreadState::getAndExecuteCommand()
{
    status_t result;
    int32_t cmd;
    //前面已经将近了该函数对binder驱动程序进行系统调用ioctl
    //并将结果保存到mIn里面
    result = talkWithDriver();
    if (result >= NO_ERROR) {
        //从mIn里面读取命令
        size_t IN = mIn.dataAvail();
        cmd = mIn.readInt32();
        //解析命令
        result = executeCommand(cmd);
    }
    return result;
}
status_t IPCThreadState::executeCommand(int32_t cmd)
{
    BBinder* obj;
    RefBase::weakref_type* refs;
    status_t result = NO_ERROR;
    switch ((uint32_t)cmd) {
    case BR_ERROR:
        result = mIn.readInt32();
        break;
    case BR_OK:
        break;
    //数据传输命令(来自BpBinder的透传命令/数据)
    case BR_TRANSACTION: {
            binder_transaction_data tr;
            result = mIn.read(&tr, sizeof(tr));
            Parcel buffer;
            Parcel reply;
            status_t error;
            if (tr.target.ptr) {
                if (reinterpret_cast<RefBase::weakref_type*>(tr.target.ptr)->attemptIncStrong(this)) {
                    //强制转换成BBinder,并调用BBinder的transact函数
                    //BBinder的transact会将业务逻辑转交给onTransact来处理
                    error = reinterpret_cast<BBinder*>(tr.cookie)->transact(tr.code, buffer, &reply, tr.flags);
                    reinterpret_cast<BBinder*>(tr.cookie)->decStrong(this);
                }
            } else {
                error = the_context_object->transact(tr.code, buffer, &reply, tr.flags);
            }
            if ((tr.flags & TF_ONE_WAY) == 0) {
                //BBinder的onTransact处理对应的命令如果有返回值
                //将返回值打包到reply并通过sendReply函数发送给binder驱动程序透传给BpBinder
                if (error < NO_ERROR) reply.setError(error);
                sendReply(reply, 0);
            } else {
                LOG_ONEWAY("NOT sending reply to %d!", mCallingPid);
            }
        }
        break;
    }
}
status_t IPCThreadState::sendReply(const Parcel& reply, uint32_t flags)
{
    status_t err;
    status_t statusBuffer;
    //设置返回数据命令BC_REPLY
    err = writeTransactionData(BC_REPLY, flags, -1, 0, reply, &statusBuffer);
    if (err < NO_ERROR) return err;
    //前面已经知道waitForResponse中会调用talkWithDriver函数来与binder驱动程序交互
    //waitForResponse最后会解析一些响应的命令
    //其中客户端进程IPCThreadState的解析BC_REPLY命令将数据回传给BpBinder
    return waitForResponse(NULL, NULL);
}

BBinder通过IPCThreadState::self()->joinThreadPool函数让整个进程进入Binder轮询模式,即开启几个线程,进入do-while死循环,死循环里面轮询检测mIn缓冲区中是否有来自binder驱动程序的数据(看是否有事情可做),然后用过getAndExecuteCommand 函数处理每条命令(处理命令的方式先talkWithDriver与驱动程序交互并获取返回值,再executeCommand中解析它)。如下: 

BBinder ------> IPCThreadState::self()

              ------> joinThreadPool

                                     ----循环---> getAndExecuteCommand --------> talkWithDriver

                                                                                                 --------> executeCommand

3)、binder驱动处理BC_TRANSACTION消息

通过上面两个小节的知识点可以得出下面一个流程:

  • 步骤一:BpBinder通过IPCThreadState的transact向binder驱动程序发送命令数据经过层层封装,可以将消息解析为主功能码BINDER_WRITE_READ,次功能码BC_TRANSACTION,代码如下:

  • 步骤二:BBinder所在的IPCThreadState轮询查找缓冲区中是否有来自binder驱动程序的命令数据,并解析的功能码是BR_TRANSACTION的消息,代码如下:

我们知道BpBinder所在进程的IPCThreadState和BBinder所在进行的IPCThreadState,上面的两个流程需要对应起来,那么binder驱动程序肯定干了不为人知的转换操作,个人估计binder驱动程序是否是先解析了BC_TRANSACTION消息,并根据该消息附带的参数handle值找到自己持有的红黑树中对应handle的binder_proc实例对象,然后重新封装BR_TRANSACTION消息发送给该实例对象。为了证实这个大胆的猜测,我们再次回到binder驱动程序的ioctl接口中看看吧,代码如下:

//kernel/drivers/staging/android/binder.c
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    //获取当前系统调用的进程实例,是否还记的第三章Binder第2节:
    //应用进程在首次打开binder驱动设备的时候就会创建一个binder_proc对象表示当前应用进程实例
    //并将其保存在文件描述符的容器(filp->private_data)中,最后还将这个对象添加到binder_procs列表中
    //该进程在以后进行系统调用mmap或者ioctl的时候就可以根据驱动设备文件描述符来得到这个实例
    struct binder_proc *proc = filp->private_data;
    //解析ioctl控制命令
	switch (cmd) {
	case BINDER_WRITE_READ: 
        //创建内核空间的binder_write_read用来描述进程间通信中请求数据和返回数据
		struct binder_write_read bwr;
        //拷贝用户空间的请求数据到bwr中
		if (copy_from_user(&bwr, ubuf, sizeof(bwr))) { }
        //将bwr中的请求数据交给上面的线程thread中进行处理
		if (bwr.write_size > 0) ret = binder_thread_write(proc, thread, (void __user *)bwr.write_buffer, bwr.write_size, &bwr.write_consumed);
        //获取binder线程thread中处理的结果保存到bwr中
		if (bwr.read_size > 0) ret = binder_thread_read(proc, thread, (void __user *)bwr.read_buffer, bwr.read_size, &bwr.read_consumed, filp->f_flags & O_NONBLOCK);
        //拷贝bwr中返回数据到用户空间
		if (copy_to_user(ubuf, &bwr, sizeof(bwr))) { }
		break;
    }
    //.....
}
//参数proc表示当前触发系统调用的应用进程实例,从文件描述符容器中获得filp->private_data
int binder_thread_write(struct binder_proc *proc, struct binder_thread *thread, void __user *buffer, int size, signed long *consumed)
{
    //定义子功能码和跟随的参数指针
    uint32_t cmd;
    void __user *ptr = buffer + *consumed;
    void __user *end = buffer + size;
    while (ptr < end && thread->return_error == BR_OK) {
        //获取子功能码
        if (get_user(cmd, (uint32_t __user *)ptr)) return -EFAULT;
        //解析子功能码
        switch (cmd) {
            case BC_TRANSACTION: //数据发送命令,BC表示上层向binder驱动发送
		    case BC_REPLY:       //数据返回命令
			    struct binder_transaction_data tr; //临时缓存通信数据
			    if (copy_from_user(&tr, ptr, sizeof(tr))) return -EFAULT;
			    ptr += sizeof(tr);
                //binder_transaction来处理数据通信命令,如果是BC_REPLY最后参数为true
			    binder_transaction(proc, thread, &tr, cmd == BC_REPLY);
			break;
        }
    }
}
//参数proc同上表示当前进行系统调用的应用进程,参数reply表示当前命令是否为BC_REPLY
static void binder_transaction(struct binder_proc *proc, struct binder_thread *thread, struct binder_transaction_data *tr, int reply)
{
    if (reply) { //处理来自服务端发送的返回数据
        //.....
    } else {     //处理来自客户端发送的请求数据
		//与service manager服务注册这一节介绍的一样先判断参数handle句柄
        if (tr->target.handle) {
			struct binder_ref *ref;
            //通过handle来得到binder_ref引用对象
			ref = binder_get_ref(proc, tr->target.handle);
            //通过binder_ref引用对象得到binder_node实体对象,它们被关联在binder驱动维护的几个红黑树里面
			target_node = ref->node;
		} else {
			target_node = binder_context_mgr_node;
		}
        //通过binder_node实体对象得到对应的binder_proc(提供service服务的进程实例对象)
        //与service manager服务注册这一节已经介绍了服务端进程在向service manager注册服务的时候
        //最终来到了binder驱动程序创建了binder_proc对象,并被添加到binder驱动程序维护的一个队列中
        //在该场景下proc表示客户端用户进程在内核空间的进程实例对象,通过该对象在binder驱动维护的关系表中依次获取它的binder_ref引用对象、binder_node实体对象、服务端用户进程实例对象binder_proc,现在binder驱动程序手握两者的命脉,想怎么玩就怎么玩,想怎么通信就通信
        target_proc = target_node->proc;
        //....
    }
}

上面代码我们发现了一些蛛丝马迹,binder_thread_write函数中解析了BC开头的命令,binder_transaction 函数负责解析来自用户进程发送过来的请求指令(客户端->binder驱动->服务端)和返回指令(客户端<-binder驱动<-服务端)。不过代码很复杂,感兴趣的同学可以参考本链接对照下图研究:

3、BnInterface vs BpInterface

前面分别介绍了本地对象BBinder和代理对象BpBinder,这对难兄难弟在两个不想交的平行世界(被隔离的用户进程),为了能够与对方进行通信,不得不找到自己进程的唯一线程实例IPCThreadState对象向binder驱动程序发起请求,binder驱动程序内部持有了所有用户进程对应的binder_proc对象组成的哈希队列,因此很方便的找到对应把他们发送的数据进行透传。

BBinder和BpBinder虽然实现了进程间的通信,但是不利于扩展,为了将它们的精神发扬光大,那么就必须依靠它们两个的派生类。什么意思呢?举个很简单的例子:现在需要系统提供视频播放的功能,那么就需要创建一个能够实现视频播放的BBinder(即需要重写该BBinder的onTransact函数来解析命令MSG_START_PLAY/MSG_STOP_PLAY进行视频播放/结束播放),还需要创建一个发起视频播放命令/结束播放的BpBinder(即需要重写该BpBinder的transcat函数调用IPCThreadState::self()->transact发送命令MSG_START_PLAY/MSG_STOP_PLAY请求BBinder进行视频播放/结束的功能);如果现在有需要提供照相功能呢,是不是又要创建实现照相功能的BBinder和BpBinder,这个时候是不是干了很多重复的工作,一个庞大的系统再干这些重复的动作只会让该系统越来越庞大,这是我们无法忍受的。那么面向对象语言的多态,就很好的解决了这个问题,无论是C++还是Java都有多态的概念(不清楚的可以点击该链接),我们可以派生BBinder和BpBinder的子类来进行功能扩展,这样就不用再每个BBinder和BpBinder里面重新写关于IPCThreadState相关代码了,当然通常做法并不会直接继承BBinder和BpBinder,Java中通常去继承接口,而C++中则是实现一个模板接口类。

1)、BnInterface

BnInterface就是用来扩展BBinder的接口,即BBinder封装了IPCThreadState与binder驱动程序之间的逻辑,具体详情参考IPCThreadState与BBinder小节。

BnInterface继承了BBinder,从BBinder中获得了进程间通信的功能,除此之外还继承业务模板INTERFACE,因此可以认为BnInterface在BBinder的基础上扩展了业务功能其定义如下:

//frameworks/native/libs/binder/include/binder/IInterface.h
//定义业务模板类INTERFACE
template<typename INTERFACE>
//BnInterface 除了继承BBinder还继承了上面的业务模板类INTERFACE
class BnInterface : public INTERFACE, public BBinder
{
public:
    virtual sp<IInterface>      queryLocalInterface(const String16& _descriptor);
    virtual const String16&     getInterfaceDescriptor() const;
protected:
    virtual IBinder*            onAsBinder();
};

2)、BpInterface

BpInterface也是用来扩展BpBinder的接口,前文已经知道BBinder与BpBinder配对,它们缺一不可否则无法进行通信,那么同样构建了BpInterface来与BnInterface配对。但是这里让人惊讶的是BpInterface并没有继承BpBinder。如下定义:

//frameworks/native/libs/binder/include/binder/IInterface.h
template<typename INTERFACE>
class BpInterface : public INTERFACE, public BpRefBase
{
public:
    //构造函数比较给力,参数是IBinder
    explicit                    BpInterface(const sp<IBinder>& remote);
protected:
    virtual IBinder*            onAsBinder();
};

从上面的定义发现BpInterface并没有直接继承BpBinder,而是继承了一个叫做BpRefBase的类,我们来看看是不是通过它来间接获取BpBinder的功能呢?

class BpRefBase : public virtual RefBase
{
protected:
    //构造函数的参数必须是IBinder
    explicit                BpRefBase(const sp<IBinder>& o);
    virtual                 ~BpRefBase();
    virtual void            onFirstRef();
    virtual void            onLastStrongRef(const void* id);
    virtual bool            onIncStrongAttempted(uint32_t flags, const void* id);
    inline  IBinder*        remote()                { return mRemote; }
    inline  IBinder*        remote() const          { return mRemote; }
private:
                            BpRefBase(const BpRefBase& o);
    BpRefBase&              operator=(const BpRefBase& o);
    IBinder* const          mRemote; //内部持有了IBinder接口,远程IBinder接口
    RefBase::weakref_type*  mRefs;
    std::atomic<int32_t>    mState;
};

从上面的定义可以发现BpRefBase内部持有了IBinder接口,在实际使用中,通常把BpBinder作为参数用来构造BpInterface对象,就这样通过组合模式实现了BpXXX具有了Binder通信的能力详情参考后文。

4、IInterface vs INTERFACE

IInterface作为一个业务接口基类,即抽象了所有业务类的共同代码:将IInterface转换成IBinder接口。根据下面代码发现,IInterface并没有实现具体的转换过程,而是定义了虚函数asBinder让它的子类去实现,如下代码:

//frameworks/native/libs/binder/include/binder/IInterface.h
class IInterface : public virtual RefBase
{
public:
            IInterface() : RefBase() { }
            //定义两个静态函数用来将IInterface转换成IBinder
            //静态转换函数最后实际上还是调用的虚函数onAsBinder
            static sp<IBinder>  asBinder(const IInterface*) {
                if (iface == NULL) return NULL;
                return const_cast<IInterface*>(iface)->onAsBinder();
            }
            static sp<IBinder>  asBinder(const sp<IInterface>&) {
                if (iface == NULL) return NULL;
                return iface->onAsBinder();
            }
protected:
    virtual                     ~IInterface() {}
    //定义虚函数来具体实现转换过程
    virtual IBinder*            onAsBinder() = 0;
};

如上代码定义IInterface,除了让其继承智能指针之外定义虚函数onAsBinder,还定义了静态函数asBinder来将自己转换成一个IBinder接口。为什么要这样做呢?IInterface代表一个业务接口的基类,例如播放视频/停止播放接口、拍照等接口,这些接口都是业务相关的,跟前面讲解的BBinder和BpBinder继承的通用接口IBinder没有半毛钱关系,我们已经知道了IBinder的transact函数才是进程间通信的通用接口,所以我们需要将它转换成IBinder才能进行进程间通信。

BnInterface和BpInterface的定义可以发现,都继承了一个模板类INTERFACE,并没有继承业务接口IInterface最初我根本无法理解模板类INTERFACE和上面说的业务接口IInterface到底是什么关系,他们除了名字类似,貌似看不出还有其他什么关系,终于皇天不负有心人,终于被我抓住了一丝破绽。

我们回到IInterface.h文件,提供了一个模板函数interface_cast来将IBinder转换成模板类INTERFACE,如下:

//frameworks/native/libs/binder/include/binder/IInterface.h
//参数为IBinder返回值是一个INTERFACE
template<typename INTERFACE>
inline sp<INTERFACE> interface_cast(const sp<IBinder>& obj)
{
    return INTERFACE::asInterface(obj);
}
/**
用法:
1:例如INTERFACE为Ixxxx,该模板函数扩展后相当于Ixxxx.asInterface(obj)
2:例如INTERFACE为IServiceManager,该模板函数如下:
inline sp<IServiceManager> interface_cast(const sp<IBinder>& obj){
    return IServiceManager::asInterface(obj);
}

可以看出模板函数interface_cast是将一个IBinder接口转换成模板类INTERFACE,根据该模板函数展开后惊奇的发现,变成了将一个IBinder接口转换成了IInterface接口的子类,是不是很神奇?google这样写代码真的是很让人蛋疼,怪只怪C++确实是世界上最复杂的语言。

再次回到IInterface.h文件,该文件还没有完,还定义了几个宏,其中比较重要的宏DECLARE_META_INTERFACE和IMPLEMENT_META_INTERFACE,代码如下:

//frameworks/native/libs/binder/include/binder/IInterface.h
//定义宏DECLARE_META_INTERFACE,需要参数INTERFACE
#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() { }                                   \

展开宏后会发现,宏DECLARE_META_INTERFACE用来定义IInterface的某些函数,宏IMPLEMENT_META_INTERFACE用来实现刚刚定义的函数。在了解这两个宏之前,我们先来找几个IXXX来研究一下。

1)、IServiceManager的定义

IServiceManager(后文把这样的类简称IXXX)其实是业务接口,因此它肯定与IInterface有些关系,如下定义:

//frameworks/native/libs/binder/include/binder/IServiceManager.h
//定义IServiceManager:继承业务接口IInterface,定义了业务逻辑接口getService/checkService/addService
class IServiceManager : public IInterface
{
public:
    DECLARE_META_INTERFACE(ServiceManager)
    //使用了宏DECLARE_META_INTERFACE,其中参数INTERFACE为ServiceManager
    /*展开如下:
    static const ::android::String16 descriptor; 
    static ::android::sp<IServiceManager> asInterface( 
            const ::android::sp<::android::IBinder>& obj); 
    virtual const ::android::String16& getInterfaceDescriptor() const; 
    IServiceManager(); 
    virtual ~IServiceManager(); 
    */
    virtual sp<IBinder>         getService( const String16& name) const = 0;
    virtual sp<IBinder>         checkService( const String16& name) const = 0;
    virtual status_t addService(const String16& name, const sp<IBinder>& service, bool allowIsolated = false, int dumpsysFlags = DUMP_FLAG_PRIORITY_DEFAULT) = 0;
    virtual Vector<String16> listServices(int dumpsysFlags = DUMP_FLAG_PRIORITY_ALL) = 0;
};
//frameworks/native/libs/binder/IServiceManager.cpp
//使用了宏IMPLEMENT_META_INTERFACE,其中参数分别是ServiceManager和"android.os.IServiceManager"
IMPLEMENT_META_INTERFACE(ServiceManager, "android.os.IServiceManager");
/*展开如下:
    const ::android::String16 IServiceManager::descriptor("android.os.IServiceManager");
    const ::android::String16& IServiceManager::getInterfaceDescriptor() const {
        return IServiceManager::descriptor;
    }
    ::android::sp<IServiceManager> IServiceManager::asInterface( const ::android::sp<::android::IBinder>& obj) {
        ::android::sp<IServiceManager> intr;
        if (obj != NULL) {
            intr = static_cast<IServiceManager*>(obj->queryLocalInterface(IServiceManager::descriptor).get());
           if (intr == NULL) {
              intr = new BpServiceManager(obj);
            }
        }
        return intr;
    }
    IServiceManager::IServiceManager() { }
    IServiceManager::~IServiceManager() { }
*/
//定义BpServiceManager:继承BpInterface
class BpServiceManager : public BpInterface<IServiceManager>
{
public:
    //BpXXX持有IBinder实例,其构造函数需要传递IBinder
    BpServiceManager(const sp<IBinder>& impl) : BpInterface<IServiceManager>(impl) {
    }
    virtual sp<IBinder> getService(const String16& name) const {
        //....
        return NULL;
    }
    virtual sp<IBinder> checkService(const String16& name) const {
        //....
        remote()->transact(CHECK_SERVICE_TRANSACTION, data, &reply);
        return reply.readStrongBinder();
    }
    virtual status_t addService(const String16& name, const sp<IBinder>& service, bool allowIsolated, int dumpsysPriority) {
        //....
        status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);
        return err == NO_ERROR ? reply.readExceptionCode() : err;
    }
    virtual Vector<String16> listServices(int dumpsysPriority) {
        Vector<String16> res;
        int n = 0;
        for (;;) {
            Parcel data, reply;
            data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
            data.writeInt32(n++);
            data.writeInt32(dumpsysPriority);
            status_t err = remote()->transact(LIST_SERVICES_TRANSACTION, data, &reply);
            if (err != NO_ERROR) break;
            res.add(reply.readString16());
        }
        return res;
    }
};

IServiceManager.h文件中,定义了类IServiceManager,其中使用了宏DECLARE_META_INTERFACE(ServiceManager),该宏展开后就会发现定义了字段descriptor(用来表示Service的描述)和函数asInterface(用来将IBinder接口转换成IInterface接口)。

IServiceManager.cpp文件中,实现了类IServiceManager,其中使用了宏IMPLEMENT_META_INTERFACE,该宏展开后就会发现实现了上面定义的函数asInterface,在该函数中居然使用了组合模式,直接将IBinder作为参数构造了BpXXX。

在Android S的代码中好像并没有使用IMPLEMENT_META_INTERFACE和DECLARE_META_INTERFACE这套机制。

2)、IPowerManager的定义

IPowerManager是一个跟电源业务相关的接口,相对于IServiceManager来说简单的太多了,如下:

//frameworks/native/include/powermanager/IPowerManager.h
//定义接口IPowerManager
class IPowerManager : public IInterface
{
public:
    enum {
        ACQUIRE_WAKE_LOCK            = IBinder::FIRST_CALL_TRANSACTION,
        ACQUIRE_WAKE_LOCK_UID        = IBinder::FIRST_CALL_TRANSACTION + 1,
        RELEASE_WAKE_LOCK            = IBinder::FIRST_CALL_TRANSACTION + 2,
        UPDATE_WAKE_LOCK_UIDS        = IBinder::FIRST_CALL_TRANSACTION + 3,
        POWER_HINT                   = IBinder::FIRST_CALL_TRANSACTION + 4,
        UPDATE_WAKE_LOCK_SOURCE      = IBinder::FIRST_CALL_TRANSACTION + 5,
        IS_WAKE_LOCK_LEVEL_SUPPORTED = IBinder::FIRST_CALL_TRANSACTION + 6,
        USER_ACTIVITY                = IBinder::FIRST_CALL_TRANSACTION + 7,
        WAKE_UP                      = IBinder::FIRST_CALL_TRANSACTION + 8,
        GO_TO_SLEEP                  = IBinder::FIRST_CALL_TRANSACTION + 9,
        NAP                          = IBinder::FIRST_CALL_TRANSACTION + 10,
        IS_INTERACTIVE               = IBinder::FIRST_CALL_TRANSACTION + 11,
        IS_POWER_SAVE_MODE           = IBinder::FIRST_CALL_TRANSACTION + 12,
        GET_POWER_SAVE_STATE         = IBinder::FIRST_CALL_TRANSACTION + 13,
        SET_POWER_SAVE_MODE          = IBinder::FIRST_CALL_TRANSACTION + 14,
        REBOOT                       = IBinder::FIRST_CALL_TRANSACTION + 17,
        REBOOT_SAFE_MODE             = IBinder::FIRST_CALL_TRANSACTION + 18,
        SHUTDOWN                     = IBinder::FIRST_CALL_TRANSACTION + 19,
        CRASH                        = IBinder::FIRST_CALL_TRANSACTION + 20,
    };
    //通过宏DECLARE_META_INTERFACE定义asInterface函数
    DECLARE_META_INTERFACE(PowerManager)
    virtual status_t acquireWakeLock(int flags, const sp<IBinder>& lock, const String16& tag, const String16& packageName, bool isOneWay = false) = 0;
    virtual status_t acquireWakeLockWithUid(int flags, const sp<IBinder>& lock, const String16& tag, const String16& packageName, int uid, bool isOneWay = false) = 0;
    virtual status_t releaseWakeLock(const sp<IBinder>& lock, int flags, bool isOneWay = false) = 0;
    virtual status_t updateWakeLockUids(const sp<IBinder>& lock, int len, const int *uids, bool isOneWay = false) = 0;
    virtual status_t powerHint(int hintId, int data) = 0;
    virtual status_t goToSleep(int64_t event_time_ms, int reason, int flags) = 0;
    virtual status_t reboot(bool confirm, const String16& reason, bool wait) = 0;
    virtual status_t shutdown(bool confirm, const String16& reason, bool wait) = 0;
    virtual status_t crash(const String16& message) = 0;
};
//frameworks/native/services/powermanager/IPowerManager.cpp
//定义BpPowerManager :继承BpInterface,业务模板接口IPowerManager
class BpPowerManager : public BpInterface<IPowerManager>
{
public:
    BpPowerManager(const sp<IBinder>& impl) : BpInterface<IPowerManager>(impl) {
    }
    //.....其他接口
    //扩展BpInterface时只需要实现IInterface提供的接口
    //内部都使用了remote()->transact用来给BnBinder发送消息
    //remote()返回的是BpRefBase持有的IBinder接口对象
    virtual status_t goToSleep(int64_t event_time_ms, int reason, int flags) {
        Parcel data, reply;
        data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor());
        data.writeInt64(event_time_ms);
        data.writeInt32(reason);
        data.writeInt32(flags);
        return remote()->transact(GO_TO_SLEEP, data, &reply, 0);
    }
    virtual status_t reboot(bool confirm, const String16& reason, bool wait) {
        Parcel data, reply;
        data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor());
        data.writeInt32(confirm);
        data.writeString16(reason);
        data.writeInt32(wait);
        return remote()->transact(REBOOT, data, &reply, 0);
    }
    virtual status_t shutdown(bool confirm, const String16& reason, bool wait) {
        Parcel data, reply;
        data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor());
        data.writeInt32(confirm);
        data.writeString16(reason);
        data.writeInt32(wait);
        return remote()->transact(SHUTDOWN, data, &reply, 0);
    }
    virtual status_t crash(const String16& message) {
        Parcel data, reply;
        data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor());
        data.writeString16(message);
        return remote()->transact(CRASH, data, &reply, 0);
    }
};
//实现函数sp<PowerManager> IPowerManager::asInterface(const sp<IBinder>& obj)函数
//即直接new BpPowerManager(obj)出来
IMPLEMENT_META_INTERFACE(PowerManager, "android.os.IPowerManager");

从这两个例子不难发现,开发者在使用binder通信的时候,定义IXXX的时候使用宏DECLARE_META_INTERFACE来定义模板XXX,在实现IXXX的时候使用宏IMPLEMENT_META_INTERFACE,这样就统一了IXXX转换成BpXXX的方式,即直接new BpXXX(IXXX);然后定义BpXXX类继承BpInterface<IXXX>,并实现IXXX的提供的业务接口;在这些业务接口中使用transact函数向BnBinder发送消息,这样就完成了binder通信库的基本模板。

3)、IMediaPlayer的定义

从上面的两个IXXX可以发现它们貌似还差个东西BnXXX,因为他们的功能比较特殊,现在介绍一个比较完整的IMediaPlayer,其定义如下:

//frameworks/av/include/media/IMediaPlayer.h
//定义接口IMediaPlayer:继承IInterface
class IMediaPlayer: public IInterface
{
public:
    DECLARE_META_INTERFACE(MediaPlayer);    
    /*展开如下:
    static const ::android::String16 descriptor;
    static ::android::sp<IMediaPlayer> asInterface(
            const ::android::sp<::android::IBinder>& obj);
    virtual const ::android::String16& getInterfaceDescriptor() const;
    IMediaPlayer();
    virtual ~IMediaPlayer();
    */
    virtual void            disconnect() = 0;
    virtual status_t        setDataSource(const sp<IMediaHTTPService> &httpService, const char *url, const KeyedVector<String8, String8>* headers) = 0;
    virtual status_t        prepareAsync() = 0;
    virtual status_t        start() = 0;
    virtual status_t        stop() = 0;
    virtual status_t        pause() = 0;
    virtual status_t        isPlaying(bool* state) = 0;
    virtual status_t        invoke(const Parcel& request, Parcel *reply) = 0;
    //.....省略
};
//定义BnMediaPlayer:继承BnInterface,只重写了onTransact方法
class BnMediaPlayer: public BnInterface<IMediaPlayer>
{
public:
    //重写该方法定制来自binder驱动程序的请求并进行消息分发处理
    virtual status_t    onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0);
};
//frameworks/av/media/libmedia/IMediaPlayer.cpp
//定义BpMediaPlayer:继承BpInterface
class BpMediaPlayer: public BpInterface<IMediaPlayer>
{
public:
    BpMediaPlayer(const sp<IBinder>& impl) : BpInterface<IMediaPlayer>(impl) {
    }
    void disconnect() {
        Parcel data, reply;
        data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
        remote()->transact(DISCONNECT, data, &reply);
    }
    status_t start() {
        Parcel data, reply;
        data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
        remote()->transact(START, data, &reply);
        return reply.readInt32();
    }
    status_t stop() {
        Parcel data, reply;
        data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
        remote()->transact(STOP, data, &reply);
        return reply.readInt32();
    }
    //.....省略
};
//实现IMediaPlayer的asInterface函数
IMPLEMENT_META_INTERFACE(MediaPlayer, "android.media.IMediaPlayer");
/*展开如下:
    const ::android::String16 IMediaPlayer::descriptor("android.media.IMediaPlayer");
    const ::android::String16& IMediaPlayer::getInterfaceDescriptor() const {
        return IMediaPlayer::descriptor;
    }
    ::android::sp<IMediaPlayer> IMediaPlayer::asInterface(const ::android::sp<::android::IBinder>& obj) {
        ::android::sp<IMediaPlayer> intr;
        if (obj != NULL) {
            intr = static_cast<IMediaPlayer*>( obj->queryLocalInterface( IMediaPlayer::descriptor).get());
            if (intr == NULL) {
                intr = new BpMediaPlayer(obj);
            }
        }
        return intr;
    }
    IMediaPlayer::IMediaPlayer() { }
    IMediaPlayer::~IMediaPlayer() { }
*/
#include <media/IMediaPlayer.h> //提供了start stop这些media相关操作的函数
//重写BnMediaPlayer的onTransact方法进行消息处理
status_t BnMediaPlayer::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
    switch (code) {
        case DISCONNECT: {
            CHECK_INTERFACE(IMediaPlayer, data, reply);
            disconnect();
            return NO_ERROR;
        } break;
        case START: { //调用start函数来执行START命令
            CHECK_INTERFACE(IMediaPlayer, data, reply);
            reply->writeInt32(start());
            return NO_ERROR;
        } break;
        case STOP: {   //调用stop函数来执行STOP命令
            CHECK_INTERFACE(IMediaPlayer, data, reply);
            reply->writeInt32(stop());
            return NO_ERROR;
        } break;
        //....省略
        default:
            return BBinder::onTransact(code, data, reply, flags);
    }
}

上面的代码可以完整的发现IXXX.h文件和IXXX.cpp文件里面已经定义和实现了IXXX业务接口类。除此之外BpXXX实现了该接口提供的所有方法:通过remote()->transact将消息交给持有的BpBinder发送给Binder驱动程序(利用了IPCThreadState);而BnXXX重写了onTransact方法:主要完成的工作就是解析来自BBinder从Binder驱动程序接收到的消息(BnXXX是BBinder的子类)

5、Service组件 vs Client组件

真相大白,Android系统在扩展业务模块的时候,在对应头文件中定义了Ixxxx继承IInterface业务接口类,且在定义的时候使用了宏DECLARE_META_INTERFACE来定义asInterface函数,并在对应的cpp文件中通过宏IMPLEMENT_META_INTERFACE来实现asInterface函数,该函数的目的就是通过IBinder将其转换成一个BpXXXX。而这样的应用方式在Android中比比皆是,可以用烂大街三个字来形容,如下图:

为了更好的梳理上面各种类和奇葩设计方式,罗升阳老师已经为我们总结了Binder通信库中的Service组件和Client组件的原理图。

1)、Client组件

  • ProcessState在实例化的时候将打开binder驱动程序文件描述符,并进行内存映射分配缓冲区,为IPCThreadState操作binder驱动程序提供保障
  • BpXXX继承BpInterface,实现IXXX定义的业务接口,其原理把消息进行封装,然后通过所持有mRemote实例发送出去
  • mRemote是从BpRefBase继承过来,表示远程代理对象,被定义为IBinder类型,其实原型是BpBinder,至于为什么是个BpBinder请继续向下阅读
  • BpBinder类型的mRemote实例对象被委任将消息传递出去,最后发送给谁了呢?那就是当前进程主线程唯一实例对象IPCThreadState
  • IPCThreadState收到消息后也不敢怠慢,直接进行了binder驱动程序的ioctl系统调用,交给了binder驱动程序
  • binder驱动程序处理了一系列的交互逻辑来保证与用户空间的客户端组件和服务端组件保持稳定,可以参考TCP三次握手机制,最终将消息发送给了Service组件(请参考第二小节)

2)、Service组件

  • ProcessState在实例化的时候将打开binder驱动程序文件描述符,并进行内存映射分配缓冲区

  • IPCThreadState接收来自Binder驱动程序的消息并回调BBinder的transact函数

  • BnInterface作为服务端接口,除了继承BBinder之外还继承IXXX业务逻辑接口

  • BnXXX对集Binder通信和业务逻辑于一身的BnInterface进行封装,即重写transact函数并对其中收到的命令进行任务分发,最后交给虚函数onTransact来处理

  • XXX最终的Service组件,只需要重写上面的虚函数onTransact来进行任务处理就行了,其他的任何事情貌似都雨我无瓜

二、service manager的客户端进程

在讲解如何运用libbinder.so之前先介绍service manager的客户端进程,因为所有需要进行binder通信的进程都需要与service manager进程进行通信,例如:提供服务的进程需要向其进行注册,使用服务的进程需要向其查询该服务,它们都作为service manager的客户端进程。

1、业务接口IServiceManager

通过第一章的知识,service manager的客户端进程需要使用Binder库里面的Client组件,即需要自己定义BpServiceManager和IServiceManager,而service manager的服务端进程确没有使用Binder库里面的Service组件,因为service manager进程比较特殊,所以把它与binder库独立出来单独实现。

//frameworks/native/libs/binder/include/binder/IServiceManager.h
//定义IServiceManager:继承业务接口IInterface,定义了业务逻辑接口getService/checkService/addService
class IServiceManager : public IInterface
{
public:
    DECLARE_META_INTERFACE(ServiceManager)
    //使用了宏DECLARE_META_INTERFACE,其中参数INTERFACE为ServiceManager
    /*展开如下:
    static const ::android::String16 descriptor; 
    static ::android::sp<IServiceManager> asInterface( 
            const ::android::sp<::android::IBinder>& obj); 
    virtual const ::android::String16& getInterfaceDescriptor() const; 
    IServiceManager(); 
    virtual ~IServiceManager(); 
    */
    virtual sp<IBinder>         getService( const String16& name) const = 0;
    virtual sp<IBinder>         checkService( const String16& name) const = 0;
    virtual status_t addService(const String16& name, const sp<IBinder>& service, bool allowIsolated = false, int dumpsysFlags = DUMP_FLAG_PRIORITY_DEFAULT) = 0;
    virtual Vector<String16> listServices(int dumpsysFlags = DUMP_FLAG_PRIORITY_ALL) = 0;
    enum {
        GET_SERVICE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
        CHECK_SERVICE_TRANSACTION,
        ADD_SERVICE_TRANSACTION,
        LIST_SERVICES_TRANSACTION,
    };
};
//定义函数defaultServiceManager:来获取service manager服务的业务接口
sp<IServiceManager> defaultServiceManager();
//定义函数getService:直接获取指定name的服务的业务接口
template<typename INTERFACE>
status_t getService(const String16& name, sp<INTERFACE>* outService) {
    const sp<IServiceManager> sm = defaultServiceManager();
    if (sm != NULL) {
        *outService = interface_cast<INTERFACE>(sm->getService(name));
        if ((*outService) != NULL) return NO_ERROR;
    }
    return NAME_NOT_FOUND;
}
//frameworks/native/libs/binder/IServiceManager.cpp
//实现函数defaultServiceManager
sp<IServiceManager> defaultServiceManager() {
    if (gDefaultServiceManager != NULL) return gDefaultServiceManager;
    {
        AutoMutex _l(gDefaultServiceManagerLock);
        while (gDefaultServiceManager == NULL) {
            gDefaultServiceManager = interface_cast<IServiceManager>( ProcessState::self()->getContextObject(NULL));
            if (gDefaultServiceManager == NULL) sleep(1);
        }
    }
    return gDefaultServiceManager;
}
//定义BpServiceManager:继承binder库中的BpInterface
class BpServiceManager : public BpInterface<IServiceManager>
{
public:
    BpServiceManager(const sp<IBinder>& impl) : BpInterface<IServiceManager>(impl) {
    }
    virtual sp<IBinder> getService(const String16& name) const
    {
        sp<IBinder> svc = checkService(name);
        if (svc != NULL) return svc;
        //...处理vendor binder和启动完成逻辑,循环延迟调用checkService来获取service
        return NULL;
    }
    virtual sp<IBinder> checkService( const String16& name) const {
        Parcel data, reply;
        data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
        data.writeString16(name);
        remote()->transact(CHECK_SERVICE_TRANSACTION, data, &reply);
        return reply.readStrongBinder();
    }
    virtual status_t addService(const String16& name, const sp<IBinder>& service, bool allowIsolated, int dumpsysPriority) {
        Parcel data, reply;
        data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
        data.writeString16(name);
        data.writeStrongBinder(service);
        data.writeInt32(allowIsolated ? 1 : 0);
        data.writeInt32(dumpsysPriority);
        status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);
        return err == NO_ERROR ? reply.readExceptionCode() : err;
    }
    virtual Vector<String16> listServices(int dumpsysPriority) {
        Vector<String16> res;
        int n = 0;
        for (;;) {
            Parcel data, reply;
            data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
            data.writeInt32(n++);
            data.writeInt32(dumpsysPriority);
            status_t err = remote()->transact(LIST_SERVICES_TRANSACTION, data, &reply);
            if (err != NO_ERROR) break;
            res.add(reply.readString16());
        }
        return res;
    }
};
//使用了宏IMPLEMENT_META_INTERFACE,其中参数分别是ServiceManager和"android.os.IServiceManager"
IMPLEMENT_META_INTERFACE(ServiceManager, "android.os.IServiceManager");
/*展开如下:
    const ::android::String16 IServiceManager::descriptor("android.os.IServiceManager");
    const ::android::String16& IServiceManager::getInterfaceDescriptor() const {
        return IServiceManager::descriptor;
    }
    ::android::sp<IServiceManager> IServiceManager::asInterface( const ::android::sp<::android::IBinder>& obj) {
        ::android::sp<IServiceManager> intr;
        if (obj != NULL) {
            intr = static_cast<IServiceManager*>(obj->queryLocalInterface(IServiceManager::descriptor).get());
           if (intr == NULL) {
              intr = new BpServiceManager(obj);
            }
        }
        return intr;
    }
    IServiceManager::IServiceManager() { }
    IServiceManager::~IServiceManager() { }
*/
  • IServiceManager:声明servicemanager进程提供的业务接口:getService、checkService、addService、listService,且内部通过宏IMPLEMENT_META_INTERFACE实现了函数IServiceManager::asInterface来将IBinder转换成BpServiceManager
  • BpServiceManager:继承BpInterface<IServiceManager>,又叫service manager的代理对象,可以向servicemanager进程(没有使用Service组件但是轮询从binder驱动程序中获取消息并处理)发起请求的能力;还实现了IServiceManager提供业务接口,remote()->transact把封装好的消息交给IPCThreadState发送给Binder驱动程序
  • sp<IServiceManager> defaultServiceManager():该函数被定义在frameworks/native/libs/binder库里面,即任何依赖了libbinder.so的进程都可以使用函数defaultServiceManager来获取IServiceManager接口
  • template<typename INTERFACE> status_t getService(const String16& name, sp<INTERFACE>* outService):该函数被定义在binder库的头文件IServiceManager.h中,因此任何依赖了该库的进程且#include<IServiceManager.h>都可以直接使用该函数来获取任意一个INTERFACE的服务例如getService("android.media.IMediaPlayer",sp<IMediaPlayer> *outService)获取IMediaPlayer接口

2、defaultServiceManager

根据上文我们会发现在IServiceManager.h定义了全局函数defaultServiceManager,该函数用来获取当前进程的IServiceManager接口,通过该接口就能实现服务的注册和查询等功能。代码如下:

//frameworks/native/libs/binder/IServiceManager.cpp
sp<IServiceManager> defaultServiceManager() {
    if (gDefaultServiceManager != NULL)    //单例模式
        return gDefaultServiceManager;
    else
    {
        AutoMutex _l(gDefaultServiceManagerLock);
        while (gDefaultServiceManager == NULL) {
            //获取service manager的代理接口,其实作了三步操作
            gDefaultServiceManager = interface_cast<IServiceManager>(
                ProcessState::self()->getContextObject(NULL));
            if (gDefaultServiceManager == NULL) sleep(1);
        }
    }
    return gDefaultServiceManager;
}

上面代码gDefaultServiceManager 是一个类型为IServiceManager的全局变量,指向进程内的service manager代理对象。其中10和11行代码包含了三次函数调用,就是这三次函数调用得到service manager代理对象。

1)、获取进程实例对象ProcessState

在binder库中也定义了一个类型为ProcessState全局变量gProcess,每个进程有且只有一个ProcessState进程状态实例对象,该对象为每个进程封装了自己的binder驱动的操作,因此如果某个进程想使用binder进行数据通信,那么就一定需要通过binder驱动程序来帮助完成,那么ProcessState就为进程封装了对binder驱动程序的一些通用操作。如下:

//frameworks/native/include/binder/ProcessState.h
class ProcessState : public virtual RefBase
{
public:
    static  sp<ProcessState>    self();
    sp<IBinder>         getContextObject(const sp<IBinder>& caller);
    sp<IBinder>         getContextObject(const String16& name, const sp<IBinder>& caller);
    sp<IBinder>         getStrongProxyForHandle(int32_t handle);
private:
    ProcessState(const char* driver);
    ~ProcessState();
    struct handle_entry {
        IBinder* binder;
        RefBase::weakref_type* refs;//通过弱引用重新封装binder代理对象
    };
    //根据句柄索引来获取binder代理对象
    handle_entry*       lookupHandleLocked(int32_t handle);
    //保存了所有的binder代理对象的容器
    Vector<handle_entry>mHandleToObject;
}
//frameworks/native/libs/binder/ProcessState.cpp
//单例模式 获取进程唯一ProcessState对象
sp<ProcessState> ProcessState::self()
{
    Mutex::Autolock _l(gProcessMutex);
    if (gProcess != NULL) return gProcess;
    gProcess = new ProcessState("/dev/binder");
    return gProcess;
}
//构造函数
ProcessState::ProcessState(const char *driver)
    : mDriverName(String8(driver))
    , mDriverFD(open_driver(driver)) //显示调用open_driver打开binder驱动程序
    , mVMStart(MAP_FAILED)
{
    if (mDriverFD >= 0) { //调用binder驱动mmap函数进行内存映射
        mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
        if (mVMStart == MAP_FAILED) {
            close(mDriverFD);
            mDriverFD = -1;
            mDriverName.clear();
        }
    }
}
ProcessState::~ProcessState()
{
    if (mDriverFD >= 0) { //析构函数需要munmap函数释放内存资源
        if (mVMStart != MAP_FAILED) munmap(mVMStart, BINDER_VM_SIZE);
        close(mDriverFD); //关闭驱动程序
    }
    mDriverFD = -1;
}
static int open_driver(const char *driver)
{
    //打开binder驱动程序
    int fd = open(driver, O_RDWR | O_CLOEXEC);
    if (fd >= 0) {
        int vers = 0;
        //获取校验binder驱动程序版本号
        status_t result = ioctl(fd, BINDER_VERSION, &vers);
        if (result == -1) {
            close(fd);
            fd = -1;
        }
        if (result != 0 || vers != BINDER_CURRENT_PROTOCOL_VERSION) {
            close(fd);
            fd = -1;
        }
        //向binder驱动程序设置最大线程数量
        size_t maxThreads = DEFAULT_MAX_BINDER_THREADS;
        result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads);
    }
    return fd;
}

由上面不难发现,ProcessState::self()如果首次调用就为当前进程创建了ProcessState对象,该对象在构造函数中完成了对binder驱动程序进行初始化和内存映射的操作,这样后续才能利用binder驱动程序来进行数据通信。

2)、创建进程间通信代理对象BpBinder

当获取到ProcessState对象后,那么就可以利用ProcessState对象的getContextObject(NULL)来获取service manager代理对象。其实getContextObject函数可以用来获取任何的service组件,这里NULL作为参数,其实内部实现就是用来获取上下文管理者service manager的代理对象。如下:

//frameworks/native/libs/binder/ProcessState.cpp
sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& /*caller*/)
{
    return getStrongProxyForHandle(0);
}
//从容器中获取句柄handle对应的handle_entry节点
//从上节知道handle_entry对binder代理对象进行弱引用的封装
ProcessState::handle_entry* ProcessState::lookupHandleLocked(int32_t handle)
{
    const size_t N=mHandleToObject.size();
    if (N <= (size_t)handle) {
        handle_entry e;
        e.binder = NULL;
        e.refs = NULL;
        //handle句柄值其实也对应于所在容器的索引值
        status_t err = mHandleToObject.insertAt(e, N, handle+1-N);
        if (err < NO_ERROR) return NULL;
    }
    return &mHandleToObject.editItemAt(handle);
}
sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{
    sp<IBinder> result;
    AutoMutex _l(mLock);
    //从mHandleToObject容器中获取handle对应的binder代理对象
    handle_entry* e = lookupHandleLocked(handle);
    //获取失败就进行创建
    if (e != NULL) {
        IBinder* b = e->binder;
        if (b == NULL || !e->refs->attemptIncWeak(this)) {
            if (handle == 0) {
                Parcel data;
                status_t status = IPCThreadState::self()->transact(
                        0, IBinder::PING_TRANSACTION, data, NULL, 0);
                if (status == DEAD_OBJECT)
                   return NULL;
            }
            //创建BpBinder,其句柄根据参数传递进来是0
            b = BpBinder::create(handle);
            //将新创建的节点添加到mHandleToObject容器中
            e->binder = b;
            if (b) e->refs = b->getWeakRefs();
            result = b;
        } else {
            result.force_set(b);
            e->refs->decWeak(this);
        }
    }
    return result;
}

上面代码完成了获取指定句柄值handle的binder代理对象。在binder库中封装的接口可以知道,进行binder通信的参与者service组件和client组件,他们往往使用同一个句柄值来表示他们的对应关系,例如service manager进程对应的代理对象(client组件)的句柄值被认定为0,自定义某个service组件的句柄值指定为10,那么其对应的client组件代理对象的句柄值也需要指定为10(后文详细介绍)。该处创建并返回了一个句柄值为0的BpBinder代理对象,第一章已经知道BpInterface继承BpRefBase,并持有IBinder类型的变量mRemote。针对于ServiceManager的客户端来说,这里的mRemote其实就是这里通过静态函数BpBinder::create(0)创建出来的。即函数ProcessState::self()->getContextObject(NULL)返回值是IBinder类型,其实这里返回的是IBinder的子类实例对象BpBinder回去,且该BpBinder的句柄值为0

3)、转换代理对象为BpServiceManager

上文已经得到了一个IBinder类型的对象,但它的真实身份是一个BpBinder,回过头会发现拿到这个对象后通过interface_cast进行转换一次,其实在第一章第四节就已经介绍了interface_cast是一个模板函数,用来将一个IBinder对象转换成<INTERFACE>类型的对象,即此处就是将上文得到的BpBinder(0)转换成IServiceManager类型,即调用了IServiceManager::asInterface函数,其中参数obj就是BpBinder(0),如下:

//frameworks/native/libs/binder/include/binder/IInterface.h
//参数为IBinder返回值是一个INTERFACE
template<typename INTERFACE>
inline sp<INTERFACE> interface_cast(const sp<IBinder>& obj)
{
    return INTERFACE::asInterface(obj);
}
/**
用法:
1:例如INTERFACE为Ixxxx,该模板函数扩展后相当于Ixxxx.asInterface(obj)
2:例如INTERFACE为IServiceManager,该模板函数如下:
inline sp<IServiceManager> interface_cast(const sp<IBinder>& obj){
    return IServiceManager::asInterface(obj);
}

同样第一章已经介绍了该函数的实现其实通过宏IMPLEMENT_META_INTERFACE来完成的,该宏展开其实创建了new BpServiceManager(BpBinder(0)),并将其返回。如下代码:

//frameworks/native/libs/binder/IServiceManager.cpp
//使用了宏IMPLEMENT_META_INTERFACE,其中参数分别是ServiceManager和"android.os.IServiceManager"
IMPLEMENT_META_INTERFACE(ServiceManager, "android.os.IServiceManager");
//展开如下:
const ::android::String16 IServiceManager::descriptor("android.os.IServiceManager");
const ::android::String16& IServiceManager::getInterfaceDescriptor() const {
    return IServiceManager::descriptor;
}
//参数obj就是BpBinder(0)
::android::sp<IServiceManager> IServiceManager::asInterface(const::android::sp<::android::IBinder>& obj) {
    ::android::sp<IServiceManager> intr;
    if (obj != NULL) {
        intr = static_cast<IServiceManager*>(obj->queryLocalInterface(IServiceManager::descriptor).get());
        if (intr == NULL) {
            intr = new BpServiceManager(obj);
        }
    }
    return intr;
}
IServiceManager::IServiceManager() { }
IServiceManager::~IServiceManager() { }

4)、总结

到此为止,defaultServiceManager的返回值虽然是IServiceManager类型,但我们调用该函数后得到的是BpServiceManager类型的实例,根据第五章的内容,我们可以得出结论:BpServcieManager作为service manager进程的Client组件,除了实现业务接口提供的几个功能之外,还持有从BpRefBase继承过来mRemote(IBinder类型),它的本质其实就是第二小节创建的BpBinder,通过它就能够向IPCThreadState及binder驱动程序发送消息。其关系图如下:

我们现在知道了可以通过binder库里面的全局函数defaultServiceManager来获取当前用户进程的IServiceManager对象,它的本质是BpServiceManager,内部持有了mRemote成员变量,而mRemote的本质是一个BpBinder(0),众所周知整个android系统句柄值为0的服务端进程只有一个,那就进程servicemanager,它作为类似路由器的存在,管理了所有的binder进程,任何一个服务端进程想为系统提供服务,那么就需要向servicemanager进程注册自己;任何一个客户端进程想要完成某件事,那么就需要向servicemanager进程查询某个服务;但是他们并不能直接通信,根据第五章的知识知道,只要获取到了对应的BpBinder,就能与其进行通信,它们的句柄就是0。因此可以这样理解,得BpBinder(0)就能得servicemanager,结合上文defaultServiceManager函数正好具有这种能力。

三、Binder通信服务端进程

现在我们可以通过函数defaultServiceManager获取当前进程的IServiceManager,其本质是BpServiceManager,即是service manager进程的客户端组件。其实在Android系统框架中,除了service manager进程之外的所有进程都可以看出是它的客户端进程,因为除了service manager之外的所有用户进程都需要service manager为它们提供服务注册和服务查询的功能,不然它们就只能自己玩自己的例如进程1提供了一个系统服务,但是没有告诉service manager,那么现在进程2需要这个服务,但是自己不知道系统中有谁提供了该服务,但它知道江湖百晓生service manager什么都懂,于是就去问了他,结果service manager也不知道谁提供了该服务,那么进程1是不是就跟没有出山的诸葛亮一样,才华横溢却没人知道,这对于整个android系统生态都是不友好的。

1、进程mediaserver

上图为mediaserver进程的源码路径,那么服务端进程应该如何向service manager进程进行服务注册呢?

  • 编译脚本如下:
#frameworks/av/media/mediaserver/Android.mk
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
#该进程源文件:main_mediaserver.cpp
LOCAL_SRC_FILES:= \
        main_mediaserver.cpp
LOCAL_SHARED_LIBRARIES := \
        libresourcemanagerservice \
        liblog \
        libmediaplayerservice \
        libutils \
        libbinder \
        libicuuc \
        [email protected] \
LOCAL_STATIC_LIBRARIES := \
        libicuandroid_utils \
        libregistermsext
LOCAL_C_INCLUDES := \
        frameworks/av/media/libmediaplayerservice \
        frameworks/av/services/mediaresourcemanager \
#该进程名:mediaserver
LOCAL_MODULE:= mediaserver
LOCAL_32_BIT_ONLY := true
#该进程启动配置:mediaserver.rc
LOCAL_INIT_RC := mediaserver.rc
LOCAL_CFLAGS := -Werror -Wall
include $(BUILD_EXECUTABLE)
  • 启动脚本,上面的mk文件指定了mediaserver进程的源文件和启动rc文件,mediaserver.rc如下:
service media /system/bin/mediaserver
    class main
    user media
    group audio camera inet net_bt net_bt_admin net_bw_acct drmrpc mediadrm
    ioprio rt 4
    writepid /dev/cpuset/foreground/tasks /dev/stune/foreground/tasks

熟悉init进程的同学应该知道在系统启动的init阶段,init进程将解析rc文件中的service,这里解析出来就会forck进程出来用来执行/system/bin/mediaserver的代码,该进程就是mediaserver进程。我们继续看看该进程的main函数,如下代码:

//frameworks/av/media/mediaserver/main_mediaserver.cpp
#include <binder/IPCThreadState.h>
#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
#include "RegisterExtensions.h"
#include "MediaPlayerService.h"
#include "ResourceManagerService.h"
using namespace android;
int main(int argc __unused, char **argv __unused)
{
    signal(SIGPIPE, SIG_IGN);
    sp<ProcessState> proc(ProcessState::self());
    //调用defaultServiceManager获取IServiceManager接口
    sp<IServiceManager> sm(defaultServiceManager());
    InitializeIcuOrDie();
    //初始化service组件:MediaPlayerService和ResourceManagerService
    MediaPlayerService::instantiate();
    ResourceManagerService::instantiate();
    registerExtensions();
    //开启binder线程池进行轮询
    ProcessState::self()->startThreadPool();
    IPCThreadState::self()->joinThreadPool();
}

该进程的main函数很简单,初始化了两个service组件之后就通过startThreadPool和joinThreadPool进入轮询模式,即将创建线程PoolThread,该线程轮询从缓冲区中获取来自binder驱动程序的消息,详情可参考第一章第2节。

2、服务注册

mediaserver进程的main函数调用了instantiate来分别对MediaPlayerService和ResourceManagerService进行初始化,其实这里的MediaPlayerService和ResourceManagerService都是第五章讲解的service组件,他们都继承于BnXXX,这里我们简单分析一下MediaPlayerService。其代码如下:

//frameworks/av/media/libmediaplayerservice/MediaPlayerService.h
class MediaPlayerService : public BnMediaPlayerService
{
public:
    static  void                instantiate();
    //...省略...
                            MediaPlayerService();
    virtual                 ~MediaPlayerService();
} // MediaPlayerService 
//frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp
//初始化MediaPlayerService
void MediaPlayerService::instantiate() {
    defaultServiceManager()->addService(
            String16("media.player"), new MediaPlayerService());
}
//构造函数
MediaPlayerService::MediaPlayerService() {
    ALOGV("MediaPlayerService created");
    mNextConnId = 1;
    MediaPlayerFactory::registerBuiltinFactories();
}
//析构函数
MediaPlayerService::~MediaPlayerService() {
    ALOGV("MediaPlayerService destroyed");
}

进程mediaserver调用MediaPlayerService::instantiate()函数实现服务注册,在该函数中通defaultServiceManager函数获得到当前进程的IServiceManager接口,紧接着就调用了它的addService函数进行服务注册,即这里的IServiceManager它的本质其实是BpServiceManager对象,该对象的addService函数其实是通过BpBinder(0)发送ADD_SERVICE_TRANSACTION消息给binder驱动程序,最终驱动程序将消息透传到service manager进程的缓存中,在轮询的时候将消息提取出来并进行服务注册,详情流程请阅读第四章。

要注意的是IServiceManager的addService函数,其参数是IBinder类型,但上面我们传递的是MediaPlayerService,根据第五章的binder库家谱图里面的BnXXX派生出的业务类XXXX,这里的MediaPlayerService继承于BnMediaPlayerService,所以也可以把它看成一个IBinder如下定义:

//frameworks/native/libs/binder/IServiceManager.cpp
class BpServiceManager : public BpInterface<IServiceManager>
{
public:
    //参数2往往是BnXXX类型子类,参数3和参数4都有默认值
    virtual status_t addService(const String16& name, const sp<IBinder>& service, bool allowIsolated, int dumpsysPriority) {
        Parcel data, reply;
        data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
        data.writeString16(name);
        data.writeStrongBinder(service);
        data.writeInt32(allowIsolated ? 1 : 0);
        data.writeInt32(dumpsysPriority);
        status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);
        return err == NO_ERROR ? reply.readExceptionCode() : err;
    }
}

综上所述,服务的注册往往不是注册一个BnXXX,BnXXX只是android框架层为我们封装Binder通信库的基类接口,在实际使用中,我们只需要自定义一个继承于BnXXX的类,因为它就表示我们具体服务的实现,我们只需要实现我们的业务逻辑就行了,不用再去考虑那繁琐复杂的通信流程。

3、实现具体业务

目前为止,我们的脑海肯定有这样一个概念,客户端进程持有client组件(即BpXXX),它的每一个接口都会向服务端进程持有的service组件(即BnXXX)发送消息请求,该BnXXX只需要解析命令功能码,并调用对应的函数得到结果,并把这个结果返回给BpXXX,这就是一个完整的binder通信过程。不信?我们可以来看看BnMediaPlayerService和BpMediaPlayerService的源码,IMediaPlayerService代码如下:

//frameworks/av/media/libmedia/IMediaPlayerService.cpp
class BpMediaPlayerService: public BpInterface<IMediaPlayerService>
{
public:
    BpMediaPlayerService(const sp<IBinder>& impl)  : BpInterface<IMediaPlayerService>(impl) {    }
    virtual sp<IMediaMetadataRetriever> createMetadataRetriever() {
        Parcel data, reply;
        data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor());
        //向BnMediaPlayerService发送消息CREATE_METADATA_RETRIEVER
        remote()->transact(CREATE_METADATA_RETRIEVER, data, &reply);
        return interface_cast<IMediaMetadataRetriever>(reply.readStrongBinder());
    }
    virtual sp<IMediaPlayer> create(const sp<IMediaPlayerClient>& client, audio_session_t audioSessionId) {
        Parcel data, reply;
        data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor());
        data.writeStrongBinder(IInterface::asBinder(client));
        data.writeInt32(audioSessionId);
        //向BnMediaPlayerService发送消息CREATE
        remote()->transact(CREATE, data, &reply);
        return interface_cast<IMediaPlayer>(reply.readStrongBinder());
    }
    virtual sp<IMediaRecorder> createMediaRecorder(const String16 &opPackageName)  {
        Parcel data, reply;
        data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor());
        data.writeString16(opPackageName);
        //向BnMediaPlayerService发送消息CREATE_MEDIA_RECORDER
        remote()->transact(CREATE_MEDIA_RECORDER, data, &reply);
        return interface_cast<IMediaRecorder>(reply.readStrongBinder());
    }
};
status_t BnMediaPlayerService::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
    switch (code) {
        case CREATE: {
            CHECK_INTERFACE(IMediaPlayerService, data, reply);
            sp<IMediaPlayerClient> client = interface_cast<IMediaPlayerClient>(data.readStrongBinder());
            audio_session_t audioSessionId = (audio_session_t) data.readInt32();
            //BnMediaPlayerService收到CREATE消息后调用了函数create
            sp<IMediaPlayer> player = create(client, audioSessionId);
            reply->writeStrongBinder(IInterface::asBinder(player));
            return NO_ERROR;
        } break;
        case CREATE_MEDIA_RECORDER: {
            CHECK_INTERFACE(IMediaPlayerService, data, reply);
            const String16 opPackageName = data.readString16();
            //BnMediaPlayerService收到CREATE_MEDIA_RECORDER消息后调用了函数createMediaRecorder
            sp<IMediaRecorder> recorder = createMediaRecorder(opPackageName);
            reply->writeStrongBinder(IInterface::asBinder(recorder));
            return NO_ERROR;
        } break;
        case CREATE_METADATA_RETRIEVER: {
            CHECK_INTERFACE(IMediaPlayerService, data, reply);
            //BnMediaPlayerService收到CREATE_METADATA_RETRIEVER消息后调用了函数createMetadataRetriever
            sp<IMediaMetadataRetriever> retriever = createMetadataRetriever();
            reply->writeStrongBinder(IInterface::asBinder(retriever));
            return NO_ERROR;
        } break;
        default:
            return BBinder::onTransact(code, data, reply, flags);
    }
}

你们知道BnMediaPlayerService::onTransact函数中解析来自BpMediaPlayerService的命令具体怎么处理的吗?没错调用对应的函数,这些函数的具体实现是什么?这个时候你可能懵逼了,因为你根本找不到他们的实现,因为这些函数都是虚函数!!

实即BnMediaPlayerService里面调用的create、createMediaRecorder、createMetadataRetriever在IMediaPlayerService里面根本没有实现,他们的具体实现在其子类上面。

还记得上一节讲解服务注册的时候,注册的是MediaPlayerService没错,MediaPlayerService就是BnMediaPlayerService的子类,因此上面几个函数的具体实现就在它的身上,熟悉多态的同学应该很容易理解。代码如下:

//frameworks/av/media/libmediaplayerservice/MediaPlayerService.h
class MediaPlayerService : public BnMediaPlayerService
{
public:
    //创建一个录音器客户端
    virtual sp<IMediaRecorder>  createMediaRecorder(const String16 &opPackageName);
    //删除一个录音器客户端
    void    removeMediaRecorderClient(const wp<MediaRecorderClient>& client);
    //创建一个播放器客户端
    virtual sp<IMediaPlayer>    create(const sp<IMediaPlayerClient>& client, audio_session_t audioSessionId);
    //删除一个播放器客户端
            void                removeClient(const wp<Client>& client);
                                MediaPlayerService();
    virtual                     ~MediaPlayerService();
    //成员变量mClients,存储了系统当前所有正在工作的媒体播放器
                SortedVector< wp<Client> >  mClients;
    //成员变量mMediaRecorderClients,存储了系统当前所有正在工作的媒体录音器
                SortedVector< wp<MediaRecorderClient> > mMediaRecorderClients;
} // MediaPlayerService
//frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp
//createMediaRecorder的具体实现
sp<IMediaRecorder> MediaPlayerService::createMediaRecorder(const String16 &opPackageName)  {
    pid_t pid = IPCThreadState::self()->getCallingPid();
    //创建一个录音器
    sp<MediaRecorderClient> recorder = new MediaRecorderClient(this, pid, opPackageName);
    wp<MediaRecorderClient> w = recorder;
    Mutex::Autolock lock(mLock);
    //将这个录音器加到队列中进行维护
    mMediaRecorderClients.add(w);
    return recorder;
}
void MediaPlayerService::removeMediaRecorderClient(const wp<MediaRecorderClient>& client)  {
    Mutex::Autolock lock(mLock);
    mMediaRecorderClients.remove(client); //删除一个录音器
}
//create的具体实现
sp<IMediaPlayer> MediaPlayerService::create(const sp<IMediaPlayerClient>& client, audio_session_t audioSessionId) {
    pid_t pid = IPCThreadState::self()->getCallingPid();
    int32_t connId = android_atomic_inc(&mNextConnId);
    //创建播放器,使用了包装模式进行创健
    sp<Client> c = new Client(this, pid, connId, client, audioSessionId, IPCThreadState::self()->getCallingUid());
    wp<Client> w = c;
    {
        Mutex::Autolock lock(mLock);
        mClients.add(w);    //将播放器添加到mClients
    }
    return c;
}
void MediaPlayerService::removeClient(const wp<Client>& client) {
    Mutex::Autolock lock(mLock);
    mClients.remove(client);    //从mClients中删除播放器
}

四、Binder通信客户端进程

前面介绍了服务端进程,它除了需要向service manager进程注册service组件之外,还需要开启轮询模式,循环从binder驱动程序中获取消息并解析,最终将任务分发给自己的BnXXX的子类,例如上面讲解的MediaPlayerService。

那么客户端进程是干什么的呢?它在需要让服务端进程帮他完成某件事情的时候,先需要向service manager进程查询服务得到对应的client组件,实际上该组件是BpXXX的子类,最终通过它向binder驱动程序发送消息。

1、服务查询

前面讲解的服务注册需要通过IServiceManager来完成,同样服务查询也需要它。通过defaultServiceManager函数获取出来的IServiceManager其实是个BpServiceManager对象,根据第五章binder库的学习已经明白调用BpXXX的业务接口其实就是在给BnXXX发送消息,最终BnXXX解析了这些消息并作了相应处理

IServiceManager.h除了定义全局函数defaultServiceManager来获取当前进程的IServiceManager之外,还定义了一个全局函数getService用来获取指定的IXXX(本质是BpXXX)接口,其代码如下:

//frameworks/native/libs/binder/IServiceManager.cpp
//定义模板INTERFACE:例如IAudioManager IMediaRecorder
template<typename INTERFACE>
//参数name:获取指定名字的IXXX接口:例如"android.media.IAudioService" "android.media.IMediaRecorder"
status_t getService(const String16& name, sp<INTERFACE>* outService) {
    //得到IServiceManager
    const sp<IServiceManager> sm = defaultServiceManager();
    if (sm != NULL) {
        //通过IServiceManager的getServcie方法获取到指定名字的IBinder(本质是BpBinder)
        //通过interface_cast将上面获取到的IBinder转换成IXXX
        *outService = interface_cast<INTERFACE>(sm->getService(name));
        if ((*outService) != NULL) return NO_ERROR;
    }
    return NAME_NOT_FOUND;
}

全局函数getService获取了IServiceManager,最后调用了它的getService,还记得第四章第四节吗,service manager进程在轮询binder驱动程序缓冲区的时候,通过binder_parse解析了驱动层通信的数据协议,如果是BR_TRANSACTION协议表示进程间数据传输,就将交给svcmgr_handler处理,在处理SVC_MGR_GET_SERVICE的时候,在本地维护的队列中查询到了对应的句柄handle,并将其封装成BINDER_TYPE_HANDLE类型的reply数据包交给了binder驱动程序,希望它能回到这儿,如下代码:

//frameworks/native/libs/binder/IServiceManager.cpp
class BpServiceManager : public BpInterface<IServiceManager>
{
public:
    virtual sp<IBinder> getService(const String16& name) const {
        sp<IBinder> svc = checkService(name);
        if (svc != NULL) return svc;
        //....vendor binder处理
    }
    virtual sp<IBinder> checkService( const String16& name) const {
        Parcel data, reply;
        data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
        data.writeString16(name);
        remote()->transact(CHECK_SERVICE_TRANSACTION, data, &reply);
        //android特有方法获取IBinder的强引用出来
        //其实servicemanager进程并只返回了handle过来,该函数内部作了些特殊处理
        return reply.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 unflatten_binder(ProcessState::self(), *this, val);
}
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->hdr.type) {
            case BINDER_TYPE_BINDER:
                *out = reinterpret_cast<IBinder*>(flat->cookie);
                return finish_unflatten_binder(NULL, *flat, in);
            case BINDER_TYPE_HANDLE:
                *out = proc->getStrongProxyForHandle(flat->handle);
                return finish_unflatten_binder(static_cast<BpBinder*>(out->get()), *flat, in);
        }
    }
    return BAD_TYPE;
}

service manager进程其实就返回了handle句柄值,但是IServiceManager却通过reply.readStrongBinder来读取出了IBinder接口?是不是很想不明白,经过大量搜索资料阅读源码,终于发现端倪,最终调用到了Parcel的unflatten_binder函数,原来Parcel不仅仅是数据的载体,还封装了很多功能性操作,例如这里将要介绍的获取binder返回数据。

unflatten_binder函数中将走BINDER_TYPE_HANDLE流程(不明白的可以返回第四章service_manager.c中的bio_put_ref函数),终于,我们又跟getStrongProxyForHandle见面了,还记得第五章第二节的内容吗?代码如下:

sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{
    sp<IBinder> result;
    AutoMutex _l(mLock);
    //从mHandleToObject容器中获取handle对应的binder代理对象
    handle_entry* e = lookupHandleLocked(handle);
    //获取失败就进行创建
    if (e != NULL) {
        IBinder* b = e->binder;
        if (b == NULL || !e->refs->attemptIncWeak(this)) {
            if (handle == 0) {
                Parcel data;
                status_t status = IPCThreadState::self()->transact(
                        0, IBinder::PING_TRANSACTION, data, NULL, 0);
                if (status == DEAD_OBJECT)
                   return NULL;
            }
            //创建BpBinder,其句柄根据参数传递进来是0
            b = BpBinder::create(handle);
            //将新创建的节点添加到mHandleToObject容器中
            e->binder = b;
            if (b) e->refs = b->getWeakRefs();
            result = b;
        } else {
            result.force_set(b);
            e->refs->decWeak(this);
        }
    }
    return result;
}

现在是不是可以总结一下了,调用getService全局函数,BpServiceManager将向service manager进程发送获取服务的消息,该进程在处理该消息的时候会从本地维护的队列中找到指定名称的服务,并得到对应的handle句柄值,最终将其包装成BINDER_TYPE_HANDLE消息返回给BpServiceManagerBpServiceManager通过Parcel来得到一个BpBinder(handle),得到BpBinder的方法其实就只有一个,千调万调还是调了getStrongProxyForHandle最后通过interface_cast<IXXX>将这个BpBinder强制转换成一个BpXXX,通过第五章的内容可以知道这个过程其实是new BpXXX(BpBinder(handle))装饰模式来实现的

2、自定义媒体播放进程

下面自定义个进程来实现媒体播放的功能,因为进程mediaserver提供了media.player的功能,因此可以认为它作为了服务端进程,而现在我们自定义的进程就是客户端进程。当然首先也需要获取对应的IXXX接口(服务查询),才能与其进行通信。该进程main代码如下:

int main(int argc __unused, char **argv __unused) {
    sp<ProcessState> proc(ProcessState::self());
    ProcessState::self()->startThreadPool();
    //获取IServiceManager接口,以便进行服务查询
    sp<IServiceManager> sm = defaultServiceManager();
    //服务查询,从service manager进程中得到了描述"media.player"的句柄
    //sm->getServic获取出来的对象类型实际上是BpBinder(handle)
    sp<IBinder> binder = sm->getService(String16("media.player"));
    //需要将其转换成对应IXXX接口,使用了装饰模式
    //interface_cast转换后的对象类型实际上是BpXXX,这里就是BpMediaPlayerService
    sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder);
    sp<ShenClient> client = new ShenClient();
    //调用IMediaPlayerService接口创建播放器 IMediaPlayer是个匿名service
    sp<IMediaPlayer> player = service->create(client, AUDIO_SESSION_ALLOW);
    //为这个播放器设置数据源
    player->setDataSource(fd, 0, 0x7fffffffLL);
    //为这个播放器设置视频边带 参数为其他图层的缓冲buffer
    player->setVideoSurfaceTexture(bufferProducer);
    //为这个播放器准备工作
    player->prepareAsynce();
    //等待客户端接收播放器准备好的消息
    client->waitForPrepared();
    //让这个播放器开始工作
    player->start();
    //等待第一帧
    client->waitForFirstFrame();
    //等待播放完成
    while(true) {  /*.......*/ }
    //停止客户端
    client->ForceStop();
    //停止播放器
    player->stop();
    //销毁播放器
    player->disconnect();
}
class ShenClient : puiblic BnMediaPlayerClient {
    void waitForPrepared() {
        //...阻塞直到收到MEDIA_PREPARED消息后唤醒返回
    }
    void waitForFirstFrame() {
        //...阻塞直到收到MEDIA_INFO里面第一帧消息后唤醒返回
    }
    void notify(int msg, int ext, int ext2, const Parcel *obj) {
        switch(msg) {
            case MEDIA_ERROR:    //播放器出错    break;
            case MEDIA_PREPARED: //播放器准备好  break;
            case MEDIA_PLAYBACK_COMPLETE: //播放结束 break;
            case MEDIA_INFO:
        }
    }
}

根据上面的例子可以总结一下客户端进程步骤如下:

  • 得到IServiceManager:defaultServiceManager函数获取当前进程的IServiceManager
  • 得到IBinder:sm->getService(String16("XXX"))函数获取指定名字的服务IBinder接口
  • 得到IXXXinterface_cast<IXXX>(binder)函数将上面的IBinder接口转换成IXXX
  • 通过IXXX向BnXXX发送消息:IXXX->xxx()调用其中的函数向服务端进程发起请求

3、匿名Service

从上面例子我们了解到了Binder库使用的基本模式,但有没有注意到一个问题?在使用media.player服务的时候,我们并没有直接通过IMediaPlayerService接口来实现视频播放,而是通过IMediaPlayerService->create(...)创建了一个IMediaPlayer接口,然后用IMediaPlayer提供的接口来实现视频播放。

1)、IMediaPlayer的定义

我们来看看IMediaPlayer到底是什么呢?代码如下:

//frameworks/av/include/media/IMediaPlayer.h
class IMediaPlayer: public IInterface
{
public:
    DECLARE_META_INTERFACE(MediaPlayer);
    virtual void            disconnect() = 0;
    virtual status_t        setDataSource(int fd, int64_t offset, int64_t length) = 0;
    virtual status_t        prepareAsync() = 0;
    virtual status_t        start() = 0;
    virtual status_t        stop() = 0;
    virtual status_t        pause() = 0;
    //....
};
class BnMediaPlayer: public BnInterface<IMediaPlayer>
{
public:
    virtual status_t    onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0);
};
//frameworks/av/media/libmedia/IMediaPlayer.cpp
class BpMediaPlayer: public BpInterface<IMediaPlayer>
{
public:
    BpMediaPlayer(const sp<IBinder>& impl) : BpInterface<IMediaPlayer>(impl) { }
    void disconnect() {
        Parcel data, reply;
        data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
        remote()->transact(DISCONNECT, data, &reply);
    }
    status_t prepareAsync() {
        Parcel data, reply;
        data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
        remote()->transact(PREPARE_ASYNC, data, &reply);
        return reply.readInt32();
    }
    status_t start() {
        Parcel data, reply;
        data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
        remote()->transact(START, data, &reply);
        return reply.readInt32();
    }
    status_t stop() {
        Parcel data, reply;
        data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
        remote()->transact(STOP, data, &reply);
        return reply.readInt32();
    }
    status_t invoke(const Parcel& request, Parcel *reply) {
        return remote()->transact(INVOKE, request, reply);
    }
};
IMPLEMENT_META_INTERFACE(MediaPlayer, "android.media.IMediaPlayer");
status_t BnMediaPlayer::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
    switch (code) {
        case DISCONNECT: {
            CHECK_INTERFACE(IMediaPlayer, data, reply);
            disconnect();
            return NO_ERROR;
        } break;
        case PREPARE_ASYNC: {
            CHECK_INTERFACE(IMediaPlayer, data, reply);
            reply->writeInt32(prepareAsync());
            return NO_ERROR;
        } break;
        case START: {
            CHECK_INTERFACE(IMediaPlayer, data, reply);
            reply->writeInt32(start());
            return NO_ERROR;
        } break;
        case STOP: {
            CHECK_INTERFACE(IMediaPlayer, data, reply);
            reply->writeInt32(stop());
            return NO_ERROR;
        } break;
    }
}

IMediaPlayer的定义可以看出来其实就是我们前面讲到的服务,但是这个服务并没有注册到service manager进程中,但是它确实是C/S架构模式来进行通信的。对于这样没有注册到service manager进程中的服务我们叫做匿名服务

2)、IMediaPlayer的创建

接下来我们看看IMediaPlayer是如何被创建?IMediaPlayerService.cpp代码如下:

//frameworks/av/include/media/IMediaPlayerService.h
class IMediaPlayerService: public IInterface
{
public:
    DECLARE_META_INTERFACE(MediaPlayerService);
    //定义函数create:通过Bn
    virtual sp<IMediaPlayer> create(const sp<IMediaPlayerClient>& client, audio_session_t audioSessionId = AUDIO_SESSION_ALLOCATE) = 0;
};
class BnMediaPlayerService: public BnInterface<IMediaPlayerService>
{
public:
    virtual status_t    onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0);
};
//frameworks/av/media/libmedia/IMediaPlayerService.cpp
class BpMediaPlayerService: public BpInterface<IMediaPlayerService>
{
    virtual sp<IMediaPlayer> create( const sp<IMediaPlayerClient>& client, audio_session_t audioSessionId) {
        Parcel data, reply;
        data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor());
        data.writeStrongBinder(IInterface::asBinder(client));
        data.writeInt32(audioSessionId);
        //向BnMediaPlayerService发送CREATE消息
        remote()->transact(CREATE, data, &reply);
        //readStrongBinder获取服务端进程返回的IBinder引用
        return interface_cast<IMediaPlayer>(reply.readStrongBinder());
    }
};
status_t BnMediaPlayerService::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
    switch (code) {
        case CREATE: {
            CHECK_INTERFACE(IMediaPlayerService, data, reply);
            sp<IMediaPlayerClient> client = interface_cast<IMediaPlayerClient>(data.readStrongBinder());
            audio_session_t audioSessionId = (audio_session_t) data.readInt32();
            //调用虚函数create创建一个IMediaPlayer出来
            sp<IMediaPlayer> player = create(client, audioSessionId);
            //将IMediaPlayer引用返回给客户端进程
            reply->writeStrongBinder(IInterface::asBinder(player));
            return NO_ERROR;
        } break;
    }
}

BnMediaPlayerService在onTransact函数中解析了CREATE消息,从上面的代码可以看出来这个匿名服务的使用有两个步骤:

  • 创建BnMediaPlayer:通过create函数实例化了一个BnMediaPlayer
  • 生成BpMediaPlayer:reply->writeStrongBinder(IInterface::asBinder(player))这句代码很重要,详解下文

3)、BnMediaPlayer的创建

服务端进程实现具体业务小节已经介绍了,create函数是个虚函数,具体实现在MediaPlayerService里面,在该函数中实例化了一个Client内部类,其继承于BnMediaPlayer,哈哈哈,就这样创建了一个BnMediaPlayer。代码如下:

//frameworks/av/media/libmediaplayerservice/MediaPlayerService.h
class MediaPlayerService : public BnMediaPlayerService
{
    //....
    class Client : public BnMediaPlayer {
        virtual void            disconnect();
        virtual status_t        prepareAsync();
        virtual status_t        start();
        virtual status_t        stop();
        //....
    }
}
//frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp
sp<IMediaPlayer> MediaPlayerService::create(const sp<IMediaPlayerClient>& client, audio_session_t audioSessionId) {
    pid_t pid = IPCThreadState::self()->getCallingPid();
    int32_t connId = android_atomic_inc(&mNextConnId);
    sp<Client> c = new Client( this, pid, connId, client, audioSessionId, IPCThreadState::self()->getCallingUid());
    wp<Client> w = c;
    {
        Mutex::Autolock lock(mLock);
        mClients.add(w);
    }
    return c;
}

4)、BpMediaPlayer的生成

BnMediaPlayerService在解析CREATE消息的时候已经创建了一个BnMediaPlayer,那现在如何把这个service组件作为返回数据交给驱动程序呢?

 reply->writeStrongBinder(IInterface::asBinder(player));

reply貌似将这种Binder数据当成了一种特殊数据处理,我们来看看writeStrongBinder函数:

//frameworks/native/libs/binder/Parcel.cpp
status_t Parcel::writeStrongBinder(const sp<IBinder>& val)
{
    return flatten_binder(ProcessState::self(), val, this);
}
status_t flatten_binder(const sp<ProcessState>& /*proc*/, const sp<IBinder>& binder, Parcel* out) {
    flat_binder_object obj;
    if (binder != NULL) {
        IBinder *local = binder->localBinder();
        if (!local) {
            //传递进来的参数就是上文实例化的BnMediaPlayer
            BpBinder *proxy = binder->remoteBinder();
            if (proxy == NULL) ALOGE("null proxy");
            //将其转换成BpBinder就是为了获取handle句柄
            const int32_t handle = proxy ? proxy->handle() : 0;
            //子消息BINDER_TYPE_HANDLE:告诉驱动程序后面的跟的数据很特殊是个句柄值
            obj.hdr.type = BINDER_TYPE_HANDLE;
            obj.binder = 0;
            obj.handle = handle;    //BnMediaPlayer对应的句柄值
            obj.cookie = 0;
        } else {
            obj.hdr.type = BINDER_TYPE_BINDER;
            obj.binder = reinterpret_cast<uintptr_t>(local->getWeakRefs());
            obj.cookie = reinterpret_cast<uintptr_t>(local);
        }
    } else {
        obj.hdr.type = BINDER_TYPE_BINDER;
        obj.binder = 0;
        obj.cookie = 0;
    }
    return finish_flatten_binder(binder, obj, out);
}

还记得第四章第七节吗?binder驱动程序在解析BINDER_TYPE_HANDLE数据包的时候,根据里面的句柄handle值创建了引用对象binder_ref,还将其的生命周期进行了处理,最终添加到维护的红黑树中。服务注册在驱动程序里面的流程貌似也是这样子的,因此在返回BINDER_TYPE_HANDLE数据包的过程是不是顺便完成了匿名服务的注册

我们在看看BpMediaPlayerService又是怎么来读取这个binder的呢?

return interface_cast<IMediaPlayer>(reply.readStrongBinder())

reply在读写binder类型的数据时候,确实做了特殊处理,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 unflatten_binder(ProcessState::self(), *this, val);
}
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->hdr.type) {
            case BINDER_TYPE_BINDER:
                *out = reinterpret_cast<IBinder*>(flat->cookie);
                return finish_unflatten_binder(NULL, *flat, in);
            case BINDER_TYPE_HANDLE:
                //通过getStrongProxyForHandle(handle)来创建BpBinder(handle)
                *out = proc->getStrongProxyForHandle(flat->handle);
                return finish_unflatten_binder(static_cast<BpBinder*>(out->get()), *flat, in);
        }
    }
    return BAD_TYPE;
}

reply在写binder类型的数据,并不是将binder的引用传递给驱动程序,而只传递了句柄handle值,reply在读binder类型的数据,也就只从驱动程序中读了一个handle句柄值出来,最后通过函数getStrongProxyForHandle(已经介绍了几遍了,这里就不想啰嗦了,不清楚的可以仔细阅读前文)生成一个BpBinder(handle)

这个过程是不是没有通过service manager来获得,我们拿到的这个handle是不是在service manager进程查询不到。匿名service的偷天换日的避开了service manager的管控,直接通过接口create把handle值传递了过来。我们可以这样理解,通过service manager查询到了媒体总管服务IMediaPlayerService,但是它不能直接提供播放器的功能,也不能直接提供录音器的功能,但是它能够管理这些服务,我们可以很方便的通过它提供的接口来获取一个IMediaPlayer播放器服务或IMediaRecorder录音器服务(只需要把对应的handle句柄告诉我就行了),既然这么简单的得到handle值,为什么还要劳驾service manager告诉我handle呢?这样合理利用了系统资源,service manager作为系统"最高指挥官"管理了比较重要的服务,这些服务自己管理自己的小弟,身处什么位置做什么事,岂不快哉。

五、自定义Binder进程间通信示例

到目前为止,基本上已经了解了Binder进程间通信框架库,除此之外还跟踪了哈Android OS里面的service manager进程和mediaservice的源码实现。本章我们来自己实现一个Binder进程间通信应用实例。

Binder进程间通信基于C/S模式,因此将该应用实例划分为三个模块:

  • common:公共模块,基于binder库定义业务接口和BnXXX、BpXXX
  • server:服务端进程,提供服务且实现具体的业务逻辑
  • client:客户端进程,模拟访问服务端进程的接口
----common
	---- IFregService.h   : Binder本地对象类BnFregService  Binder代理对象类BpFregService
	---- IFregService.cpp    : 硬件访问服务接口
----server
	---- FregServer.cpp   : Server进程
	---- Android.mk
----client
	---- FregClient.cpp   : Client 进程
	---- Android.mk

1、Binder库重要组件的定义

第一章介绍的binder进程间通信库,除了对binder驱动程序的封装,还实现了业务逻辑与驱动程序的分离。因此在实现基于binder进程间通信的实例首先要做的就是仿造MediaPlayerService一样,定义IXXX、BnXXX、BpXXX,当然还有两个很重要的宏DECLARE_META_INTERFACE和IMPLEMENT_META_INTERFACE来简化我们的代码。

//common/IFregService.h
#ifndef IFREGSERVICE_H_
#define IFREGSERVICE_H_
#include <utils/RefBase.h>
#include <binder/IInterface.h>
#include <binder/Parcel.h>
//定义当前服务的名称
#define FREG_SERVICE "hr.ma.FregService"
using namespace android;
//定义业务逻接口
class IFregService: public IInterface
{
public:
    //宏DECLARE_META_INTERFACE帮我们定义了几个重要函数
	DECLARE_META_INTERFACE(FregService);
	virtual int32_t getVal() = 0;
	virtual void setVal(int32_t val) = 0;
};
//定义BnXXX
class BnFregService: public BnInterface<IFregService>
{
public:
    //成员函数onTransact是虚函数,它是由BBinder的子类,即Binder本地对象类来实现的,他负责分发与业务相关的进程间通信请求。事实上,与业务相关的进程间通信请求是由Binder本地对象类的子类,即Service组件来负责处理的
	virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0);
};
#endif
//common/IFregService.cpp
#define LOG_TAG "IFregService"
#include <utils/Log.h>
#include <IFregService.h>
using namespace android;
//定义通信消息的功能码
enum {
	GET_VAL = IBinder::FIRST_CALL_TRANSACTION,
	SET_VAL
};
//定义实现BpXXX
class BpFregService: public BpInterface<IFrefService>
{
public:
	BpFregService(const sp<IBinder>& imp1) : BpInterface<IFregService>(imp1) { }
    //定义业务接口getVal
	int32_t getVal() {
		Parcel data;
		data.writeInterfaceToken(IFregService::getInterfaceDescriptor());
		Parcel reply;
		remote()->transact(GET_VAL, data, &reply);
		int32_t val = reply.readInt32();
		return val;
	}
    //定义业务接口setVal
	void setVal(int32_t val) {
		Parcel data;
		data.writeInterfaceToken(IFregService::getInterfaceDescriptor());
		data.writeInt32(val);
		Parcel reply;
		remote()->transact(SET_VAL,data, &reply);
	}	
};
//宏IMPLEMENT_META_INTERFACE实现重要几个函数
IMPLEMENT_META_INTERFACE(FregService, "hr.ma.IFregService");
//实现BnXXX::onTransact函数
status_t BnFregService::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
	switch(code) {
		case GET_VAL: {
			CHECK_INTERFACE(IFregService, data, reply);
            //函数getVal其实是虚函数
			int32_t val = getVal();
			reply->writeInt32(val);
			return NO_ERROR;
		}
		case SET_VAL: {
			CHECK_INTERFACE(IFregService, data, reply);
			int32_t val = data.readInt32();
            //函数setVal其实是虚函数
			setVal(val);
			return NO_ERRORL
		}
		default: {
			return BBinder::onTransact(code, data, reply, flags);
		}
	}
}

2、服务端进程实现

上节内容已经给我们搭建了一个C/S模式的基本结构,这样给我们省下了不少事情,接下来就只需要实现具体的逻辑就行了。在服务端进程中我们可以直接创建一个服务,然后将其注册到service manager,最后进入轮询模式。如下代码:

//server/FregServer.cpp
#define LOG_TAG "FregServer"
#include <fcntl.h>
#include <utils/Log.h>
#include <binder/IServiceManager.h>
#include <binder/IPCThreadState.h>
#include "../common/IFregService.h"
#define FREG_DEVICE_NAME "/dev/freg"
//实现具体服务XXX:继承于BnXXX,且实现common/IFregService.cpp中定义的虚函数getVal和setVal
class FregService: public BnFregService
{
public:
	FregService() {
        //构造函数中打开文件,后续的接口我们都从该文件中得到,该文件可以是驱动设备文件也可以是其他文件,或者不需要该文件都可以
		fb = open(FREG_DEVICE_NAME, O_RDWR);
		if(fd == -1) LOGE("Failed to open device %s.\n", FREG_DEVICE_NAME );
	}
	virtual ~FregService() {
		if(fd != -1) close(fd);
	}
    //静态函数进来进行服务注册,android os基本上采用该方法,当然也可以将该操作放在其他地方实现
	static void instantiate() {
		defaultServiceManager()->addService(String16(FREG_SERVICE), new FregService());
	}
    //真正的业务逻辑实现,前面讲到的都是虚函数
	int32_t getVal() {
		int32_t val = 0;
		if(fd != -1) read(fd, &val, sizeof(val));
		returmn val;
	}
	void setVal(int32_t val) {
		if(fd != -1) write(fd, &val, sizeof(val)); //真正的实现还是对寄存器的直接读写操作
	}
private:
	int fd;	
};
//服务进程入口函数
int main(int argc, char** argv)
{
    //服务注册,不一定非要该方式进行注册,但是该方式确实比较优雅
	FregService::instantiante();
    //创建线程池并进入轮询模式
	ProcessState::seif()->startThreadPool();
	IPCRhreadState::self()->joinThreadPool();
	return 0;
}

服务端进程的入库函数也比较简单,就干了两件事情,把定义好的业务子类XXXFregService注册到service manager里面后就开启binder线程池进入轮询模式。

3、客户端进程实现

客户端进程就更加简单了,获取到对应IXXX后,这里就是IFregService接口,就可以调用其接口得到自己的结果了,这些接口的具体执行在服务端进程。

//client/FregClient.cpp
#define LOG_TAG "FregClient"
#include <utils/Log.h>
#include <binder/IServiceManager.h>
#include "../common/IFregService.h"
int main()
{
    //得到IServiceManager
    sp<IServiceManager> ism = defaultServiceManager();
    //得到IFregService
    sp<IBinder> ibinder =  ism->getService(String16(FREG_SERVICE));
	if(binder == NULL) return -1;
    //转换IFregService
    sp<IFregService> service = IFregService::asInterface(binder);
	if(service == NULL) return -2;
    //试着调用一下业务接口
	int32_t val = service->getVal();
	printf("%d.\n", val);
	printf("Add value 1 to FregService.\n");
	val +=1;
	service->setVal(val);
	printf("Read the value from FregService again:\n");
	val = service->getVal();
	printf("%d.\n", val);
	return 0;
}

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_27672101/article/details/108286460

智能推荐

[MAC] 编译安装和测试《魔兽世界》模拟服务端 TrinityCore-程序员宅基地

文章浏览阅读618次。2019独角兽企业重金招聘Python工程师标准>>> ..._mac trinitycore

Windows驱动开发之日志打印 - TraceEvents_windows驱动开发 日志-程序员宅基地

文章浏览阅读3.6k次。《Windows驱动开发技术详解》一书中,介绍了一种“Windows驱动程序日志打印和查看的方法”,具体就是:在需要打印日志的地方,调用“KdPrint”函数,该函数类似标准C的printf(print file)函数。然后用“DebugView.exe”软件查看日志。一、引入事实上,微软也提供了一个日志打印和日志查看机制,它可以查看指定的驱动文件的日志,并..._windows驱动开发 日志

游戏玩家的程序猿之路-程序员宅基地

文章浏览阅读2.9k次,点赞6次,收藏2次。目录一.自我介绍二.编程目标三.学习编程的方法四.分配编程的时间五.最想进入的IT公司一.自我介绍大家好,这里是ONE_KICK,一名一名普通的大二学生,这是我的第一篇blog,现在还是一名编程小白,因为自己对于游戏的热爱决定开始学习基础的编程,来到CSDN,一方面是可以学习到很多的知识,另一方面是想要记录一下自己的学习历程。二.编程目标从零开始学习编程,打算先学习C语言,而后开始学习C++,近期的目标是在一年时间内掌握C++的用法。三.学习编程的方法打算稳扎稳打的学习C++,每天会有固定的时间

VC+API模拟键盘按键(详解篇)_vc模拟键盘输入-程序员宅基地

文章浏览阅读1.6w次。模拟键盘按键━━━━━━━━━━━━━━━━━━━━━━━━以前就想过如何模拟键盘按键向其他程序发送控制命令,但总是无功而返,这次也不例外。模拟按键的方法很多,如PostMessage(不能用SendMessage),SendInput,keybd_event。但最要命的是以上方法基本上都要窗口获取焦点时才有效。也就是想后台模拟键盘按键难度是很高的。当然有些特殊_vc模拟键盘输入

Docer容器客户端在启动的镜像的时候报错Error invoking remote method ‘docker-start-container‘: Error: (HTTP code 500_error invoking remote method 'docker-run-container-程序员宅基地

文章浏览阅读6.7k次,点赞8次,收藏3次。打开docker容器,启动redis镜像,报错:Error invoking remote method 'docker-start-container': Error: (HTTP code 500) server error - Ports are not available: listen tcp 0.0.0.0:2181: bind: An attempt was made to access a socket in a way forbidden by its access per._error invoking remote method 'docker-run-container': error: (http code 400)

数论模板-程序员宅基地

文章浏览阅读64次。组合数  递推void get_C(){ for(int i=0;i<=2005;i++) { C[0][i]=C[i][i]=1; for(int j=1;j<i;j++) { C[j][i]=(C[j-1][i-1]+C[j][i-1])%MOD; ..._inv2 数论

随便推点

100个免费可商用字体,你总有一天用到它-程序员宅基地

文章浏览阅读636次。我一个一个查授权、筛选证实可商用。你知道吗?你平时在电脑轻轻一点就能用的字体,属于法律保护的美术作品!当我们习惯于在网上搜刮各种字体,以为可以随便用在自己的设计图、网页(比如H5广告)上时,可能一直无意间伤害着创作者、版权人。以下整合出可能是最全的免费可商用字体,下载方式见文末——都是我一个一个查授权、筛选证实可以用的啊。最后提醒一下..._点字灵动体字体免费商用吗

openssl 1.1.1L /1.1.1o/1.1.1t rpm包制作——筑梦之路_libcrypto 1.1.1t-程序员宅基地

文章浏览阅读4.2k次,点赞6次,收藏11次。https://www.openssl.org/source/openssl-1.1.1l.tar.gzhttps://www.openssl.org/source/openssl-1.1.1l.tar.gzopenssl.spec文件内容Summary: OpenSSL 1.1.1l for CentOSName: opensslVersion: %{?version}%{!?version:1.1.1l}Release: 1%{?dist}Obsoletes: %{name} <=_libcrypto 1.1.1t

将MNIST手写数字数据集导入NumPy数组(《深度学习入门:基于Python的理论与实现》实践笔记)_ubyte.gz用什么打开-程序员宅基地

文章浏览阅读1.2k次,点赞6次,收藏12次。将MNIST手写数字数据集导入NumPy数组下载MNIST数据集(使用urllib.request.urlretrieve()函数)打开下载得到的.gz压缩文件(使用gzip.open()函数)并导入NumPy数组(使用np.frombuffer()函数)完整实例(能直接运行):可能遇到的问题:下载MNIST数据集(使用urllib.request.urlretrieve()函数)os.path.exists(path)可以判断是否存在以path为地址的文件。urllib.request.urlre_ubyte.gz用什么打开

【剧前爆米花--C语言篇】C语言数组底层原理详解_数组的底层逻辑-程序员宅基地

文章浏览阅读1k次,点赞15次,收藏13次。对C语言数组的详细剖析,以及底层理解,相信你一定有所收获_数组的底层逻辑

java--html(6)框架_h|keycount| html-程序员宅基地

文章浏览阅读950次。框架标签:< frameset> < frameset rows=”10%,*”> < frame src=”1.html” name=”top” /> < frameset cols=”30%,*”> < frame src=”2.html” name=”left_h|keycount| html

虚机修改yum源_虚拟机如何优化yum源-程序员宅基地

文章浏览阅读1.1k次,点赞2次,收藏7次。修改yum的下载源_虚拟机如何优化yum源