Binder通信原理

2018-06-03  本文已影响8人  gczxbb

在前面的Binder注册与查找一文中,已经介绍过Binder机制在Java层的通信架构。我们知道,在通信过程中,请求进程要一个业务代理和BinderProxy代理,服务进程要实现业务代理并继承Binder类。本文主要介绍一下底层数据的通信流程,先看一下Java层的通信架构图。

IActivityManager业务Java层的Binder通信架构图.jpg 看一下请求进程,这是App进程ActivityManagerProxy的startActivity业务方法。
public int startActivity(IApplicationThread caller,...) throws RemoteException {
    Parcel data = Parcel.obtain();
    Parcel reply = Parcel.obtain();
    ...
    data.writeStrongBinder(caller != null ? caller.asBinder() : null);
    ...
    mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
    ...
    return result;
}

将参数写入Parcel对象,普通类型int、char、float、boolean等分别调用Parcel的writeXxx对应方法,复杂类型,如Binder类型,IApplicationThread是这种类型,调用Parcel的writeStrongBinder方法。所有数据都写入底层Parcel。最后,调用BinderProxy的transact方法,向底层发送数据。
再看一下服务进程,ActivityManagerNative类重写Binder类的onTransact方法。

@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
        throws RemoteException {
    switch (code) {
        case START_ACTIVITY_TRANSACTION:
        {
            data.enforceInterface(IActivityManager.descriptor);
            IBinder b = data.readStrongBinder();
            IApplicationThread app = ApplicationThreadNative.asInterface(b);
            ...
            int result = startActivity(app, callingPackage, intent, resolvedType,
                    resultTo, resultWho, requestCode, startFlags, profilerInfo, options);
            reply.writeInt(result);
            return true;
        }
        ...//其他业务code判断。
    }
    return super.onTransact(code, data, reply, flags);
}

该方法通过业务code,区别每一个业务方法。然后,调用子类ActivityManagerService对应的业务方法。这是服务进程的逻辑,其中,Binder的execTransact方法将触发该方法。

private boolean execTransact(int code, long dataObj, long replyObj,
                             int flags) {
    Parcel data = Parcel.obtain(dataObj);
    Parcel reply = Parcel.obtain(replyObj);
    boolean res;
    try {
        res = onTransact(code, data, reply, flags);
    } catch (RemoteException e) {
    } catch (RuntimeException e) {
    } catch (OutOfMemoryError e) {
    }
    reply.recycle();
    data.recycle();
    StrictMode.clearGatheredViolations();
    return res;
}

该方法在Binder类,将调用业务类重写的onTransact方法,Binder本身不关心业务,因此,每一个继承Binder的业务类,都需要重写onTransact方法,根据业务code查找业务方法。execTransact方法是由底层调用的。
总之,当请求进程调用某个业务方法时,会将code和参数一起,传入底层。服务进程底层得知有请求发生时,将code和参数交给服务进程上层,execTransact方法就是底层调用上层的入口方法。下面看一下底层的通信流程。


请求进程底层流程

先看一下请求进程BinderProxy的transact方法,它将调用JNI#transactNative方法,进入底层代码。

public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
    Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");
    return transactNative(code, data, reply, flags);
}

transactNative方法,底层JNI对应android_os_BinderProxy_transact方法。

static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,
        jint code, jobject dataObj, jobject replyObj, jint flags) {
                    // throws RemoteException 
    //dataObj为空抛出异常
    Parcel* data = parcelForJavaObject(env, dataObj); 
    Parcel* reply = parcelForJavaObject(env, replyObj);
    //data,reply为返回false
    IBinder* target = (IBinder*)
        env->GetLongField(obj, gBinderProxyOffsets.mObject);
    //target为空抛出异常
    ...
    status_t err = target->transact(code, *data, reply, flags);
    ...
    return JNI_FALSE;
}

首先,根据上层Parcel参数内部mNativePtr指针,获取底层Parcel对象,根据上层BinderProxy内部mObject指针,获取底层BpBinder对象。
然后,调用底层BpBinder的transact方法。

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

该方法中,入参是上层传入code、flag和底层Parcel,我们在上层向Parcel写入传递的数据时,其实是写入引用的底层Parcel中。调用IPCThreadState的transact方法,每个线程IPCThreadState是一个单独对象,属于线程私有变量。mHandle是底层BpBinder句柄,在BpBinder创建时初始化其值,通过该值可以查找服务进程的底层Binder。每一个请求进程底层都会有一个对应的BpBinder。看一下IPCThreadState的transact方法。

status_t IPCThreadState::transact(int32_t handle,
                uint32_t code, const Parcel& data,
                Parcel* reply, uint32_t flags) {
    status_t err = data.errorCheck();
    flags |= TF_ACCEPT_FDS;
    ...
    if (err == NO_ERROR) {
        err = writeTransactionData(BC_TRANSACTION, flags, handle, 
                        code, data, NULL);
    }    
    ...    
    if ((flags & TF_ONE_WAY) == 0) {
        ...
        if (reply) {
            err = waitForResponse(reply);
        } else {
            Parcel fakeReply;
            err = waitForResponse(&fakeReply);
        }
        ....
    } else {
        //携带TF_ONE_WAY标志,无需回复
        err = waitForResponse(NULL, NULL);
    }    
    return err;
}

调用IPCThreadState的writeTransactionData方法,将code,handle,底层Parcel以及通信命令BC_TRANSACTION,写入binder_transaction_data结构体。
上层flag带有TF_ONE_WAY标志,代表一次通信,不需要服务进程回复消息。当没有该标志位时,调用waitForResponse方法,入参是回复Parcel,等待服务进程回复。

status_t IPCThreadState::writeTransactionData(int32_t cmd, uint32_t binderFlags,
    int32_t handle, uint32_t code, const Parcel& data, status_t* statusBuffer) {
    binder_transaction_data tr;
    tr.target.ptr = 0; 
    tr.target.handle = handle;
    tr.code = code;
    tr.cookie = 0;
    ....
    const status_t err = data.errorCheck();
    if (err == NO_ERROR) {
        tr.data_size = data.ipcDataSize();
        tr.data.ptr.buffer = data.ipcData();
        tr.offsets_size = data.ipcObjectsCount()*sizeof(binder_size_t);
        tr.data.ptr.offsets = data.ipcObjects();
    } else if (statusBuffer) {
        ....
    }
    mOut.writeInt32(cmd);
    mOut.write(&tr, sizeof(tr));    
    return NO_ERROR;
}

将binder_transaction_data结构体和通信命令cmd一起写入mOut,即输出Parcel,它存储向Binder驱动发送的数据。

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

    while (1) {
        if ((err=talkWithDriver()) < NO_ERROR) break;
        err = mIn.errorCheck();
        if (err < NO_ERROR) break;
        if (mIn.dataAvail() == 0) continue;
        
        cmd = (uint32_t)mIn.readInt32();
        
        switch (cmd) {
        case BR_TRANSACTION_COMPLETE:
            if (!reply && !acquireResult) goto finish;
            break;
        ...
        case BR_REPLY:
            {
                binder_transaction_data tr;
                err = mIn.read(&tr, sizeof(tr));
                if (err != NO_ERROR) goto finish;
                if (reply) {
                } else {
                }
            }
            goto finish;
        default:
            err = executeCommand(cmd);
            if (err != NO_ERROR) goto finish;
            break;
        }
    }
finish:
    if (err != NO_ERROR) {
    //error
    }    
    return err;
}

该方法是一个无限循环,中间状态会判断退出。首先,它调用talkWithDriver方法,此方法包含真正与Binder驱动通信的系统调用。当从talkWithDriver唤醒时,查看输入Parcel,即mIn是否有数据,如果包含输入数据,读取通信命令,处理数据。

status_t IPCThreadState::talkWithDriver(bool doReceive) {
    //mDriverFD必须大于0,否则返回错误
    binder_write_read bwr;
   
    const bool needRead = mIn.dataPosition() >= mIn.dataSize();

    const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0;
    
    bwr.write_size = outAvail;
    bwr.write_buffer = (uintptr_t)mOut.data();

    if (doReceive && needRead) {
        bwr.read_size = mIn.dataCapacity();
        bwr.read_buffer = (uintptr_t)mIn.data();
    } else {
        bwr.read_size = 0;
        bwr.read_buffer = 0;
    }
    if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR;
    bwr.write_consumed = 0;
    bwr.read_consumed = 0;
    status_t err;
    do {
        if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
            err = NO_ERROR;
        else
            err = -errno;
        if (mProcess->mDriverFD <= 0) {
            err = -EBADF;
        }
    } while (err == -EINTR);   

    if (err >= NO_ERROR) {
        if (bwr.write_consumed > 0) {
            if (bwr.write_consumed < mOut.dataSize())
                mOut.remove(0, bwr.write_consumed);
            else
                mOut.setDataSize(0);
        }
        if (bwr.read_consumed > 0) {
            mIn.setDataSize(bwr.read_consumed);
            mIn.setDataPosition(0);
        }      
        return NO_ERROR;
    }    
    return err;
}

binder_write_read数据读写结构体,保存mIn和mOut的数据。当发送数据时,用mOut数据的指针初始化binder_write_read结构体写缓冲区write_buffer和写入write_size大小。接收数据时,用mIn数据的指针初始化该结构体读缓冲区read_buffer和读取read_size大小。
如果读写大小都是空,不需要与驱动交互,直接返回,说明什么都没有做。如果binder_write_read有一个buffer不空,系统调用ioctl方法,陷入Binder驱动执行代码
ioctl方法返回后,根据write_consumed与read_consumed,重设mOut与mIn对象。当请求进程在内核态执行ioctl系统调用时,会因等待数据挂起或数据到来而被唤醒,因此,在ioctl方法处会有休眠,等待数据返回。若被唤醒,回到waitForResponse方法,从mIn中读取数据。
系统调用ioctl方法,mDriverFD是文件描述符,BINDER_WRITE_READ是读写命令,内核态执行的是binder_ioctl方法。请求进程通信流程图。

Binder通信请求端流程图.jpg

任重而道远

上一篇 下一篇

猜你喜欢

热点阅读