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

简介: asInterface判断是否为同一个进程的依据。由此引出我的三个问题:1)asInterface 方法的参数指的是谁;2)AMS,为什么不能直接实例化AMS然后调用其StartActivity方法呢,而是要调用 asInterface(某IBinder对象).startActivity 方法呢?3)asInterface()方法,是怎么判断出 AMS 是在不同的进程中的呢?
看书《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 代理

综上,我的理解是,

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.png

//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。

相关文章
|
1月前
|
存储 监控
【Azure Cloud Service】在Azure云服务中收集CPU监控指标和IIS进程的DUMP方法
在使用Cloud Service服务时,发现服务的CPU占用很高,在业务请求并不大的情况下,需要直到到底是什么进程占用了大量的CPU资源,已经如何获取IIS进程(w3wp.exe)的DUMP文件?
|
13天前
|
编译器
【收藏】内核级利用通用Hook函数方法检测进程
【收藏】内核级利用通用Hook函数方法检测进程
|
5月前
|
Linux
【Linux】命名管道的创建方法&&基于命名管道的两个进程通信的实现
【Linux】命名管道的创建方法&&基于命名管道的两个进程通信的实现
|
5月前
|
安全 Linux 开发者
⭐⭐⭐⭐⭐Linux C/C++ 进程崩溃诊断以及有效数据收集:解锁代码问题快速定位与修复的方法
⭐⭐⭐⭐⭐Linux C/C++ 进程崩溃诊断以及有效数据收集:解锁代码问题快速定位与修复的方法
343 1
|
2月前
|
数据安全/隐私保护 异构计算 Windows
【Azure 环境】 介绍两种常规的方法来监视Window系统的CPU高时的进程信息: Performance Monitor 和 Powershell Get-Counter
【Azure 环境】 介绍两种常规的方法来监视Window系统的CPU高时的进程信息: Performance Monitor 和 Powershell Get-Counter
|
2月前
|
Linux Perl
在Linux中,系统目前有许多正在运行的任务,在不重启机器的条件下,有什么方法可以把所有正在运行的进程移除呢?
在Linux中,系统目前有许多正在运行的任务,在不重启机器的条件下,有什么方法可以把所有正在运行的进程移除呢?
|
2月前
|
机器学习/深度学习 数据可视化 搜索推荐
低代码开发是一种能够加速软件研发进程的高效开发方法
【8月更文挑战第4天】低代码开发是一种能够加速软件研发进程的高效开发方法
46 0
|
3月前
|
Python
Python的`signal`模块提供了访问底层操作系统提供的信号机制的方式。信号是操作系统用来通知进程发生了某种情况(如用户按下Ctrl+C)的一种机制。
Python的`signal`模块提供了访问底层操作系统提供的信号机制的方式。信号是操作系统用来通知进程发生了某种情况(如用户按下Ctrl+C)的一种机制。
|
5月前
|
Linux 开发者
【亮剑】Linux系统中的四种文件锁定机制:flock、fcntl、lockfile和flockfile,用于多进程环境下协调共享资源访问,防止数据损坏和竞争条件
【4月更文挑战第30天】本文介绍了Linux系统中的四种文件锁定机制:flock、fcntl、lockfile和flockfile,用于多进程环境下协调共享资源访问,防止数据损坏和竞争条件。flock适合脚本,fcntl提供底层灵活性,lockfile用于管理锁定文件,flockfile则结合两者功能。选择锁定策略时需考虑应用场景,如脚本可选flock,复杂需求则用fcntl。理解并正确使用这些工具对保证系统稳定性和数据一致性至关重要。
289 1
|
5月前
|
Python
多ip多进程代理的实现方法
多ip多进程代理的实现方法
133 7

相关实验场景

更多