AIDL的一次调用链路追踪

9 阅读3分钟

AIDL 的用法

Step 1 定义通信协议

先看一段代码,这个是一个很简单的计算,A进程发起计算,B进程进行计算,返回结果。

interface ICalculatorService {
    String add(int a, int b, );
}

编译后得到AIDL文件,我们拆分一下客户端进程A使用类 和 进程B服务端使用类:

Step 2 客户端A进程代码实现

客户端A进程使用aidl做加减:

private var calculatorService: ICalculatorService? = null

private val serviceConnection = object : ServiceConnection {
    override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
        calculatorService = ICalculatorService.Stub.asInterface(service)
        isBound = true
        callRemoteAdd(3, 5)
    }

    override fun onServiceDisconnected(name: ComponentName?) {
        calculatorService = null
        isBound = false
    }
}


private fun callRemoteAdd(a: Int, b: Int) {
    try {
        val result = calculatorService?.add(a, b)
        val mainProcess = currentProcessName()
        val message = "主进程: $mainProcess\n调用 add($a, $b)\n返回: $result"
        binding.sampleText.text = message
        Log.d(TAG, message)
    } catch (e: RemoteException) {
        binding.sampleText.text = "AIDL 调用失败: ${e.message}"
        Log.e(TAG, "AIDL call failed", e)
    }
}


private fun bindCalculatorService() {
    val intent = Intent(this, CalculatorService::class.java)
    isBound = bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)
    if (!isBound) {
        binding.sampleText.text = "绑定 CalculatorService 失败"
    }
}

这个是客户端的代码,点击按钮调用 bindCalculatorService(),回调中返回了onServiceConnected IBinder,然后ICalculatorService.Stub.asInterface 转为Proxy,我们看一看Proxy都做了什么:

public static com.example.wu.myapplication.ICalculatorService asInterface(android.os.IBinder obj){                                                                                  
    return new com.example.wu.myapplication.ICalculatorService.Stub.Proxy(obj);                                                                 
  }  

把一个远程的iBinder对象包装了一下变为ICalculatorService.Stub.Proxy 我们再看看这个ICalculatorService.Stub.Proxy都干了什么

private static class Proxy implements com.example.wu.myapplication.ICalculatorService
  {
    private android.os.IBinder mRemote;
    Proxy(android.os.IBinder remote)
    {
      mRemote = remote;
    }
    @Override public android.os.IBinder asBinder()
    {
      return mRemote;
    }
    public java.lang.String getInterfaceDescriptor()
    {
      return DESCRIPTOR;
    }
    @Override public java.lang.String add(int a, int b) throws android.os.RemoteException
    {
      android.os.Parcel _data = android.os.Parcel.obtain();
      android.os.Parcel _reply = android.os.Parcel.obtain();
      java.lang.String _result;
      try {
        _data.writeInterfaceToken(DESCRIPTOR);
        _data.writeInt(a);
        _data.writeInt(b);
        boolean _status = mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
        _reply.readException();
        _result = _reply.readString();
      }
      finally {
        _reply.recycle();
        _data.recycle();
      }
      return _result;
    }
  }
  static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}

主要看add 做了什么 1.把数据入参使用Parcel封装一下 2.mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0); 3._reply.readException(); _result = _reply.readString();

这个看起来就是我拿到了一个B进程的一个binder,AIDL文件帮我们生成帮助代理类,实际是用B进程返回的binder进行transact调用,等待结果。

Step 3 进程A怎么得到的binder对象

image.png

具体流程如下:

1)Activity作为Client发起bindService,最终会调度到AMS去执行bindService。在这个过程中,Client要去调用AMS的代码,所以此时就会涉及到跨进程调度,基于第三章的Binder通信模型我们不难知道,Client会先和ServiceManager通信,从ServiceManager中拿到AMS的IBinder。

2)Activity拿到AMS的IBinder后,跨进程执行AMS的BindService函数;

3)由于AMS管理所有的应用进程,因此AMS中持有了应用进程的Binder,所以此时AMS可以发起第4步也就是跨进程调度scheduleBindService();

4)Server端会在收到AMS的bindService的请求后,会将自己的IBinder发送给Client,但是Server必须通过AMS才能将Binder对象传过去,所以此时需要跨进程从ServiceManager中去拿到AMS的binder;

5)Serve端通过AMS的binder直接调用AMS的代码publishService(),将service的Binder发送给AMS;

6)经过层层调用,最终AMS讲Server端的binder通过回调connect函数传递给了Client端的Activity;

以上就是bindService的全流程,这个流程主要的目的是将server端的Binder对象发送给Client端。从此以后,Client端就可以通过Server端的binder与Server端像调用自己的代码一样完成跨进程通信了。

下面是一步一步的分析

bindService() 会先通过 ServiceManager 找到系统的 activity 服务,也就是 ActivityManagerService

// ContextImpl.java

private boolean bindServiceCommon(Intent service, ServiceConnection conn, long flags,
        String instanceName, Handler handler, Executor executor, UserHandle user) {
    ...
    int res = ActivityManager.getService().bindServiceInstance(
            mMainThread.getApplicationThread(), getActivityToken(), service,
            service.resolveTypeIfNeeded(getContentResolver()),
            sd, flags, instanceName, getOpPackageName(), user.getIdentifier());

ActivityManager.getService() 这里才会查 ServiceManager,拿到的是 AMS:

// ActivityManager.java

public static IActivityManager getService() {
    return IActivityManagerSingleton.get();
}

private static final Singleton<IActivityManager> IActivityManagerSingleton =
        new Singleton<IActivityManager>() {
            @Override
            protected IActivityManager create() {
                final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
                final IActivityManager am = IActivityManager.Stub.asInterface(b);
                return am;
            }
        };

到了 system_server 以后,AMS 转给 ActiveServices

// ActivityManagerService.java

synchronized (this) {
    return mServices.bindServiceLocked(caller, token, service, resolvedType, connection,
            flags, instanceName, isSdkSandboxService, sdkSandboxClientAppUid,
            sdkSandboxClientAppPackage, sdkSandboxClientApplicationThread,
            callingPackage, userId);
}

ActiveServices 再根据 IntentComponentName、包名、userId 等去解析并维护 ServiceRecordConnectionRecord,而不是查 native ServiceManager

// ActivityManagerService.java

ServiceLookupResult res = retrieveServiceLocked(service, instanceName,
        isSdkSandboxService, sdkSandboxClientAppUid, sdkSandboxClientAppPackage,
        resolvedType, callingPackage, callingPid, callingUid, userId, true, callerFg,
        isBindExternal, allowInstant, null /* fgsDelegateOptions */,
        inSharedIsolatedProcess, inPrivateSharedIsolatedProcess, matchQuarantined);
...
ServiceRecord s = res.record;
final AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp, attributedApp);

服务进程起来后,AMS 会让目标 app 执行 Service.onBind()

// ActivityServices.java

r.app.getThread().scheduleBindService(r, i.intent.getIntent(), rebind,
        r.app.mState.getReportedProcState(), mBindServiceSeqCounter++);

目标 app 的 onBind() 返回的通常就是你 AIDL 生成的 Stub 对象,我们这里说一下B进程的服务端:


private val binder = object : ICalculatorService.Stub() {
        override fun add(a: Int, b: Int): String {
            val processName = currentProcessName()
            val sum = a + b
            Log.d(TAG, "add($a, $b) = $sum in process=$processName, pid=${android.os.Process.myPid()}")
            return "$processName: $sum"
        }
    }
    
    override fun onBind(intent: Intent?): IBinder {
        Log.d(TAG, "onBind in process=${currentProcessName()}, pid=${android.os.Process.myPid()}")
        return binder
    }

我们再看看stub的定义:

public static abstract class Stub extends android.os.Binder implements com.example.wu.myapplication.ICalculatorService                          
{                                                                                                                                               
  /** Construct the stub at attach it to the interface. */                                                                                      
  @SuppressWarnings("this-escape")                                                                                                              
  public Stub()                                                                                                                                 
  {                                                                                                                                             
    this.attachInterface(this, DESCRIPTOR);                                                                                                     
  }                                                                                                                                             
  /**                                                                                                                                           
   * Cast an IBinder object into an com.example.wu.myapplication.ICalculatorService interface,                                                  
   * generating a proxy if needed.                                                                                                              
   */                                                                                                                                           

  @Override public android.os.IBinder asBinder()                                                                                                
  {                                                                                                                                             
    return this;                                                                                                                                
  }                                                                                                                                             
  @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException   
  {                                                                                                                                             
    java.lang.String descriptor = DESCRIPTOR;                                                                                                   
    if (code >= android.os.IBinder.FIRST_CALL_TRANSACTION && code <= android.os.IBinder.LAST_CALL_TRANSACTION) {                                
      data.enforceInterface(descriptor);                                                                                                        
    }                                                                                                                                           
    if (code == INTERFACE_TRANSACTION) {                                                                                                        
      reply.writeString(descriptor);                                                                                                            
      return true;                                                                                                                              
    }                                                                                                                                           
    switch (code)                                                                                                                               
    {                                                                                                                                           
      case TRANSACTION_add:                                                                                                                     
      {                                                                                                                                         
        int _arg0;                                                                                                                              
        _arg0 = data.readInt();                                                                                                                 
        int _arg1;                                                                                                                              
        _arg1 = data.readInt();                                                                                                                 
        java.lang.String _result = this.add(_arg0, _arg1);                                                                                      
        reply.writeNoException();                                                                                                               
        reply.writeString(_result);                                                                                                             
        break;                                                                                                                                  
      }                                                                                                                                         
      default:                                                                                                                                  
      {                                                                                                                                         
        return super.onTransact(code, data, reply, flags);                                                                                      
      }                                                                                                                                         
    }                                                                                                                                           
    return true;                                                                                                                                
  }                                                                                                                                             
                                                                                     
  static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);                                                           
}                                                                                                                                               

Stub也就是一个 Java Binder。这个 IBinder 再通过 AMS 回调给客户端:

// ActivityServices.java


b.binder = service;
b.requested = true;
b.received = true;
...
c.conn.connected(clientSideComponentName, service, false);

客户端 ServiceConnection.onServiceConnected(name, binder) 收到这个 binder 后,你通常写:

ICalculatorService.Stub.asInterface(service)

这就攒起来了,从客户端A出发,拿到服务端B的一个binder了。

Step 4 一次add(1,2)怎么拿到的结果

            ...
mRemote.transact(TRANSACTION_add, _data, _reply, 0);

transact 做了什么 如果是跨进程,mRemote 通常是 BinderProxy。 AOSP 里 BinderProxy.transact() 最终会调用 native:

// BinderProxy.java

public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
    Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");
    // ...
    final boolean result = transactNative(code, data, reply, flags);

JNI 层在 android_util_Binder.cpp

// android_util_Binder.cpp

static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,
        jint code, jobject dataObj, jobject replyObj, jint flags)
{
    // ...
    IBinder* target = getBPNativeData(env, obj)->mObject.get();
    // ...
    status_t err = target->transact(code, *data, reply, flags);

native Binder 再进入 IPCThreadState::transact(),写 BC_TRANSACTION 给 Binder 驱动:

// IPCThreadState.cpp

status_t IPCThreadState::transact(int32_t handle,
                                  uint32_t code, const Parcel& data,
                                  Parcel* reply, uint32_t flags)
{
    // ...
    err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, nullptr);

    if ((flags & TF_ONE_WAY) == 0) {
        if (reply) {
            err = waitForResponse(reply);
        }

普通 AIDL 方法不是 oneway,所以客户端线程会阻塞在 waitForResponse(reply),等服务端算完返回。

服务端怎么进入 onTransact 服务端进程有 Binder 线程池,线程等待 Binder 驱动消息。驱动把请求送到服务端后,服务端 native 层收到 BR_TRANSACTION

// IPCThreadState.cpp

case BR_TRANSACTION:
    // ...
    Parcel buffer;
    buffer.ipcSetDataReference(...);

    Parcel reply;
    status_t error;

    if (tr.target.ptr) {
        error = reinterpret_cast<BBinder*>(tr.cookie)->transact(
                tr.code, buffer, &reply, tr.flags);

如果服务端对象是 Java Binder,native 会回调 Java 的 Binder.execTransact()

//android_util_Binder.cpp

status_t onTransact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0) override
{
    // ...
    jboolean res = env->CallBooleanMethod(mObject, gBinderOffsets.mExecTransact,
        code, reinterpret_cast<jlong>(&data), reinterpret_cast<jlong>(reply), flags);

Java 层 Binder.execTransactInternal() 再调用真正的 onTransact()

// Binder.java

res = onTransact(code, data, reply, flags);
// ...
res = onTransact(code, data, reply, flags);

Binder.java 就是在进程B中Stub 的实现类,Binder是他们的父类 服务端 onTransact 如何做加法 请求到达后,Stub 根据 code 判断是哪一个方法:

// ICalculatorService.Stub()

case TRANSACTION_add:
    data.enforceInterface(DESCRIPTOR);

    int _arg0 = data.readInt(); // 读出 a
    int _arg1 = data.readInt(); // 读出 b

    String _result = this.add(_arg0, _arg1); // 真正执行加法

    reply.writeNoException();
    reply.writeString(_result);
    return true;

// 服务端进程B的实现
private val binder = object : ICalculatorService.Stub() {
        override fun add(a: Int, b: Int): String {
            val processName = currentProcessName()
            val sum = a + b
            Log.d(TAG, "add($a, $b) = $sum in process=$processName, pid=${android.os.Process.myPid()}")
            return "$processName: $sum"
        }
    }
    

终于到了B进程得到了 “进程B : 3” 再会写到reply

reply.writeNoException();
reply.writeString("3");


服务端 native 层把 reply 通过 Binder 驱动发回客户端。客户端 waitForResponse(reply) 返回后,Proxy.add() 继续执行:

_reply.readException();
String _result = _reply.readString();
return _result;

从南阳到南洋,根深叶茂啊,从binder到binder,一个来回啊

客户端 service.add(1,2)
 -> Proxy.add()
 -> _data.writeInt(1), _data.writeInt(2)
 -> BinderProxy.transact(TRANSACTION_add, _data, _reply, 0)
 -> native Binder / Binder 驱动
 -> 服务端 Binder 线程收到 BR_TRANSACTION
 -> Java Binder.execTransact()
 -> Stub.onTransact()
 -> data.readInt(), data.readInt()
 -> this.add(1,2)
 -> reply.writeString("3")
 -> Binder 驱动返回
 -> Proxy 读 _reply.readString()
 -> 客户端得到 "3"

image.png