Binder通信传输callback时,为什么能实现unRegi

2020-12-06  本文已影响0人  浪里_个郎

问题描述

Android开发中,客户端通过Binder向服务端注册 / 去注册 callback,是我们常用的开发方式。了解Binder通信机制的同学应该会知道,我们通过Binder,把callback从客户端发送回调接口到服务端时,服务端获得的,其实是一份反序列化后new出来的IInterface实例。例如以下服务端接收代码:

status_t BnPreviewService::onTransact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    switch(code) {
        case START_PREVIEW: {
            CHECK_INTERFACE(IPreviewService, data, reply);
            
            sp<IGraphicBufferProducer> st =
                interface_cast<IGraphicBufferProducer>(data.readStrongBinder());
            int channel = data.readInt32();
            reply->writeInt32(startPreview(st,channel));
            return NO_ERROR;
        } break;

通过Binder,服务端接收到客户端发来的序列化打包的Parcel,用于构建一个新的IGraphicBufferProducer实例。
即使重复将同一个callback实例通过Binder发向服务端,服务端都会构建出一个新的IGraphicBufferProducer实例。这时候对比这两个实例,它们指向的地址必然是不同的。

我们为什么能够实现callback的去注册?

google为我们封装了一个管理callback的类:RemoteCallbackList,我们看看它是如何实现callback的注册和去注册的。

//RemoteCallbackList.java
    public boolean register(E callback, Object cookie) {
        synchronized (mCallbacks) {
            if (mKilled) {
                return false;
            }
            // Flag unusual case that could be caused by a leak. b/36778087
            logExcessiveCallbacks();
            IBinder binder = callback.asBinder();
            try {
                Callback cb = new Callback(callback, cookie);
                binder.linkToDeath(cb, 0);
                mCallbacks.put(binder, cb);
                return true;
            } catch (RemoteException e) {
                return false;
            }
        }
    }

最核心的两行:
1,通过IInterface接口的public IBinder asBinder()方法,获取IBinder接口的实例:

IBinder binder = callback.asBinder();

2,存储IBinder实例:

mCallbacks.put(binder, cb);

再看去注册回调的实现:

    public boolean unregister(E callback) {
        synchronized (mCallbacks) {
            Callback cb = mCallbacks.remove(callback.asBinder());
            if (cb != null) {
                cb.mCallback.asBinder().unlinkToDeath(cb, 0);
                return true;
            }
            return false;
        }
    }

关键在于下面这句:

Callback cb = mCallbacks.remove(callback.asBinder());

这里就有两个疑问:

1,是如何比较两个IBiner实例是否相同的?
2,IBinder实例为什么能作为Map的key进行正确的比较?

1,asBinder()返回的实例为什么能指向同一个BBinder?

先看注册callback时,从aidl文件自动生成的代码:

@Override public int registerServerCallBack(IMyCallback client) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeStrongBinder((((client!=null))?(client.asBinder()):(null)));
mRemote.transact(Stub.TRANSACTION_registerServerCallBack, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}

可以看到,传递的aidl接口序列化时调用了

Parcel.writeStrongBinder

而Parcel的这个接口,实际上通过JNI调用了Native接口:

    public final void writeStrongBinder(IBinder val) {
        nativeWriteStrongBinder(mNativePtr, val);
    }

跟踪代码:

/frameworks/base/core/jni/android_os_Parcel.cpp

static void android_os_Parcel_writeStrongBinder(JNIEnv* env, jclass clazz, jint nativePtr, jobject object)
{
    // 使用Native的Parcel方法进行实际的序列化
    Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
    if (parcel != NULL) {
        const status_t err = parcel->writeStrongBinder(ibinderForJavaObject(env, object));
        if (err != NO_ERROR) {
            signalExceptionForError(env, clazz, err);
        }
    }
}


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

sp<IBinder> ibinderForJavaObject(JNIEnv* env, jobject obj)
{
    if (obj == NULL) return NULL;

    if (env->IsInstanceOf(obj, gBinderOffsets.mClass)) { //mClass指向Java层中的Binder class
        JavaBBinderHolder* jbh = (JavaBBinderHolder*)
            env->GetIntField(obj, gBinderOffsets.mObject);
        return jbh != NULL ? jbh->get(env, obj) : NULL; //get() 返回一個JavaBBinder,继承自BBinder
    }

    if (env->IsInstanceOf(obj, gBinderProxyOffsets.mClass)) { //mClass 指向Java层的BinderProxy class
        return (IBinder*)
            env->GetIntField(obj, gBinderProxyOffsets.mObject); //返回一个BpBinder,mObject是它的地址值
    }
    ALOGW("ibinderForJavaObject: %p is not a Binder object", obj);
    return NULL;
}

序列化时,IBinder实际是BBinder类。Native的Parcel真正地序列化:

// framework/native/libs/binder/Parcel.cpp

status_t flatten_binder(const sp<ProcessState>& /*proc*/,
    const sp<IBinder>& binder, Parcel* out)
{
    flat_binder_object obj;

    if (IPCThreadState::self()->backgroundSchedulingDisabled()) {
        /* minimum priority for all nodes is nice 0 */
        obj.flags = FLAT_BINDER_FLAG_ACCEPTS_FDS;
    } else {
        /* minimum priority for all nodes is MAX_NICE(19) */
        obj.flags = 0x13 | FLAT_BINDER_FLAG_ACCEPTS_FDS;
    }

    if (binder != NULL) {
        IBinder *local = binder->localBinder();
        if (!local) {
            BpBinder *proxy = binder->remoteBinder();
            if (proxy == NULL) {
                ALOGE("null proxy");
            }
            const int32_t handle = proxy ? proxy->handle() : 0;
            obj.hdr.type = BINDER_TYPE_HANDLE;
            obj.binder = 0; /* Don't pass uninitialized stack data to a remote process */
            obj.handle = handle;
            obj.cookie = 0;
        } else {            
            // 因为打包时callback属于BBinder,所以local不是null,走这个分支
            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);
}

结论:
callback传递时,序列化的过程就是将BBinder的地址放进了Pacel中。

服务端反序列化代码:

case TRANSACTION_registerServerCallBack:
{
data.enforceInterface(descriptor);
IMyCallback _arg0;
_arg0 = IMyCallback.Stub.asInterface(data.readStrongBinder());
int _result = this.registerServerCallBack(_arg0);
reply.writeNoException();
reply.writeInt(_result);
return true;
}

data.readStrongBinder()其实就是返回了一个指向BBinder地址的IBinder对象。
调用Stub.asInterface后,得到的就是Proxy了。参考以下代码:

        public static android.os.IMessenger asInterface(android.os.IBinder obj)
        {
            if ((obj==null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin!=null)&&(iin instanceof android.os.IMessenger))) {
                return ((android.os.IMessenger)iin);
            }
            return new android.os.IMessenger.Stub.Proxy(obj);
        }

Proxy会保存BBinder的地址:


        private static class Proxy implements android.os.IMessenger
        {
            private android.os.IBinder mRemote;
            Proxy(android.os.IBinder remote)
            {
            mRemote = remote;
            }
            
            @Override public android.os.IBinder asBinder()
            {
                return mRemote;
            }

所以,属于同一个BBinder的Proxy,通过asBinder方法得到的IBinder对象的地址都是一样的。

2,为什么IBinder实例能作为Map的key进行正确的比较?

我们已经知道,Proxy.asBinder能够获取到IBinder接口对象。这个C++对象,在java端对应的是BinderProxy类。具体可以看反序列化的那段代码。

vfinal class BinderProxy implements IBinder {
    // See android_util_Binder.cpp for the native half of this.

    private static final class ProxyMap {

        /**
         * Hash function tailored to native pointers.
         * Returns a value < MAIN_INDEX_SIZE.
         */
        private static int hash(long arg) {
            return ((int) ((arg >> 2) ^ (arg >> (2 + LOG_MAIN_INDEX_SIZE)))) & MAIN_INDEX_MASK;
        }

在java层,我们使用IBinder作为Map的Key时,它的Hash值就是来源于此。

上一篇下一篇

猜你喜欢

热点阅读