AIDL 介紹
AIDL全稱為Android介面定義語言,是Android系統提供的一款可供使用者用來抽象IPC的工具,它提供了語法讓我們來定義跨程序通訊的服務介面,也就是.aidl
檔案,它也提供了工具,幫助我們把定義檔案專程目標語言的程式碼。
我們自己使用AIDL建立的服務,或者一部分系統內的服務,比如IWindowSession
、IApplicationThread
等,這些多是執行在App程序,一般都不是系統服務,因此都是匿名的。我們這裏以IApplicationThread
來分析AIDL建立的匿名服務是怎麼傳遞Binder給使用端的。
以IApplicationThread
來分析,則服務端是我們的App程序,而客戶端則是system_server程序。作為AIDL建立的Binder,首先會有一個AIDL檔案,這裏是IApplicationThread.aidl
,其中定義了一些跨程序呼叫的方法,部分內容如下:
package android.app; ... oneway interface IApplicationThread { void scheduleReceiver(in Intent intent, in ActivityInfo info, in CompatibilityInfo compatInfo, int resultCode, in String data, in Bundle extras, boolean sync, int sendingUser, int processState); @UnsupportedAppUsage void scheduleStopService(IBinder token); ... }
當前Android AIDL已經支援生成Java、C++、Rust的程式碼,對於IApplicationThread
這裏我們只需關注生成Java版本的程式碼即可。生成的程式碼在IApplicationThread
當中,大概如下所示:
public interface IApplicationThread extends android.os.IInterface { public static class Default implements android.app.IApplicationThread { @Override public void scheduleReceiver(android.content.Intent ...) throws android.os.RemoteException {} @Override public android.os.IBinder asBinder() { return null; } } public abstract static class Stub extends android.os.Binder implements android.app.IApplicationThread { public Stub() { this.attachInterface(this, DESCRIPTOR); } public static android.app.IApplicationThread asInterface(android.os.IBinder obj) { if ((obj == null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin != null) && (iin instanceof android.app.IApplicationThread))) { return ((android.app.IApplicationThread) iin); } return new android.app.IApplicationThread.Stub.Proxy(obj); } @Override public android.os.IBinder asBinder() { return this; } public static java.lang.String getDefaultTransactionName(int transactionCode) { switch (transactionCode) { case TRANSACTION_scheduleReceiver:{ return "scheduleReceiver"; } case TRANSACTION_scheduleCreateService: { return "scheduleCreateService"; } default: { return null; } } } public java.lang.String getTransactionName(int transactionCode) { return this.getDefaultTransactionName(transactionCode); } @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); } switch (code) { case INTERFACE_TRANSACTION:{ reply.writeString(descriptor); return true; } } switch (code) { case TRANSACTION_scheduleReceiver:{ android.content.Intent _arg0; _arg0 = data.readTypedObject(android.content.Intent.CREATOR); ... int _arg8; _arg8 = data.readInt(); data.enforceNoDataAvail(); this.scheduleReceiver(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5, _arg6, _arg7, _arg8); break; } ... default:{ return super.onTransact(code, data, reply, flags); } } return true; } private static class Proxy implements android.app.IApplicationThread { 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 void scheduleReceiver( android.content.Intent intent, ... int processState) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); ... _data.writeTypedObject(intent, 0); boolean _status = mRemote.transact( Stub.TRANSACTION_scheduleReceiver, _data, null, android.os.IBinder.FLAG_ONEWAY); } finally { _data.recycle(); } } ... } public static final java.lang.String DESCRIPTOR = "android.app.IApplicationThread"; static final int TRANSACTION_scheduleReceiver = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); static final int TRANSACTION_scheduleCreateService = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1); ... public int getMaxTransactionId() { return 57; } } ... public void scheduleReceiver( android.content.Intent intent, ... int processState) throws android.os.RemoteException; }
對於Java程式碼,AIDL會生成一個跟AIDL同名的介面,同時繼承自IInterface
,同時還會建立內部類,分別為Default和Stub。Default為預設實現,大多數情況下是沒有的,因為我們自己會實現。而Stub為抽象類,我們自己實現的時候會使用它,它以及繼承自Binder
,AIDL工具幫助我們把Parcel讀寫相關的程式碼已經生成,我們只需要去繼承它,實現業務邏輯即可。而Stub
中還有一個內部類Proxy
,這個類用於Binder服務的客戶端使用。對於IApplicationThread
的實現,在ActivityThread
當中。
匿名服務的傳輸
那麼ActivityServiceManager(之後簡稱AMS)是怎麼拿到ApplicationThread
的呢,ActivityThread
的attach方法則是這一切的入口:
final IActivityManager mgr = ActivityManager.getService(); try { mgr.attachApplication(mAppThread, startSeq); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); }
程式碼中,首先拿到AMS
的binder客戶端類,這裏也就是IActivityManager$Stub$Proxy
,因為它也用了AIDL,所以跟我們ApplicationThread
的類是類似的,具體如何拿到的,這個之前ServiceManager分析getService的時候已經分析過了,這裏不看了。我們可以看一下它的attachApplication
方法,程式碼如下:
@Override public void attachApplication(android.app.IApplicationThread app, long startSeq) 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.writeStrongInterface(app); _data.writeLong(startSeq); boolean _status = mRemote.transact(Stub.TRANSACTION_attachApplication, _data, _reply, 0); _reply.readException(); } finally { _reply.recycle(); _data.recycle(); } }
這裏也是跟我們之前addService類似,把binder寫入到Parcel中去,因為App程序這裏相當於是ApplicationThread它的服務端,因此這裏寫入的type為BINDER_TYPE_BINDER
,而呼叫mRemote.transact
則為呼叫BinderProxy
的同名方法,我們知道最終會呼叫到IPCThreadState
的transact
方法,從而呼叫binder驅動。
類似於getService的方法,AMS所在的system_server程序會收到BR_TRANSACTION
命令,在其中解析資料知道呼叫的是TRANSACTION_attachApplication
這個業務命令,進而使用readStrongInterface
來獲取到binder的代理物件BinderProxy
。具體程式碼在IActivityManager.Stub
的onTransact
中,程式碼如下:
case TRANSACTION_attachApplication: { android.app.IApplicationThread _arg0; _arg0 = android.app.IApplicationThread.Stub.asInterface(data.readStrongBinder()); long _arg1; _arg1 = data.readLong(); data.enforceNoDataAvail(); this.attachApplication(_arg0, _arg1); reply.writeNoException(); break; }
到這樣呼叫AMS
服務端的attachApplication
的時候就能使用IApplicationThread
所提供的方法了。
Binder驅動中binder節點的處理
但是看到這裏,有個問題,就是我們是匿名的binder,驅動怎麼是處理的呢。這個需要去看一下binder驅動的原始碼,不過就不具體看呼叫流程了,直接看生成建立node和handle部分的程式碼:
static int binder_translate_binder(struct flat_binder_object *fp, struct binder_transaction *t, struct binder_thread *thread) { struct binder_node *node; struct binder_proc *proc = thread->proc; struct binder_proc *target_proc = t->to_proc; struct binder_ref_data rdata; int ret = 0; node = binder_get_node(proc, fp->binder); if (!node) { node = binder_new_node(proc, fp); if (!node) return -ENOMEM; } ... ret = binder_inc_ref_for_node(target_proc, node, fp->hdr.type == BINDER_TYPE_BINDER, &thread->todo, &rdata); ... if (fp->hdr.type == BINDER_TYPE_BINDER) fp->hdr.type = BINDER_TYPE_HANDLE; else fp->hdr.type = BINDER_TYPE_WEAK_HANDLE; fp->binder = 0; fp->handle = rdata.desc; fp->cookie = 0; ... done: binder_put_node(node); return ret; }
可以看到對於透過Parcel呼叫經過binder驅動的binder物件,binder驅動都會給他們建立一個binder_node
,並且為其設定handle,在傳輸到客戶端的時候還會把type
設定為BINDER_TYPE_HANDLE
。 這樣我們就對整給流程有所瞭解了,如果讀者還想窺探更多的細節,則需要自行去閱讀binder驅動的原始碼了。
開發者如何使用匿名服務
這上面介紹的部分是我們使用系統的服務來獲取AIDL建立的服務,對於應用開發者來說有什麼辦法呢。我們可以透過AIDL+Service來實現,Android的四大元件之一的Service
,它提供了透過bindService
的方式來啟動服務。在它的onBind
中就可以返回IBinder
,Android Framework會幫助我們呼叫操作程式碼如下:
private void handleBindService(BindServiceData data) { CreateServiceData createData = mServicesData.get(data.token); Service s = mServices.get(data.token); if (s != null) { try { .... try { if (!data.rebind) { IBinder binder = s.onBind(data.intent); ActivityManager.getService().publishService( data.token, data.intent, binder); } else { ... } } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } } catch (Exception e) { .... } } }
可以看到在ActivityThread
的handleBindService
方法 中,我們在拿到Service所提供的IBinder
之後,AMS
會呼叫publishService
,我們可以在ServiceConnection
回撥中拿到Binder
的代理物件,之後就可以進行跨程序通訊了。
另外Android Framework還為我們提供了Messenger
,其實現為Service+AIDL+Handler,讓我們不用自己寫AIDL,我們自己定義Service的時候使用Messenger和Handler就可以實現跨程序通訊了。
總結
到此為止,我們已經分析了Binder服務管家ServiceManager的啟動、使用ServiceManger新增服務和查詢服務以及匿名服務的傳遞,在此過程中我們瞭解了程序是如何與Binder驅動互動的,以及binder呼叫過程中的會執行的方法等,我們對於Binder就有了一個全面的瞭解。在本文還簡單介紹了應用開發者如何使用Binder,有了這些基礎,我們後面分析Android系統其他部分的程式碼就會更加容易了。