2011年03月17日
AndroidのBinderによるプロセス間のメソッド呼び出し(メモ)
Androidのシステムでは同時にたくさんのプロセスでJava言語で書かれたプログラムがDalvikVMによって動いています。intentを発行して別のプロセスで処理を実行できることがAndroidでのプログラミングの大きな強みです。
このようなプロセスをまたがったメソッド呼び出しは下位レベルではBinderという技術が使われているのですが、私はいままでそれがぼんやりとしか理解できていませんでした。
ソースコードを無理やり追いかけたら、わかりかけてきたのでメモを残します。
Androidのアプリケーションプログラムから別のアプリケーションを起動するにはIntentを発行すればよいのですが、それには android.app.Activity#startActivity メソッドを使用します。
そのソースコードをたどってみました。
frameworks/base/core/java/android/app/Activity.java public void startActivity(Intent intent) --> frameworks/base/core/java/android/app/Activity.java public void startActivityForResult(Intent intent, int requestCode) --> frameworks/base/core/java/android/app/Instrumentation.java public ActivityResult execStartActivity( .. ) int result = ActivityManagerNative.getDefault().startActivity( .. );
結論を先に言うと、このActivityManagerNative.getDefault().startActivity( .. ); でプロセスをまたがってSystemServerのプロセスの中でActivityManagerServiceのstartActivity( .. )が実行されます。
もう少しじっくり見てみましょう。
ActivityManagerNative.getDefault()は以下のようになっています。
frameworks/base/core/java/android/app/ActivityMangerNative.java static public IActivityManager getDefault()
IActivityManagerはJavaのinterfaceになっています。
frameworks/base/core/java/android/app/IActivityManager.java public interface IActivityManager extends IInterface { ... public int startActivity(IApplicationThread caller, Intent intent, String resolvedType, Uri[] grantedUriPermissions, int grantedMode, IBinder resultTo, String resultWho, int requestCode, boolean onlyIfNeeded, boolean debug) throws RemoteException; ... }
interfaceはJavaのソースのコンパイル時には型だけが定義されていて、実行時になんらかの方法でこの型を実装するクラスを得て実行されることになります。
AndroidのソースのframeworksディレクトリでIActivityManagerを実装しているクラスを探してみると
$ grep -r 'implements.*IActivityManager' * 2>/dev/null base/core/java/android/app/ActivityManagerNative.java:public abstract class ActivityManagerNative extends Binder implements IActivityManager base/core/java/android/app/ActivityManagerNative.java:class ActivityManagerProxy implements IActivityManager
ActivityManagerNative.javaの中に2つ見つかりました。
先にActivityManagerProxy#startActivity を見てみると
frameworks/base/core/java/android/app/ActivityMangerNative.java class ActivityManagerProxy implements IActivityManager { ... public int startActivity(IApplicationThread caller, Intent intent, String resolvedType, Uri[] grantedUriPermissions, int grantedMode, IBinder resultTo, String resultWho, int requestCode, boolean onlyIfNeeded, boolean debug) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); data.writeStrongBinder(caller != null ? caller.asBinder() : null); intent.writeToParcel(data, 0); data.writeString(resolvedType); data.writeTypedArray(grantedUriPermissions, 0); data.writeInt(grantedMode); data.writeStrongBinder(resultTo); data.writeString(resultWho); data.writeInt(requestCode); data.writeInt(onlyIfNeeded ? 1 : 0); data.writeInt(debug ? 1 : 0); mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0); reply.readException(); int result = reply.readInt(); reply.recycle(); data.recycle(); return result; } ... }
ああ、なるほど。引数をParcelクラスでシリアライズしてmRemote.transact( .. )を呼び出しています。
mRemoteの型はIBinderです。ここでBinderを使ってプロセス間通信をしています。
これの受け側のプロセスではParcelクラスから引数をデシリアライズしてstartActivityメソッドを呼ぶところがあるはずです。
それを見つけました。
public abstract class ActivityManagerNative extends Binder implements IActivityManager { ... public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { switch (code) { case START_ACTIVITY_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); IBinder b = data.readStrongBinder(); IApplicationThread app = ApplicationThreadNative.asInterface(b); Intent intent = Intent.CREATOR.createFromParcel(data); String resolvedType = data.readString(); Uri[] grantedUriPermissions = data.createTypedArray(Uri.CREATOR); int grantedMode = data.readInt(); IBinder resultTo = data.readStrongBinder(); String resultWho = data.readString(); int requestCode = data.readInt(); boolean onlyIfNeeded = data.readInt() != 0; boolean debug = data.readInt() != 0; int result = startActivity(app, intent, resolvedType, grantedUriPermissions, grantedMode, resultTo, resultWho, requestCode, onlyIfNeeded, debug); reply.writeNoException(); reply.writeInt(result); return true; } ... }
実際に呼ばれるstartActivityというメソッドはこのActivityManagerNativeクラスにはありません。そもそもこれはabstractクラスなので、これを継承しているクラスがあるはず。
見つけました。
frameworks/base/services/java/com/android/server/am/ActivityManagerService.java
public final class ActivityManagerService extends ActivityManagerNative implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback { ... public final int startActivity(IApplicationThread caller, Intent intent, String resolvedType, Uri[] grantedUriPermissions, int grantedMode, IBinder resultTo, String resultWho, int requestCode, boolean onlyIfNeeded, boolean debug) { return mMainStack.startActivityMayWait(caller, intent, resolvedType, grantedUriPermissions, grantedMode, resultTo, resultWho, requestCode, onlyIfNeeded, debug, null, null); } ... }
ここで再度 ActivityManagerNative.getDefault() に戻ってその中身を追いかけていくと、そのプロセスの中でActivityManagnerNativeクラスのコンストラクタが実行されている場合はActivityManagnerNativeクラスのインスタンスを返し、そうでない場合はActivityManagerProxyクラスのインスタンスを返すようになっていました。つまり実行したいメソッドのServiceが自分のプロセスで提供されているならばそれを呼び出し、そうでなければ、そのProxyメソッドによってBinder経由で呼び出すということになっていました。
ActivityManagerNative.java
/** * Retrieve the system's default/global activity manager. */ static public IActivityManager getDefault() { if (gDefault != null) { //if (Config.LOGV) Log.v( // "ActivityManager", "returning cur default = " + gDefault); return gDefault; } IBinder b = ServiceManager.getService("activity"); if (Config.LOGV) Log.v( "ActivityManager", "default service binder = " + b); gDefault = asInterface(b); if (Config.LOGV) Log.v( "ActivityManager", "default service = " + gDefault); return gDefault; }
/** * Cast a Binder object into an activity manager interface, generating * a proxy if needed. */ static public IActivityManager asInterface(IBinder obj) { if (obj == null) { return null; } IActivityManager in = (IActivityManager)obj.queryLocalInterface(descriptor); if (in != null) { return in; } return new ActivityManagerProxy(obj); }
queryLocalInterface(descriptor)では、そのプロセスの中でActivityManagnerNativeクラスのコンストラクタが実行されている場合はActivityManagnerNativeクラスのインスタンスを返し、そうでない場合はnullを返します。
関連するつぶやき
関連するページ
AndroidのBinderによるプロセス間のメソッド呼び出し(メモ)
AndroidのBinderによるプロセス間のメソッド呼び出し(メモ2:AIDL)
AndroidのBinderによるプロセス間のメソッド呼び出し(メモ3:Binder Thread)