コアサービス開発に必要な情報を整理
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;