美文网首页
Binder通信传输callback时,为什么能实现unRegi

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

作者: 浪里_个郎 | 来源:发表于2020-12-06 00:18 被阅读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值就是来源于此。

    相关文章

      网友评论

          本文标题:Binder通信传输callback时,为什么能实现unRegi

          本文链接:https://www.haomeiwen.com/subject/vvebwktx.html