深入浅出Android底层(三)-Android中的IPC-Binder通信机制--客户端

1,095 阅读8分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第8天,点击查看活动详情

前言

上一篇文章我们从binder驱动层知道了启动binder后是如何监听数据读写的,那么当客户端需要向binder层请求远端服务层对象,并且涉及到数据传输时,客户端又是如何发起请求并且进行数据写入和监听数据输出的呢?

今天这篇文章我们就来看看客户端发送Binder请求后到底发生了什么?

一、ServiceManager

1、getIServiceManager

客户端首先肯定要获取到这个服务端对象or代理,提供这个serice的方法在ServiceManger的代码里

frameworks/base/core/java/android/os/ServiceManager.java


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

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

2、BinderInternal.getContextObject()

BinderInternal.getContextObject()是一个native函数

/frameworks/base/core/jni/android_util_Binder.cpp

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

可以看到这里返回了一个ProcessState类对象

sp<ProcessState> ProcessState::self()
{
    Mutex::Autolock _l(gProcessMutex);
    if (gProcess != NULL) {
        return gProcess;
    }
    gProcess = new ProcessState("/dev/binder");
    return gProcess;
}

3、ProcessState

ProcessState::ProcessState(const char *driver)
    : mDriverName(String8(driver))
    //打开binder驱动
    , mDriverFD(open_driver(driver))
    , mVMStart(MAP_FAILED)
    , mThreadCountLock(PTHREAD_MUTEX_INITIALIZER)
    , mThreadCountDecrement(PTHREAD_COND_INITIALIZER)
    , mExecutingThreadsCount(0)
    , mMaxThreads(DEFAULT_MAX_BINDER_THREADS)
    , mStarvationStartTimeMs(0)
    , mManagesContexts(false)
    , mBinderContextCheckFunc(NULL)
    , mBinderContextUserData(NULL)
    , mThreadPoolStarted(false)
    , mThreadPoolSeq(1)
{
    if (mDriverFD >= 0) {
        // mmap the binder, providing a chunk of virtual address space to receive transactions.
        mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
        if (mVMStart == MAP_FAILED) {
            // *sigh*
            ALOGE("Using %s failed: unable to mmap transaction memory.\n", mDriverName.c_str());
            close(mDriverFD);
            mDriverFD = -1;
            mDriverName.clear();
        }
    }

    LOG_ALWAYS_FATAL_IF(mDriverFD < 0, "Binder driver could not be opened.  Terminating.");
}

可以看到这里调用了open_driver函数打开了binder驱动

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) {
            ...
        }
        ... 设置binder最大线程数
        size_t maxThreads = DEFAULT_MAX_BINDER_THREADS;
        result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads);
        ...
    return fd;
}

可以看到这里主要的三部曲分别是:打开binder驱动,验证binder版本,设置binder最大线程数,后续的步骤在咱们的binder层已经进行了分析,主要是调用mmap进行了内存映射.

我们看下binder已经准备好后后续又做了哪些工作.

4、getContextObject

sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& /*caller*/)
{
    return getStrongProxyForHandle(0);
}

可以看到,这里传入的入参是0

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

    AutoMutex _l(mLock);
    
    //查找或建立handle对应的handle_entry
    handle_entry* e = lookupHandleLocked(handle);

    if (e != NULL) {
        
        IBinder* b = e->binder;
        if (b == NULL || !e->refs->attemptIncWeak(this)) {
            if (handle == 0) {
                //当handle为ServiceManager的特殊情况
                //需要确保在创建binder引用之前,context manager已经被binder注册

                Parcel data;
                status_t status = IPCThreadState::self()->transact(
                        0, IBinder::PING_TRANSACTION, data, NULL, 0);
                if (status == DEAD_OBJECT)
                   return NULL;
            }

            //创建BpBinder并保存下来以便后面再次查找
            b = BpBinder::create(handle);
            e->binder = b;
            if (b) e->refs = b->getWeakRefs();
            result = b;
        } else {
            // This little bit of nastyness is to allow us to add a primary
            // reference to the remote proxy when this team doesn't have one
            // but another team is sending the handle to us.
            result.force_set(b);
            e->refs->decWeak(this);
        }
    }

    return result;
}

5、lookupHandleLocked

ProcessState::handle_entry* ProcessState::lookupHandleLocked(int32_t handle)
{
    const size_t N=mHandleToObject.size();
    //新建一个handle_entry并插入到vector中
    if (N <= (size_t)handle) {
        handle_entry e;
        e.binder = NULL;
        e.refs = NULL;
        status_t err = mHandleToObject.insertAt(e, N, handle+1-N);
        if (err < NO_ERROR) return NULL;
    }
    return &mHandleToObject.editItemAt(handle);
}

可以看到,这里获得了一个BpBinder对象,由于它还是native的对象,需要将它转换成java类型,

6、javaObjectForIBinder

jobject javaObjectForIBinder(JNIEnv* env, const sp<IBinder>& val)
{
    if (val == NULL) return NULL;

    //JavaBBinder返回true,其他类型发挥false
    if (val->checkSubclass(&gBinderOffsets)) {
        // It's a JavaBBinder created by ibinderForJavaObject. Already has Java object.
        jobject object = static_cast<JavaBBinder*>(val.get())->object();
        LOGDEATH("objectForBinder %p: it's our own %p!\n", val.get(), object);
        return object;
    }

    // For the rest of the function we will hold this lock, to serialize
    // looking/creation/destruction of Java proxies for native Binder proxies.
    AutoMutex _l(gProxyLock);

    BinderProxyNativeData* nativeData = gNativeDataCache;
    if (nativeData == nullptr) {
        nativeData = new BinderProxyNativeData();
    }
    // gNativeDataCache is now logically empty.
    jobject object = env->CallStaticObjectMethod(gBinderProxyOffsets.mClass,
            gBinderProxyOffsets.mGetInstance, (jlong) nativeData, (jlong) val.get());
    if (env->ExceptionCheck()) {
        // In the exception case, getInstance still took ownership of nativeData.
        gNativeDataCache = nullptr;
        return NULL;
    }
    BinderProxyNativeData* actualNativeData = getBPNativeData(env, object);
   //如果object是刚刚新建出来的BinderProxy
    if (actualNativeData == nativeData) {
        //处理proxy计数
    } else {
        // nativeData wasn't used. Reuse it the next time.
        gNativeDataCache = nativeData;
    }

    return object;
}

首先会有一个IBinder类型的判断,只有当IBinder的的实际类型为JavaBBinder的时候会返回true,其他子类均返回false.

可以看到,JavaBBinder类继承自BBinder,里面保存了对java层Binder对象的引用,所以直接走后面的if.返回里面的object.

native层的javaBBinder与java层的Binder是对应关系

接着实例化一个BinderProxyNativeData,接着调用gBinderProxyOffsets.mGetInstance

这里的gBinderProxyOffsets 对应的数据类型其实是BinderProxy

7、getInstance

    private static BinderProxy getInstance(long nativeData, long iBinder) {
        BinderProxy result;
        try {
            result = sProxyMap.get(iBinder);
            if (result != null) {
                return result;
            }
            result = new BinderProxy(nativeData);
        } catch (Throwable e) {
            // We're throwing an exception (probably OOME); don't drop nativeData.
            NativeAllocationRegistry.applyFreeFunction(NoImagePreloadHolder.sNativeFinalizer,
                    nativeData);
            throw e;
        }
        NoImagePreloadHolder.sRegistry.registerNativeAllocation(result, nativeData);
        // The registry now owns nativeData, even if registration threw an exception.
        sProxyMap.set(iBinder, result);
        return result;
    }

可以看到,这里是以iBinder作为key尝试从sProxyMap取出BinderProxy,如果取到值了就直接将它返回出去,如果没取到,用之前传进来的BinderProxyNativeData指针为参数实例化一个BinderProxy,并将其设置到sProxyMap中。

从这里可以看出每一个BinderProxy都是以单例的形式存在的,并且native层的BinderProxyNativeData与Java层的BinderProxy是一一对应的关系.

getInstance方法执行完毕后,接下来会判断BinderProxy是不是新建出来的,如果是新建出来的需要处理一下proxy计数.

8、getIServiceManager

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

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

此时再回到这个方法,我们知道BinderInternal.getContextObject()方法返回的值对应的类型是BinderProxy代理类,接着调用Binder.allowBlocking方法,这个方法只是改变了BinderProxy中的一个参数,允许其阻塞调用.

接着asInterface方法实例化了一个ServiceManagerProxy对象

二、ServiceManagerProxy

class ServiceManagerProxy implements IServiceManager {
    public ServiceManagerProxy(IBinder remote) {
        mRemote = remote;
    }

    public IBinder asBinder() {
        return mRemote;
    }

    public IBinder getService(String name) throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        data.writeInterfaceToken(IServiceManager.descriptor);
        data.writeString(name);
        mRemote.transact(GET_SERVICE_TRANSACTION, data, reply, 0);
        IBinder binder = reply.readStrongBinder();
        reply.recycle();
        data.recycle();
        return binder;
    }
    
      ...

可以看到ServiceManagerProxy实现了ISerivceManager接口,ISerivceManager暴露了若干实现方法,例如getService,addService等等.

1、AIDL

AIDL生成的代码对于Binder进行了进一步封装,<接口>.Stub对应服务端Binder,<接口>.Stub.Proxy标识客户端,内部持有一个mRemote实例(BinderProxy),aidl根据定义的接口方法生成若干个TRANSACTION_<函数名> code常量,两端Binder通过这些code标识解析参数,调用相应接口方法。

换言之AIDL就是对BinderProxy.transactBinder.onTransact进行了封装,使用者不必再自己定义每次通讯的code以及参数解析

2、BinderProxy.transact

    public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
    ///检查Parcel大小
1108        Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");
1109
1110        if (mWarnOnBlocking && ((flags & FLAG_ONEWAY) == 0)) {
1111            // For now, avoid spamming the log by disabling after we've logged
1112            // about this interface at least once
1113            mWarnOnBlocking = false;
1114            Log.w(Binder.TAG, "Outgoing transactions from this process must be FLAG_ONEWAY",
1115                    new Throwable());
1116        }
1117
1118        final boolean tracingEnabled = Binder.isTracingEnabled();
1119        if (tracingEnabled) {
1120            final Throwable tr = new Throwable();
1121            Binder.getTransactionTracker().addTrace(tr);
1122            StackTraceElement stackTraceElement = tr.getStackTrace()[1];
1123            Trace.traceBegin(Trace.TRACE_TAG_ALWAYS,
1124                    stackTraceElement.getClassName() + "." + stackTraceElement.getMethodName());
1125        }
1126        try {
1127            return transactNative(code, data, reply, flags);
1128        } finally {
1129            if (tracingEnabled) {
1130                Trace.traceEnd(Trace.TRACE_TAG_ALWAYS);
1131            }
1132        }
1133    }
    static void checkParcel(IBinder obj, int code, Parcel parcel, String msg) {
688        if (CHECK_PARCEL_SIZE && parcel.dataSize() >= 800*1024) {
689            // Trying to send > 800k, this is way too much
690            StringBuilder sb = new StringBuilder();
691            sb.append(msg);
692            sb.append(": on ");
693            sb.append(obj);
694            sb.append(" calling ");
695            sb.append(code);
696            sb.append(" size ");
697            sb.append(parcel.dataSize());
698            sb.append(" (data: ");
699            parcel.setDataPosition(0);
700            sb.append(parcel.readInt());
701            sb.append(", ");
702            sb.append(parcel.readInt());
703            sb.append(", ");
704            sb.append(parcel.readInt());
705            sb.append(")");
706            Slog.wtfStack(TAG, sb.toString());
707        }
708    }

可以看到这里我们的Parcel限制了数据传输的大小为800k,这也是我们序列化传输数据有限制的原因.

3、transactNative

这是一个native方法

static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,
        jint code, jobject dataObj, jobject replyObj, jint flags) 
        // throws RemoteException
{
    if (dataObj == NULL) {
        jniThrowNullPointerException(env, NULL);
        return JNI_FALSE;
    }

    Parcel* data = parcelForJavaObject(env, dataObj);
    if (data == NULL) {
        return JNI_FALSE;
    }
    Parcel* reply = parcelForJavaObject(env, replyObj);
    if (reply == NULL && replyObj != NULL) {
        return JNI_FALSE;
    }

    IBinder* target = getBPNativeData(env, obj)->mObject.get();
    if (target == NULL) {
        jniThrowException(env, "java/lang/IllegalStateException", "Binder has been finalized!");
        return JNI_FALSE;
    }

    //log
    ...
    
    status_t err = target->transact(code, *data, reply, flags);
    
    //log
    ...

    if (err == NO_ERROR) {
        return JNI_TRUE;
    } else if (err == UNKNOWN_TRANSACTION) {
        return JNI_FALSE;
    }

    signalExceptionForError(env, obj, err, true /*canThrowRemoteException*/, data->dataSize());
    return JNI_FALSE;
}

这里首先是获得native层对应的Parcel并执行判断,Parcel实际上功能是在native中实现的,java中的Parcel类使用mNativePtr成员变量保存了其对应native中的Parcel的指针

然后调用getBPNativeData函数获得BinderProxy在native中对应的BinderProxyNativeData,再通过里面的mObject域成员变量得到其对应的BpBinder,这个函数在之前分析javaObjectForIBinder的时候已经出现过了

4、BpBinder.transact

status_t BpBinder::transact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    // Once a binder has died, it will never come back to life.
    //判断binder服务是否存活
    if (mAlive) {
        ...
        status_t status = IPCThreadState::self()->transact(
            mHandle, code, data, reply, flags);
        if (status == DEAD_OBJECT) mAlive = 0;

        return status;
    }

    return DEAD_OBJECT;
}

这里有一个Alive判断,可以避免对一个已经死亡的binder服务再发起事务,浪费资源,除此之外便是调用IPCThreadStatetransact函数了

三、IPCThreadState

status_t IPCThreadState::transact(int32_t handle,
uint32_t code, c
onst Parcel& data,Parcel* reply, uint32_t flags)
{
    ...
  // 传输数据
  err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, nullptr);
  
  if ((flags & TF_ONE_WAY) == 0) {
  
      ...
      if (reply) {
          // 等待远程数据的返回
          err = waitForResponse(reply);
      } else {
          Parcel fakeReply;
          err = waitForResponse(&fakeReply);
      }
  } else {
      err = waitForResponse(nullptr, nullptr);
  }
  return err;
}

这里主要做的就两件事

    1. 通过writeTransactionData将数据写入到Parcel中,对应的cmd命令为BC_TRANSACTION,这是请求码,对应的都是以BC_开头命名,在service端会通过这些请求码做不同的逻辑处理。
    1. 通过waitForResponse来等待远端的数据返回,这里也有一个cmd命令行,例如BR_REPLY,这是返回码,对应的都是以BR_开头命名的,在client也会通过这些返回码做不同的逻辑处理

总结

我们已经知道了Client端主要通过BpBindertransact方法与service端进行通信,在BpBindertransact方法中又通过IPCThreadStatetransact方法将数据传递到service端。

最终通过IPCThreadStatewriteTransactionData方法 进行数据的读写,下一篇接着分析服务端收到客户端的请求后如何处理~