切換語言為:簡體

Binder原始碼分析:匿名服務啟動與AIDL

  • 爱糖宝
  • 2024-09-08
  • 2042
  • 0
  • 0

AIDL 介紹

AIDL全稱為Android介面定義語言,是Android系統提供的一款可供使用者用來抽象IPC的工具,它提供了語法讓我們來定義跨程序通訊的服務介面,也就是.aidl檔案,它也提供了工具,幫助我們把定義檔案專程目標語言的程式碼。

我們自己使用AIDL建立的服務,或者一部分系統內的服務,比如IWindowSessionIApplicationThread等,這些多是執行在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的同名方法,我們知道最終會呼叫到IPCThreadStatetransact方法,從而呼叫binder驅動。

類似於getService的方法,AMS所在的system_server程序會收到BR_TRANSACTION命令,在其中解析資料知道呼叫的是TRANSACTION_attachApplication這個業務命令,進而使用readStrongInterface來獲取到binder的代理物件BinderProxy。具體程式碼在IActivityManager.StubonTransact中,程式碼如下:

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) {  
            ....
        }  
    }  
}

可以看到在ActivityThreadhandleBindService方法 中,我們在拿到Service所提供的IBinder之後,AMS會呼叫publishService,我們可以在ServiceConnection回撥中拿到Binder的代理物件,之後就可以進行跨程序通訊了。

另外Android Framework還為我們提供了Messenger,其實現為Service+AIDL+Handler,讓我們不用自己寫AIDL,我們自己定義Service的時候使用Messenger和Handler就可以實現跨程序通訊了。

總結

到此為止,我們已經分析了Binder服務管家ServiceManager的啟動、使用ServiceManger新增服務和查詢服務以及匿名服務的傳遞,在此過程中我們瞭解了程序是如何與Binder驅動互動的,以及binder呼叫過程中的會執行的方法等,我們對於Binder就有了一個全面的瞭解。在本文還簡單介紹了應用開發者如何使用Binder,有了這些基礎,我們後面分析Android系統其他部分的程式碼就會更加容易了。

0則評論

您的電子郵件等資訊不會被公開,以下所有項目均必填

OK! You can skip this field.