コアサービス開発に必要な情報を整理

SDKベースサービス実装だけならAIDLの使い方だけでいいかもしれませんが、Systemレベルのコアサービス開発に必要な整理のメモ。
IBinderインターフェース、BinderDriver、ServiceManager、Stub、Proxyあたりを順々と。何回かに分けて、javaベースから、最終的にNative(C++)の実装整理を。
一回目はandroidのプロセス間通信を改めて追いかけるところから。
frameworkを見ると色々なところで類似な実装がされているが、ここではまず、別のアプリケーションを起動するのにおなじみのIntent発行に用いるActivityを追ってみる。最後にAIDLの生成物と比較します。

chiaki@ubuntu:~/mydroid/frameworks/base/core$ vi java/android/app/Activity.java
public void startActivityForResult(Intent intent, int requestCode) {
        if (mParent == null) {
            Instrumentation.ActivityResult ar =
                mInstrumentation.execStartActivity(
                    this, mMainThread.getApplicationThread(), mToken, this,
                    intent, requestCode);
			…略

ふむ、Instrumentation.ActivityResult execStartActivityを追っかける。

chiaki@ubuntu:~/mydroid/frameworks/base/core$ vi java/android/app/Instrumentation.java 
    public ActivityResult execStartActivity(
		…略
        try {
            int result = ActivityManagerNative.getDefault()
                .startActivity(whoThread, intent,
                        intent.resolveTypeIfNeeded(who.getContentResolver()),
                        null, 0, token, target != null ? target.mEmbeddedID : null,
                        requestCode, false, false);
		…略

ふむ、ActivityManagerNative.getDefault()を追っかける。

chiaki@ubuntu:~/mydroid/frameworks/base/core$ vi java/android/app/ActivityManagerNative.java
    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;
    }

  //asInterfaceで、Proxyメソッドを使ってBinder経由でサービスを呼び出す

    /**
     * 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);
    }

つまりプロセス間通信は、
他のプロセスからのリクエストをメソッドを実行するStubと、binderを通して他のプロセスに実行を要求するProxyで構成される。

ふむ、ついでなので続けてIActivityManagerを追っかける。

chiaki@ubuntu:~/mydroid/frameworks/base/core$ vi 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で、startActivityが定義されている。これを実装している箇所を追っかける。

chiaki@ubuntu:~/mydroid/frameworks/base/core/java/android/app$ cd java/android/app
chiaki@ubuntu:~/mydroid/frameworks/base/core/java/android/app$ grep -r 'implements.*IActivityManager' * 2>/dev/null
ActivityManagerNative.java:public abstract class ActivityManagerNative extends Binder implements IActivityManager
ActivityManagerNative.java:class ActivityManagerProxy implements IActivityManager

ふむ、ActivityManagerNative.javaに、2箇所。追っかける。

chiaki@ubuntu:~/mydroid/frameworks/base/core/java/android/app$ vi ActivityManagerNative.java 
public abstract class ActivityManagerNative extends Binder implements IActivityManager
{
	…略
   class ActivityManagerProxy implements IActivityManager
   {
     	 public ActivityManagerProxy(IBinder remote)
    	 {
           mRemote = remote;
    	 }   
    	 public IBinder asBinder()
    	 {
         return mRemote;
    }
   
    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クラスでシリアライズ
        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);

	//シリアライズした引数をIBinderのmRemoteを使ってプロセス間通信を投げてる
        mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
        reply.readException();
        int result = reply.readInt();
        reply.recycle();
        data.recycle();
        return result;
    }

ふむ、このプロセス間通信の受信側を追っかける。

    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;

	    //受け取った引数をデシリアライズしてstartActivityメソッドを呼んでる
            int result = startActivity(app, intent, resolvedType,
                    grantedUriPermissions, grantedMode, resultTo, resultWho,
                    requestCode, onlyIfNeeded, debug);
            reply.writeNoException();
            reply.writeInt(result);
            return true;
        }

ふむ、ここにはstartActivityメソッドがない。上を見るとabstractなので、継承してるクラスを追っかける。

chiaki@ubuntu:~/mydroid/frameworks/base$ vi services/java/com/android/server/am/ActivityManagerService.java
    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);
    }

以上、Activityを例に見てきたが、androidでのプロセス間通信で一般的に利用されるAIDLの内幕もつまるところこれと同じ感じ。
例:IThumbnailReceiver.aidlから自動生成されるjavaファイルを見てみると、基本構造は同じ。

public interface IThumbnailReceiver extends android.os.IInterface
{
…略
public Stub()
{
…略
}
…略
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
…略
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements android.app.IThumbnailReceiver
{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}

以下全掲

chiaki@ubuntu:~/mydroid$ vi ./out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/app/IThumbnailReceiver.java
public interface IThumbnailReceiver extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements android.app.IThumbnailReceiver
{
private static final java.lang.String DESCRIPTOR = "android.app.IThumbnailReceiver";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
 * Cast an IBinder object into an android.app.IThumbnailReceiver interface,
 * generating a proxy if needed.
 */
public static android.app.IThumbnailReceiver asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = (android.os.IInterface)obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof android.app.IThumbnailReceiver))) {
return ((android.app.IThumbnailReceiver)iin);
}
return new android.app.IThumbnailReceiver.Stub.Proxy(obj);
}
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
{
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_newThumbnail:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
android.graphics.Bitmap _arg1;
if ((0!=data.readInt())) {
_arg1 = android.graphics.Bitmap.CREATOR.createFromParcel(data);
}
else {
_arg1 = null;
}
java.lang.CharSequence _arg2;
if ((0!=data.readInt())) {
_arg2 = android.text.TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(data);
}
else {
_arg2 = null;
}
this.newThumbnail(_arg0, _arg1, _arg2);
return true;
}
case TRANSACTION_finished:
{
data.enforceInterface(DESCRIPTOR);
this.finished();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements android.app.IThumbnailReceiver
{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
public android.os.IBinder asBinder()
{
return mRemote;
}
public java.lang.String getInterfaceDescriptor()
{
return DESCRIPTOR;
}
public void newThumbnail(int id, android.graphics.Bitmap thumbnail, java.lang.CharSequence description) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(id);
if ((thumbnail!=null)) {
_data.writeInt(1);
thumbnail.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
if ((description!=null)) {
_data.writeInt(1);
android.text.TextUtils.writeToParcel(description, _data, 0);
}
else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_newThumbnail, _data, null, android.os.IBinder.FLAG_ONEWAY);
}
finally {
_data.recycle();
}
}
public void finished() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_finished, _data, null, android.os.IBinder.FLAG_ONEWAY);
}
finally {
_data.recycle();
}
}
}
static final int TRANSACTION_newThumbnail = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_finished = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
public void newThumbnail(int id, android.graphics.Bitmap thumbnail, java.lang.CharSequence description) throws android.os.RemoteException;
public void finished() throws android.os.RemoteException;