• Android binder 匿名服务实现双向通信


    在binder 用户空间通信模型中,涉及client,server和servicemanager进程。一般来说,都是server注册服务到servicemanager中,client从servicemanager中获取服务,然后由client发起,使用服务中的方法。server都是被动的接收client发起的请求。那如果server想主动的发起请求调用client中的方法,应该怎么做呢?
    实现上面的需求,首先可以想到的是,client也向servicemanager注册一个服务,server中从servicemanager获取服务,这样client就变成了服务端,server就变成了客户端,不就可以实现吗?
    当然,这种方案是可行的,只是需要client和server都向servicemanager注册一个服务,实现起来有点麻烦,不太建议这么做。完全可以使用匿名服务来实现双向通信的需求
    1,背景知识
    在注册服务时,通过调用Parcel的writeStrongBinder,会构造一个flat_binder_obj结构体,其中的type为BINDER_TYPE_BINDER,binder驱动对于type是BINDER_TYPE_BINDER,则会生成一个binder_node,并为servicemanager创建binder_ref,而且还会将这个flat_binder_obj的type改为BINDER_TYPE_HANDLE,将binder_ref中的desc存入flat_binder_obj的handle,传给servicemanager,在servicemanager中记录其信息。这个过程就是binder实名服务的注册过程。

    2,匿名服务是什么?
    binder_node代表一个服务的实体。了解了上面的背景知识后知道writeStrongBinder会导致在驱动中创建一个binder_node,那么我们可不可以直接在client和server通信的过程中,调用writeStrongBinder,而不需要通过添加服务在servicemanager中记录这个服务的信息呢?答案是可以的,这就是匿名服务。匿名服务需要依赖于server已经注册好的实名服务

    3,匿名服务在系统源码中的使用
    应用进程和wms进行通信,通常是借助于一个IWindowSession对象。我们来看一下其构造过程

    @UnsupportedAppUsage
        public static IWindowSession getWindowSession() {
            synchronized (WindowManagerGlobal.class) {
                if (sWindowSession == null) {
                    try {
                        // Emulate the legacy behavior.  The global instance of InputMethodManager
                        // was instantiated here.
                        // TODO(b/116157766): Remove this hack after cleaning up @UnsupportedAppUsage
                        InputMethodManager.ensureDefaultInstanceForDefaultDisplayIfNecessary();
                        IWindowManager windowManager = getWindowManagerService();//1
                        sWindowSession = windowManager.openSession( //2
                                new IWindowSessionCallback.Stub() {
                                    @Override
                                    public void onAnimatorScaleChanged(float scale) {
                                        ValueAnimator.setDurationScale(scale);
                                    }
                                });
                    } catch (RemoteException e) {
                        throw e.rethrowFromSystemServer();
                    }
                }
                return sWindowSession;
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    注释1处获取一个IWindowManager.Stub.Proxy对象,不在本分分析的重点。注释2处通过调用openSession获取一个IWindowSession对象。

    @Override public android.view.IWindowSession openSession(android.view.IWindowSessionCallback callback, com.android.internal.view.IInputMethodClient client, com.android.internal.view.IInputContext inputContext) throws android.os.RemoteException
    {
    android.os.Parcel _data = android.os.Parcel.obtain();
    android.os.Parcel _reply = android.os.Parcel.obtain();
    android.view.IWindowSession _result;
    try {
    _data.writeInterfaceToken(DESCRIPTOR);
    _data.writeStrongBinder((((callback!=null))?(callback.asBinder()):(null)));
    _data.writeStrongBinder((((client!=null))?(client.asBinder()):(null)));
    _data.writeStrongBinder((((inputContext!=null))?(inputContext.asBinder()):(null)));
    mRemote.transact(Stub.TRANSACTION_openSession, _data, _reply, 0);//1
    _reply.readException();
    _result = android.view.IWindowSession.Stub.asInterface(_reply.readStrongBinder());//2
    }
    finally {
    _reply.recycle();
    _data.recycle();
    }
    return _result;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    可以看出,注释1处发起远程调用,然后注释2处从_reply中取出数据,转化之后返回。那我们来看看服务端的处理

    case TRANSACTION_openSession:
    {
    data.enforceInterface(DESCRIPTOR);
    android.view.IWindowSessionCallback _arg0;
    _arg0 = android.view.IWindowSessionCallback.Stub.asInterface(data.readStrongBinder());
    com.android.internal.view.IInputMethodClient _arg1;
    _arg1 = com.android.internal.view.IInputMethodClient.Stub.asInterface(data.readStrongBinder());
    com.android.internal.view.IInputContext _arg2;
    _arg2 = com.android.internal.view.IInputContext.Stub.asInterface(data.readStrongBinder());
    android.view.IWindowSession _result = this.openSession(_arg0, _arg1, _arg2);
    reply.writeNoException();
    reply.writeStrongBinder((((_result!=null))?(_result.asBinder()):(null)));//1
    return true;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    注释1处也是调用writeStrongBinder,直接写给客户端。结合前面的背景知识,就知道这是一个binder匿名服务(并没有先向servicemanager获取)。匿名服务的实现也是通过writeStrongBinder来实现的,客户端通过readStrongBinder来取出binder驱动转化过后的flat_binder_obj,取出handle并存入BpBinder中

    4,匿名服务双向通信实战

    在我们的日常开发中,通常有这样的需求,服务端更新了某种状态需要实时的通知给客户端。那么我们就可以在客户端通过writeStrongBinder创建一个binder匿名服务供服务端使用。

    1,在客户端和服务端新建一个用于服务端通知客户端的aidl文件,如:ICallBack.aidl。注意包名需要一致

    // ICallBack.aidl
    package com.test.testserver;
    
    // Declare any non-default types here with import statements
    
    interface ICallBack {
        void onSucess(int code);
        void onError();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    2,在客户端和服务端本来的通信文件中(客户端发起和服务端通信的aidl文件),新增接口

    // ITestInterface.aidl
    package com.test.testserver;
    
    import com.test.testserver.ICallBack;
    // Declare any non-default types here with import statements
    
    interface ITestInterface {
        void registerCallBack(ICallBack callback);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    3,客户端调用 registerCallBack

    binder.registerCallBack(new ICallBack.Stub() {
                        @Override
                        public void onSucess(int code) throws RemoteException {
                            Log.d("test", "onSucess code: "+code);
                        }
    
                        @Override
                        public void onError() throws RemoteException {
                            Log.d("test", "onError: ");
                        }
                    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    4,服务端接收到之后,做自己的处理

    @Override
        public void registerCallBack(ICallBack callback) throws RemoteException {
            this.callBack = callback;
        }
    
    • 1
    • 2
    • 3
    • 4

    我这里只是将客户端传过来的ICallBack 赋值给自己的callBack 对象

    5,服务端需要通知时,调用ICallBack 中的方法

    callBack.onError();
    或者
    callBack.onSucess(1);
    
    • 1
    • 2
    • 3

    最后我们来看看,内部实现是不是通过writeStrongBinder和readStrongBinder来实现
    客户端发起调用:

    @Override public void registerCallBack(com.test.testserver.ICallBack callback) throws android.os.RemoteException
          {
            android.os.Parcel _data = android.os.Parcel.obtain();
            android.os.Parcel _reply = android.os.Parcel.obtain();
            try {
              _data.writeInterfaceToken(DESCRIPTOR);
              _data.writeStrongBinder((((callback!=null))?(callback.asBinder()):(null)));//1
              boolean _status = mRemote.transact(Stub.TRANSACTION_registerCallBack, _data, _reply, 0);
              if (!_status && getDefaultImpl() != null) {
                getDefaultImpl().registerCallBack(callback);
                return;
              }
              _reply.readException();
            }
            finally {
              _reply.recycle();
              _data.recycle();
            }
          }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    注释1处可以看出是通过writeStrongBinder,构建一个匿名服务
    服务端:

    case TRANSACTION_registerCallBack:
            {
              data.enforceInterface(descriptor);
              com.test.testserver.ICallBack _arg0;
              _arg0 = com.test.testserver.ICallBack.Stub.asInterface(data.readStrongBinder());//1
              this.registerCallBack(_arg0);
              reply.writeNoException();
              return true;
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    注释1处是通过readStrongBinder取出来并转化为ICallBack.Stub.proxy对象,用于和客户端通信

  • 相关阅读:
    js setTimeout()与面试题
    LeetCode_数学分析_中等_754.到达终点数字
    java线程和go协程
    Matlab:在多行上延续长语句
    C语言基础Day7-结构体
    【Pytorch with fastai】第 9 章 :表格建模深入探讨
    Defocus(散焦)
    P251——用RadialGradientBrush填充椭圆,并进行RotateTransform变换
    【GD32F310开发板试用】Keil编程环境配置、避坑
    MySQL-Q&A-异常问题及解决方案(持续更新)
  • 原文地址:https://blog.csdn.net/littleyards/article/details/138151234