AIDL的思考——asInterface判断是否为同一个进程的依据+不同进程是怎么访问到asInterface方法的

看书《Android插件化开发指南》带来的思考

asInterface判断是否为同一个进程的依据

《Android插件化开发指南》中有一句话:

从 Client 看,对于 AIDL 的使用者,我们写程序:

MyAidl.Stub.asInterface(某IBinder对象).sum(1, 2);

asInterface 方法的作用是判断参数,也就是 IBinder 对象和自己是否在同一个进程··· ···

由此,想到了以下三个问题:

问题1:“某IBinder对象” 指的是谁?

答:从结果来看分为 同进程调用跨进程调用 两种情况,以下面的代码段为例,

MyAidl.Stub.asInterface(mRemote).sum(1,2);

1)同进程调用

mRemote 为 MyAidl.Stub 的子类实例。比如说 mRemote 是 AMS 的实例,其是 IActivityManager.Stub 的子类。

2)跨进程调用

mRemote 为 android.os.BinderProxy 类型

那么,参数 mRemote 是怎么赋值的呢?

1、以 AMN.getDefault() 为例,其对应的 mRemote 为 IBinder b = ServiceManager.getService("activity")
//android.os.ServiceManager.java
private static HashMap<String, IBinder> sCache = new HashMap<String, IBinder>();
public static IBinder getService(String name) {
      try {
          IBinder service = sCache.get(name);
          //getService("activity")走的是else,具体原因见下面的 1.1
          if (service != null) {
               return service;
          } else {
          //代码流程见 1.2
               return getIServiceManager().getService(name);
          }
      } catch (RemoteException e) {
              Log.e(TAG, "error in getService", e);
      }
      return null;
}
public static void initServiceCache(Map<String, IBinder> cache) {
       if (sCache.size() != 0) {
           throw new IllegalStateException("setServiceCache may only be called once");
       }
       sCache.putAll(cache);
}

1.1 getService("activity")走的是else,具体原因见下面

initServiceCache 的调用链如下所示:

1、
SystemServer.main
    SystemService.run
        SystemServer.createSystemContext
            ActivityThread.attach(true)
                AMS.attachApplication
                    AMS.attachApplicationLocked
                        ActivityThread.bindApplication
                            ServiceManager.initServiceCache
 
2、
ActivityThread.main
    ActivityThread.attach(false)
        AMS.attachApplication
            AMS.attachApplicationLocked
                ActivityThread.bindApplication
                    ServiceManager.initServiceCache

initServiceCache 的参数来源于 bindApplicaton 的参数 AMS.getCommonServiceLocked

///frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
private HashMap<String, IBinder> getCommonServicesLocked(boolean isolated) {
    // Isolated processes won't get this optimization, so that we don't
    // violate the rules about which services they have access to.
    if (isolated) {
        if (mIsolatedAppBindArgs == null) {
            mIsolatedAppBindArgs = new HashMap<>();
            mIsolatedAppBindArgs.put("package", ServiceManager.getService("package"));
        }
        return mIsolatedAppBindArgs;
    }
 
    if (mAppBindArgs == null) {
        mAppBindArgs = new HashMap<>();
 
        // Setup the application init args
        mAppBindArgs.put("package", ServiceManager.getService("package"));
        mAppBindArgs.put("window", ServiceManager.getService("window"));
        mAppBindArgs.put(Context.ALARM_SERVICE,
                ServiceManager.getService(Context.ALARM_SERVICE));
    }
    return mAppBindArgs;
}

可见,sCache 中仅仅包含了 package、window、alarm服务,不包含 activity 服务,所以在 sCache 中找不到activity。如果不在 system_server 进程,package、window、alarm也是Binder代理,只不过是先保存起来了。

1.2 getIServiceManager().getService("activity")

返回一个远端 Binder 代理

ServiceManager.getService的流程分析

综上,我的理解是,

1)asInterface(某IBinder对象)中的 IBinder 对象,是通过 ServiceManager.getService("activity") 或者 ServiceManager.getService("package") 等等根据具体请求的服务类型而得到的;

2)当前进程为 system_server 进程时,ServiceManager.getService("activity") 直接返回 AMS 实例的引用(见system_service进程里 调用SystemManager.getService("activity") 直接返回ams的引用?

3)当前进程不是 system_server 进程时,ServiceManager.getService("activity") 返回一个远程 Binder 代理

问题2:AMS,为什么不能直接实例化AMS然后调用其StartActivity方法呢,而是要调用 asInterface(某IBinder对象).startActivity 方法呢?

答:Android进程学习笔记:https://blog.csdn.net/aaqian1/article/details/109295579
代码段共享 指的是 “机器中有数个进程运行相同的一个程序,那么它们就可以使用相同的代码段 ”。但是 system_server 进程中的类(如AMS)可以理解为一个新的应用程序中的代码,所以并不属于共享的代码段,所以不能在自己的代码中直接实例化AMS,或者直接调用其中AMS中的静态方法和变量。

同时,根据问题1也可得知,与AMS同进程时,asInterface的参数为 AMS 的实例;与AMS跨进程时,asInterface的参数为一个远程Binder代理对象。

所以,引出了问题3。

问题3:asInterface()方法,是怎么判断出 AMS 是在不同的进程中的呢?

AIDL的思考——asInterface判断是否为同一个进程的依据+不同进程是怎么访问到asInterface方法的

//android.app.ActivityManagerNative
    private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
          protected IActivityManager create() {
              IBinder b = ServiceManager.getService("activity");
              if (false) {
                  Log.v("ActivityManager", "default service binder = " + b);
              }
              IActivityManager am = asInterface(b);
              if (false) {
                  Log.v("ActivityManager", "default service = " + am);
              }
              return am;
          }
     };

    static public IActivityManager asInterface(IBinder obj) {
          if (obj == null) {
              return null;
          }
          IActivityManager in =
              (IActivityManager)obj.queryLocalInterface(descriptor);  
          if (in != null) { //如果in不为null,说明是同一个进程;否则,不是同一个进程
              return in;
          }
          return new ActivityManagerProxy(obj);
    }

从上述代码可以看出,通过判断 ServiceManager.getService(“activity”).queryLocalInterface(descriptor) 即可判断出,是否在同一个进程中。

由问题1可知,同进程时,调用的是 (AMS实例).queryLocalInterface(descriptor) ;跨进程时,调用的是(远程Binder代理对象).queryLocalInterface(descriptor) 。

接下来将以VA为例,AMS将替换为VAMS

还是以下述代码段为例:

MyAidl.Stub.asInterface(mRemote).sum(1,2);

1)同进程调用

同进程时,mRemote 为 MyAidl.Stub 的子类实例

此时 mRemote.queryLocalInterface 方法来源于超类 android.os.Binder,参数来源于父类 MyAidl.Stub 中定义的 DESCRIPTOR。

//android.os.Binder.java
private IInterface mOwner;
private String mDescriptor;
public void attachInterface(IInterface owner, String descriptor) {
       mOwner = owner;
       mDescriptor = descriptor;
}
public IInterface queryLocalInterface(String descriptor) {
       if (mDescriptor.equals(descriptor)) {
           return mOwner;
       }
       return null;
}

Binder 中的 attachInterface 方法是在 MyAidl.Stub 的构造函数中调用的。MyAidl.Stub 是 Binder 的子类。

Stub 构造函数是在 子类 VActivityManagerService 实例化时执行的,在VirtualApp中是在 BinderProvider 的 onCreate 方法中初始化这些服务的。

//MyAidl.Stub
public Stub(){
    this.attachInterface(this, DESCRIPTOR);  //子类实例化,父类的构造函数中的this指向子类对象
}

所以,同进程时,queryLocalInterface 方法的返回值为 VAMS 实例对象本身!

之前卡住我好几天的难点的一个点找到了,子类实例化时调用父类构造函数时,父类构造函数的this指向子类对象。参考:Java学习1-子类实例化时调用父类构造函数时this指向

下面是我自己写的一个例子:

public interface Stub {
    public class People{
        public People() {
            System.out.println("People::constructor:::" + this);
        }
    }
    class Student extends People{
        public Student() {
            System.out.println("Student::constructor:::" + this);
        }
    }
}
public class Test {  
    public static void main(String[] args) {  
        new Stub.Student();
    }  
}  

运行Test.java,结果如下:

People::constructor:::leetcode.Stub$Student@4c203ea1
Student::constructor:::leetcode.Stub$Student@4c203ea1

2)跨进程调用

跨进程时,mRemote 为 android.os.BinderProxy 类型

BinderProxy 为 final 类,其 queryLocalInterface 方法直接返回 null 值:

public IInterface queryLocalInterface(String descriptor) {
    return null;
}

所以 asInterface 方法判断出 AMS 与应用程序不在同一个进程 是依靠 asInterface 的参数 mRemote。

同进程时,mRemote 为 AMS 的实例,mRemote.queryLocalInterface(Descriptor) 返回的是 AMS 实例对象本身;跨进程时,mRemote 为 BinderProxy 类型,mRemote.queryLocalInterface(Descriptor) 返回null。

上一篇:linux设备驱动--等待队列实现


下一篇:Linux设备驱动中的阻塞与非阻塞I/O