Android跨进程通信中:Client端调度流程分析

346 阅读9分钟

跨进程调用

image.png

ServiceManager内部存储的只是Binder的一个代号。为什么会有个BpBinder和BBinder之分呢?BpBinder是给客户端使用的,BBinder用来给服务端使用。他们都是对Binder的封装。这样可以更好地适配Binder驱动。

我们在客户端调用一个函数的时候,到底是如何实现对Service端的访问的呢?下面就从源码角度来分析这个流程:

以客户端尝试调用服务端的addPerson方法为例:

private ILeoAidl myService;
myService.addPerson(new Person("leo", 3));

之后会进入这里:

@Override public void addPerson(com.enjoy.leoservice.Person person) throws android.os.RemoteException {
  // 序列化是跨进程通信的必须。
  android.os.Parcel _data = android.os.Parcel.obtain();
  android.os.Parcel _reply = android.os.Parcel.obtain();
  try {
    _data.writeInterfaceToken(DESCRIPTOR);
    if ((person!=null)) {
      _data.writeInt(1);
      person.writeToParcel(_data, 0);
    } else {
      _data.writeInt(0);
    }
    // 关键调用
    boolean _status = mRemote.transact(Stub.TRANSACTION_addPerson, _data, _reply, 0);
    if (!_status && getDefaultImpl() != null) {
      getDefaultImpl().addPerson(person);
      return;
    }
    _reply.readException();
  } finally {
    _reply.recycle();
    _data.recycle();
  }
}

这里的mRemote实际上就是一个IBinder的实现对象。那么它实际是谁呢?因为我们是在客户端调用的,所以这个Binder对象实际上就是BinderProxy。这是因为我们在上一篇文章中分析过,客户端拿到的只能是服务端的代理。

public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
    // ...
    try {
        // 这里就会进入JNI的世界了。
        return transactNative(code, data, reply, flags);
    } finally {
        if (transactListener != null) {
            transactListener.onTransactEnded(session);
        }
        if (tracingEnabled) {
            Trace.traceEnd(Trace.TRACE_TAG_ALWAYS);
        }
    }
}

之后就进入了Native的世界去继续操作了,这里的obj就是binderproxy。这里也同时完成了数据格式的转化,这也是为什么Binder需要使用Parcel进行序列化的原因。这里的target就是C++层的BpBinder。

image.png

JNI函数的注册是在Zygote init注册的时候完成的。然后继续上图,看一看getBpNativeData的实现:

image.png

这里的target就是BpBinder。

image.png

这个IPCThreadState,每个线程都会有一个,ProcessState,每个进程都会有一个。关于IPCThreadState,由于每一次通信都会创建一个,这也就解释了为什么会有Binder线程池的说法。因为这个IPCThreadState就是线程池中的一个线程。

image.png

而在transact函数中。最终会走到这里:

image.png

其中waitForResponse就是他的一个IPCThreadState的一个函数,这里边,不停地通过一个死循环去读驱动:

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();

        IF_LOG_COMMANDS() {
            alog << "Processing waitForResponse Command: "
                << getReturnString(cmd) << endl;
        }

        switch (cmd) {
        case BR_TRANSACTION_COMPLETE:
            if (!reply && !acquireResult) goto finish;
            break;

        case BR_DEAD_REPLY:
            err = DEAD_OBJECT;
            goto finish;

        case BR_FAILED_REPLY:
            err = FAILED_TRANSACTION;
            goto finish;

        case BR_ACQUIRE_RESULT: {
            ALOG_ASSERT(acquireResult != nullptr, "Unexpected brACQUIRE_RESULT");
            const int32_t result = mIn.readInt32();
            if (!acquireResult) continue;
            *acquireResult = result ? NO_ERROR : INVALID_OPERATION;
        }
        goto finish;

        case BR_REPLY: {
            binder_transaction_data tr;
            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->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(nullptr,
                        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);
                }
            } else {
                freeBuffer(nullptr,
                    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);
                continue;
            }
        }
        goto finish;

        default:
            err = executeCommand(cmd);
            if (err != NO_ERROR) goto finish;
            break;
        }
    }

talkWithDriver中,就有ioctl函数。就是去读取驱动,或者写驱动:

status_t IPCThreadState::talkWithDriver(bool doReceive) {
    if (mProcess->mDriverFD <= 0) {
        return -EBADF;
    }

    binder_write_read bwr;
    // Is the read buffer empty?
    const bool needRead = mIn.dataPosition() >= mIn.dataSize();
    // 我们不想写任何东西,如果我们还在读取input buffer里剩下的数据,而调用者要求读取下一条数据。
    const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0;

    bwr.write_size = outAvail;
    bwr.write_buffer = (uintptr_t)mOut.data();

    // This is what we'll read.
    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_LOG_COMMANDS
    // 实际调用驱动
    int ret = ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr);
    // ...
}

客户端通过Binder完成通信的整体源码流程总结

app发起调用:service.addPerson() → BinderProxy.transact() → android_util_binder.android_os_BinderProxy_transact() → BpBinder.transact() → IPCThreadState::transact → IPCThreadState::waitForResponse → talkWithDriver → ioctl


不过我们依然存在以下的疑问:

  1. BinderProxy什么时候出现的?
  2. android_util_binder是什么?
  3. BpBinder是什么?
  4. IPCThreadState是什么?

我们以ServiceManager如何完成client端的调用来分析这几个类到底是什么。

image.png

我们通过ServiceManager去获取一个服务的时候,一般都会调用getService方法,它的具体实现是这样的:

public static IBinder getService(String name) {
    try {
        IBinder service = sCache.get(name);
        if (service != null) {
            return service;
        } else {
            return Binder.allowBlocking(rawGetService(name));
        }
    } catch (RemoteException e) {
        Log.e(TAG, "error in getService", e);
    }
    return null;
}

private static IBinder rawGetService(String name) throws RemoteException {
    // 获取Binder对象。
    final IBinder binder = getIServiceManager().getService(name);
    
private static IServiceManager getIServiceManager() {
    if (sServiceManager != null) {
        return sServiceManager;
    }
    // Find the service manager,拿到的实际上就是ServiceManager的一个Binder代理对象。
    sServiceManager = ServiceManagerNative
            .asInterface(Binder.allowBlocking(BinderInternal.getContextObject()));
    return sServiceManager;
}

// ServiceManagerNative中
public static IServiceManager asInterface(IBinder obj) {
    if (obj == null) {
        return null;
    }
    // ServiceManager is never local
    return new ServiceManagerProxy(obj);
// 返回的就是一个ServiceManager代理。
public ServiceManagerProxy(IBinder remote) {
    mRemote = remote;
    mServiceManager = IServiceManager.Stub.asInterface(remote);
}

// BinderInternal.getContextObject()
public static final native IBinder getContextObject();

这里的IServiceManager其实就是一个AIDL。

image.png

我们发现,这里写的AIDL强转的方式,和我们自己在应用程序中写的AIDL其实是一样的:

image.png

ServiceManager由于是服务的管理类,所有的跨进程通信,只要是用AIDL的,就肯定需要它,那么我们必然无法通过ServiceConnection去获取它了,因为ServiceConnection也是ServiceManager中间的一环,如果ServiceManager不存在,那么ServiceConnection也无法触发。**ServiceManager相当于网络通信中的DNS服务器,它的地址是固定的。**我们获取ServiceManager对象的方式也和传统方式不同。那么是怎么拿到的呢?继续上边的getContextObject(),后边在android_util_Binder.cpp中会继续执行:

static const JNINativeMethod gBinderInternalMethods[] = {
    /* name, signature, funcPtr */
    { "getContextObject", "()Landroid/os/IBinder;", (void*)android_os_BinderInternal_getContextObject },
    { "joinThreadPool", "()V", (void*)android_os_BinderInternal_joinThreadPool },
    { "disableBackgroundScheduling", "(Z)V", (void*)android_os_BinderInternal_disableBackgroundScheduling },
    { "setMaxThreads", "(I)V", (void*)android_os_BinderInternal_setMaxThreads },
    { "handleGc", "()V", (void*)android_os_BinderInternal_handleGc },
    { "nSetBinderProxyCountEnabled", "(Z)V", (void*)android_os_BinderInternal_setBinderProxyCountEnabled },
    { "nGetBinderProxyPerUidCounts", "()Landroid/util/SparseIntArray;", (void*)android_os_BinderInternal_getBinderProxyPerUidCounts },
    { "nGetBinderProxyCount", "(I)I", (void*)android_os_BinderInternal_getBinderProxyCount },
    { "nSetBinderProxyCountWatermarks", "(II)V", (void*)android_os_BinderInternal_setBinderProxyCountWatermarks}
};

然后会进入这里继续执行:

static jobject android_os_BinderInternal_getContextObject(JNIEnv* env, jobject clazz)
{
    // 去Native层构建一个IBinder对象,实际拿到的就是BpBinder。
    sp<IBinder> b = ProcessState::self()->getContextObject(NULL);
    // 上边拿到的BpBinder,这个是一个Native对象,需要通过下边的转化成Java对象才能给Java使用。
    return javaObjectForIBinder(env, b);
}

ProcessState,每一个进程启动的时候都会首先启动。用来表示进程的状态。里边主要用来管理Binder驱动的初始化,Binder线程池的初始化,为我们的ServiceManager提供支持。

ProcessState::self()->getContextObject(NULL)中:

sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& /*caller*/) {
    // handle句柄0代表的就是serviceManager,所以这里调用getStrongProxyForHandle函数的参类。
    return getStrongProxyForHandle(0);
}

// 如果对应的BpBinder对象不存在,就直接new一个出来
wp<IBinder> ProcessState::getWeakProxyForHandle(int32_t handle) {
    wp<IBinder> result;
    AutoMutex _l(mLock);
    handle_entry* e = lookupHandleLocked(handle);
    if (e != nullptr) {
        IBinder* b = e->binder;
        if (b == nullptr || !e->refs->attemptIncWeak(this)) {
            // 如果对应的BpBinder对象不存在,就直接new一个出来,这也解释了为什么客户端拿到的是BpBinder。
            b = new BpHwBinder(handle);
            result = b;
            e->binder = b;
            if (b) e->refs = b->getWeakRefs();
        } else {
            result = b;
            e->refs->decWeak(this);
        }
    }
    // 如果对应的BpBinder对象不存在,就直接new一个出来
    return result;
}

BpBinder是对驱动的封装。

javaObjectForIBinder(env, b)中:

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

    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;
    }

    BinderProxyNativeData* nativeData = new BinderProxyNativeData();
    nativeData->mOrgue = new DeathRecipientList;
    nativeData->mObject = val;
    // 利用反射完成object的创建,这个其实就是代理类。gBinderProxyOffsets也是在int_register_android_os_BinderProxy完成初始化的,(jlong) val.get()代表的是BpBinder所在的地址
    jobject object = env->CallStaticObjectMethod(gBinderProxyOffsets.mClass,
            gBinderProxyOffsets.mGetInstance, (jlong) nativeData, (jlong) val.get()/*BpBinder地址*/);
    if (env->ExceptionCheck()) {
        // In the exception case, getInstance still took ownership of nativeData.
        return NULL;
    }
    BinderProxyNativeData* actualNativeData = getBPNativeData(env, object);
    if (actualNativeData == nativeData) {
        // Created a new Proxy
        uint32_t numProxies = gNumProxies.fetch_add(1, std::memory_order_relaxed);
        uint32_t numLastWarned = gProxiesWarned.load(std::memory_order_relaxed);
        if (numProxies >= numLastWarned + PROXY_WARN_INTERVAL) {
            // 多线程下确保只有一个线程能修改warn计数
            if (gProxiesWarned.compare_exchange_strong(numLastWarned,
                        numLastWarned + PROXY_WARN_INTERVAL, std::memory_order_relaxed)) {
                ALOGW("Unexpectedly many live BinderProxies: %d\n", numProxies);
            }
        }
    } else {
        delete nativeData;
    }
    return object;
}

gBinderProxyOffsets.mGetInstance,实际上是在BinderProxy里边完成实际的初始化的。

image.png

从上边的流程总结下,这就是ServiceManager的整体封装流程:

image.png

源码的调用流程:

ServiceManagerProxy(Stub.proxy)->IServiceManager.Stub.Proxy.getService()->android_util_binder.android_os_BinderProxy_transact()->BinderProxy.transact()->BpBinder.transact()

学后检测

单选题

1. 在Android Binder通信体系中,客户端通过ServiceManager拿到的Binder对象在Java层实际是哪类对象?

A. BBinder
B. BpBinder
C. BinderProxy
D. IBinder.Stub

答案:C
解析: ServiceManager返回的是BpBinder(C++层),然后用javaObjectForIBinder转换为Java层的BinderProxy对象。


2. 以下哪一项不是Android常见的进程间通信方式?

A. 管道
B. Socket
C. Binder
D. EventBus

答案:D
解析: EventBus是进程内的事件分发,不涉及IPC。

多选题

3. 关于Binder机制的说法,哪些是正确的?

A. Binder驱动是内核空间的虚拟设备
B. BBinder用于服务端,BpBinder用于客户端
C. 每个线程有独立的IPCThreadState实例
D. Binder通信完全不需要序列化

答案:A、B、C
解析: Binder需要序列化(用Parcel),D项错误。

4. 关于ServiceManager的获取,以下哪些说法是正确的?

A. 其地址在系统中是固定的
B. getContextObject()会返回一个BpBinder
C. 获取过程会用到JNI
D. 只能通过ServiceConnection拿到ServiceManager

答案:A、B、C
解析: D错误,ServiceManager不能用ServiceConnection获取。

判断题

5. BinderProxy对象在服务端创建,客户端使用。(判断对错)

答案:错
解析: BinderProxy是客户端用于代理服务端的Binder的Java对象,服务端只会有BBinder。

6. talkWithDriver方法中会通过ioctl与Binder驱动交互。(判断对错)

答案:对
解析: 源码清晰表明,最终系统调用是ioctl。

简答题

7. 为什么Binder通信的数据需要使用Parcel进行序列化?

答案:
因为跨进程通信需要将复杂的Java对象转化为可以跨进程传递的字节流,Binder驱动只认原始二进制格式,Parcel高效地实现了数据的序列化与反序列化。

8. 简述Binder通信过程中,客户端调用addPerson后,数据如何一步步送达服务端?

答案:

  1. 客户端AIDL代理类封装参数到Parcel
  2. 调用BinderProxy.transact,进入JNI(transactNative)
  3. JNI找到C++层BpBinder对象
  4. BpBinder.transact通过IPCThreadState发送数据
  5. IPCThreadState::talkWithDriver调用ioctl与Binder驱动通信
  6. 数据由驱动路由到服务端,服务端BBinder收到,回调onTransact,反序列化数据并执行业务逻辑

源码填空题

9. 填空:在ProcessState::getContextObject(NULL)函数里,handle为( )的BpBinder代表ServiceManager。

答案:0
解析: ServiceManager的Binder handle固定为0。

10. 填空:javaObjectForIBinder方法会将native层BpBinder包装成Java层的( )对象。

答案:BinderProxy
解析: 用于Java层跨进程代理。

开放编程题

11. 简述如何在Android中自定义AIDL服务,并让不同进程的Activity与之通信?

答案:

  1. 创建.aidl文件,定义接口和数据结构
  2. Service端实现Stub,注册Service并在AndroidManifest声明android:process属性
  3. 客户端复制.aidl到相同包路径下,通过bindService绑定远程Service
  4. 在onServiceConnected中用asInterface获取代理对象,调用方法即可