天天看點

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);           

同程序時,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

跨程序時,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。

繼續閱讀