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のメモ(Togetter)



トラックバックURL

コメントする

名前
URL
 
  絵文字
 
 
記事検索
最新コメント
アクセスカウンター
  • 今日:
  • 昨日:
  • 累計:

QRコード
QRコード