Android Binderの通信原理(5):Java側でのサービスの登録と取得

ソースコードベース: Android R

0. 序文

関連する知識ポイントを説明するには、まずフレーム図を整理し、次にフレーム図を作成してプロセスを分析します。

Java側でのバインダーの使い方は大きく分けて以下のようになります。

  • Javaクライアント Javaサービス
  • Javaクライアントネイティブサービス

Java側のサービスの登録はServiceManager.addService()を使用します。

ServiceManager.getService() を使用して Java 側のサービスを取得します

エントリは ServiceManager.java を通じて行われ、ファイルは Frameworks/base/core/java/android/os/ServiceManager.java にあります。

addService や getService を解析する前に、Binder が最終的にネイティブ側を呼び出すことを理解する必要があり、Java は JNI を経由する必要があるため、その前に JNI の登録プロセスを理解する必要があります。

1. バインダーJNI登録

zygote の起動プロセスを理解している学生は、zygote が起動時にまず仮想マシンを作成し、次に startReg() を通じてシステムに必要な JNI インターフェイスを登録することを知っているはずです。

frameworks/base/core/jni/AndroidRuntime.cpp

/*static*/ int AndroidRuntime::startReg(JNIEnv* env)
{
    ...
    if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) {
        env->PopLocalFrame(NULL);
        return -1;
    }
    ...
}

登録されたインターフェイスは次のように定義されます。

frameworks/base/core/jni/AndroidRuntime.cpp

static const RegJNIRec gRegJNI[] = {
        REG_JNI(register_com_android_internal_os_RuntimeInit),
        REG_JNI(register_com_android_internal_os_ZygoteInit_nativeZygoteInit),
        REG_JNI(register_android_os_SystemClock),
        REG_JNI(register_android_util_EventLog),
        REG_JNI(register_android_util_Log),
        REG_JNI(register_android_util_MemoryIntArray),
        ...
        REG_JNI(register_android_os_Binder),
        ...

配列は非常に長いので、ここにはあまり貼り付けませんが、バインダー関連の登録関数は次のとおりです。

        REG_JNI(register_android_os_Binder),

最後に、関数 register_android_os_Binder() が呼び出されます。

frameworks/base/core/jni/android_util_Binder.cpp

int register_android_os_Binder(JNIEnv* env)
{
    if (int_register_android_os_Binder(env) < 0)
        return -1;
    if (int_register_android_os_BinderInternal(env) < 0)
        return -1;
    if (int_register_android_os_BinderProxy(env) < 0)
        return -1;
    ...

残りはあまり分析しません. 興味のある学生は、ソース コードのプロセスを詳細に表示するためにアクセスできます (もちろん、次の遭遇もついでに分析されます). ここで知っておく必要があるのは、Java 上のバインダー側はネイティブ操作が必要です JNI インターフェースはすべて zygote にあります 起動時に登録されます。

2.ServiceManager プロキシ

ServiceManager.addService()にしてServiceManager.getService()にしても、最終的にはネイティブと通信する必要があり、ServiceManager のプロキシを取得する必要があります。つまり、関数getIServiceManager()は次のようになります。

frameworks/base/core/java/android/os/ServiceManager.java 

    private static IServiceManager getIServiceManager() {
        if (sServiceManager != null) {
            return sServiceManager;
        }

        // Find the service manager
        sServiceManager = ServiceManagerNative
                .asInterface(Binder.allowBlocking(BinderInternal.getContextObject()));
        return sServiceManager;
    }

主なポイントは次の 2 つです。

  • BidnerInternal.getcontextObject()
  • ServiceManagerNative.asInterface()

2.1 BinderInternal.getContextObject()

frameworks/base/core/java/com/android/internal/os/BinderInternal.java

public static final native IBinder getContextObject();

これには JNI インターフェイスを呼び出す必要があります。このインターフェイスの登録についてはセクション1で説明しています。詳細については、 int_register_android_os_BinderInternal()を参照してください。

frameworks/base/core/jin/android_util_Binder.cpp

static const JNINativeMethod gBinderInternalMethods[] = {
     /* name, signature, funcPtr */
    { "getContextObject", "()Landroid/os/IBinder;", (void*)android_os_BinderInternal_getContextObject },
    ...
    }
};
static int int_register_android_os_BinderInternal(JNIEnv* env)
{
    ...
    return RegisterMethodsOrDie(
env, kBinderInternalPathName,
 gBinderInternalMethods, NELEM(gBinderInternalMethods));
}

gBinderInternalMethods 配列を登録することで知られる、インターフェイス android_os_BinderInternal_getContextObject() が最終的に呼び出されます。

frameworks/base/core/jin/android_util_Binder.cpp

static jobject android_os_BinderInternal_getContextObject(JNIEnv* env, jobject clazz)
{
    sp<IBinder> b = ProcessState::self()->getContextObject(NULL);
    return javaObjectForIBinder(env, b);
}

ここではおなじみですが、取得するのはコンテキストが0のservicemanagerのBpBinderで、そのBpBinderをjavaObjectForIBinder()関数で JavaのBinderProxyオブジェクトに変換して返します。

2.1.1  javaObjectForIBinder()

frameworks/base/core/jin/android_util_Binder.cpp

jobject javaObjectForIBinder(JNIEnv* env, const sp<IBinder>& val)
{
    ...
    BinderProxyNativeData* nativeData = new BinderProxyNativeData();
    nativeData->mOrgue = new DeathRecipientList;
    nativeData->mObject = val;

    jobject object = env->CallStaticObjectMethod(gBinderProxyOffsets.mClass,
            gBinderProxyOffsets.mGetInstance, (jlong) nativeData, (jlong) val.get());
    if (env->ExceptionCheck()) {
        // In the exception case, getInstance still took ownership of nativeData.
        return NULL;
    }
    ...

    return object;
}

ここで、 BinderProxy のgeInstance() は主に CallStaticObjectMethod() を通じて呼び出され、ここでのグローバル オブジェクトgBinderProxyOffsetsはint_register_android_os_BinderProxy()で指定されます

frameworks/base/core/jin/android_util_Binder.cpp

static int int_register_android_os_BinderProxy(JNIEnv* env)
{
    gErrorOffsets.mError = MakeGlobalRefOrDie(env, FindClassOrDie(env, "java/lang/Error"));
    gErrorOffsets.mOutOfMemory =
        MakeGlobalRefOrDie(env, FindClassOrDie(env, "java/lang/OutOfMemoryError"));
    gErrorOffsets.mStackOverflow =
        MakeGlobalRefOrDie(env, FindClassOrDie(env, "java/lang/StackOverflowError"));

    jclass clazz = FindClassOrDie(env, kBinderProxyPathName);
    gBinderProxyOffsets.mClass = MakeGlobalRefOrDie(env, clazz);
    gBinderProxyOffsets.mGetInstance = GetStaticMethodIDOrDie(env, clazz, "getInstance",
            "(JJ)Landroid/os/BinderProxy;");
    gBinderProxyOffsets.mSendDeathNotice =
            GetStaticMethodIDOrDie(env, clazz, "sendDeathNotice",
                                   "(Landroid/os/IBinder$DeathRecipient;Landroid/os/IBinder;)V");
    gBinderProxyOffsets.mNativeData = GetFieldIDOrDie(env, clazz, "mNativeData", "J");

    clazz = FindClassOrDie(env, "java/lang/Class");
    gClassOffsets.mGetName = GetMethodIDOrDie(env, clazz, "getName", "()Ljava/lang/String;");

    return RegisterMethodsOrDie(
        env, kBinderProxyPathName,
        gBinderProxyMethods, NELEM(gBinderProxyMethods));
}

javaObjectForIBinder() は、 CallStaticObjectMethod() 関数を通じて BinderProxy の getInstance() インターフェイスを呼び出す必要があります。見てみましょう。

frameworks/base/core/java/android/os/BinderProxy.java

    private static BinderProxy getInstance(long nativeData, long iBinder) {
        BinderProxy result;
        synchronized (sProxyMap) {
            try {
                result = sProxyMap.get(iBinder);
                if (result != null) {
                    return result;
                }
                result = new BinderProxy(nativeData);
            } catch (Throwable e) {
                ...
            }
            ...
        }
        return result;
    }

これまでのところ、 BidnerInternal.getcontextObject() が取得するのは、コンテキストが 0 の BinderProxy オブジェクトであることがわかります。

2.2 ServiceManagerNative.asInterface()

framworks/base/core/java/android/os/ServiceManagerNative.java

    public static IServiceManager asInterface(IBinder obj) {
        if (obj == null) {
            return null;
        }

        // ServiceManager is never local
        return new ServiceManagerProxy(obj);
    }

BinderProxy オブジェクトを取得したら、asInterface を使用して ServiceManager の Java 側プロキシ ServiceManagerProxy を作成します。

framworks/base/core/java/android/os/ServiceManagerNative.java

class ServiceManagerProxy implements IServiceManager {
    public ServiceManagerProxy(IBinder remote) {
        mRemote = remote;
        mServiceManager = IServiceManager.Stub.asInterface(remote);
    }
    ...
    public void addService(String name, IBinder service, boolean allowIsolated, int dumpPriority)
            throws RemoteException {
        mServiceManager.addService(name, service, allowIsolated, dumpPriority);
    }
    ...
    private IBinder mRemote;
    private IServiceManager mServiceManager;
}

mRemoteは BinderProxy オブジェクト、mServiceManagerはすべてのインターフェイスの入り口、ServiceManager の実際のプロキシです。分析は以下に続きます。

3.ServiceManager.addService()

セクション2を通じて、Java 側のバインダー通信は、クライアントがServiceManager.getService() を通じて取得したサーバー プロキシであるか、サーバー側がServiceManager.addService()を通じてServiceManager にサービスを登録する可能性があることがわかります。 。ただし、どのインターフェイスであっても、 Java 側の ServiceManager のコードBinderProxyを通じて操作されます。

このセクションでは addService() を分析します。

frameworks/base/core/java/android/os/ServiceManager.java 

    public static void addService(String name, IBinder service, boolean allowIsolated,
            int dumpPriority) {
        try {
            getIServiceManager().addService(name, service, allowIsolated, dumpPriority);
        } catch (RemoteException e) {
            Log.e(TAG, "error in addService", e);
        }
    }

getIServiceManager() は、上記のセクション 2.2 で返された ServiceManagerProxy オブジェクトです。上記のセクション 2.2 のコードに示されているように、 addService() は最後にmServiceManager.addService()を呼び出します。この mServiceManager は、上記のコンテキスト 0 の BinderProxyオブジェクトです。 IServiceManager を通じてパラメータとして取得されたプロキシ:

 mServiceManager = IServiceManager.Stub.asInterface(remote);

分析を続けます。

通常、andorid は*.aidlファイルを対応するJavaファイルにコンパイルし、最終的な Java ソース コードはout/soong/.intermediates/の下にあります。詳細については、この記事シリーズの 7 番目の記事を参照してください: Java での CS

Aidl によってコンパイルされた JAVA ファイルは、最終的には Aidl*.srcjar にパッケージ化されることになりますが、特定のファイルを特定するにはどうすればよいでしょうか? これは、フレームワーク フォルダーまたはシステム フォルダー内の*.aidl.dファイルに依存する必要があります。

Aidl ファイルがフレームワークで定義されている場合は、フレームワークの対応するディレクトリに移動して、対応する *.aidl.d ファイルを見つけることができます。

この IServiceManager は、IServiceManager.aidl のコンパイルされたクラス名です。

      @Override public void addService(java.lang.String name, android.os.IBinder service, boolean allowIsolated, int dumpPriority) 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.writeStrongBinder(service);
          ...
          boolean _status = mRemote.transact(Stub.TRANSACTION_addService, _data, _reply, 0);
          if (!_status && getDefaultImpl() != null) {
            getDefaultImpl().addService(name, service, allowIsolated, dumpPriority);
            return;
          }
          _reply.readException();
        }
        ...
      }

プロキシ コードは比較的単純です。

  • インターフェイス writeStrongBinder(); を通じて、Parcel に追加するサービスを書き込みます。
  • mRemote.transact() を通じて通信して、addService() の目的が達成されたかどうかを確認します。
  • 上記が失敗した場合は、getDefaultImpl() 経由で addService() を試してください。

通常は 2 番目のステップが実行され、3 番目のステップのデフォルトの impl はインターフェースsetDefaultImpl()を通じて指定する必要があります。

セクション2.2によると、mRemote は ServiceManager の Java 側の BinderProxy オブジェクトです。

3.1 writeStrongBinder()

frameworks/base/core/java/android/os/Parcel.java

    public final void writeStrongBinder(IBinder val) {
        nativeWriteStrongBinder(mNativePtr, val);
    }

JNI インターフェイスは android_os_Parcel.cpp にあり、このインターフェイスも zygote の起動時にロードされます。ここではこれ以上の分析はありません。詳細については、AndroidRuntime.cpp の配列 gRegJNI 内のメンバー register_android_os_Parcel を参照してください。

最後に、JNI android_os_Parcel_writeStrongBinder() を呼び出します。

frameworks/base/core/jni/android_os_Parcel.cpp

static void android_os_Parcel_writeStrongBinder(JNIEnv* env, jclass clazz, jlong nativePtr, jobject object)
{
    Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
    if (parcel != NULL) {
        const status_t err = parcel->writeStrongBinder(ibinderForJavaObject(env, object));
        if (err != NO_ERROR) {
            signalExceptionForError(env, clazz, err);
        }
    }
}

コアの ibinderForJavaObject() を見てみましょう。

frameworks/base/core/jni/android_util_Binder.cpp

sp<IBinder> ibinderForJavaObject(JNIEnv* env, jobject obj)
{
    if (obj == NULL) return NULL;

    // Instance of Binder?
    if (env->IsInstanceOf(obj, gBinderOffsets.mClass)) {
        JavaBBinderHolder* jbh = (JavaBBinderHolder*)
            env->GetLongField(obj, gBinderOffsets.mObject);
        return jbh->get(env, obj);
    }

    // Instance of BinderProxy?
    if (env->IsInstanceOf(obj, gBinderProxyOffsets.mClass)) {
        return getBPNativeData(env, obj)->mObject;
    }

    ALOGW("ibinderForJavaObject: %p is not a Binder object", obj);
    return NULL;
}

純粋なサービスは Binder タイプに属しているため、照合時には gBinderOffsets.mClass と照合され、この変数は登録時にも割り当てられます。詳細については、 int_register_android_os_Binder()を参照してください。

gBinderOffsets.mObject は、Binder.java の mObject オブジェクト (詳細については int_register_android_os_Binder 関数を参照)、つまり、Binder の構築時にネイティブから取得される BBinderHolder ポインターを取得します。

frameworks/base/core/java/android/os/Binder.java

    public Binder(@Nullable String descriptor)  {
        mObject = getNativeBBinderHolder();

        ...
    }

----> 

frameworks/base/core/jni/android_util_Binder.cpp

static jlong android_os_Binder_getNativeBBinderHolder(JNIEnv* env, jobject clazz)
{
    JavaBBinderHolder* jbh = new JavaBBinderHolder();
    return (jlong) jbh;
}

ibinderForJavaObject()上記で取得したJavaBBinderHolderに加えて、最後にホルダーを通じて get() インターフェイスを呼び出し、サービスの BBinder を取得します。

frameworks/base/core/jni/android_util_Binder.cpp

    sp<JavaBBinder> get(JNIEnv* env, jobject obj)
    {
        AutoMutex _l(mLock);
        sp<JavaBBinder> b = mBinder.promote();
        if (b == NULL) {
            b = new JavaBBinder(env, obj);
            ...
        }

        return b;
    }

JavaBBinder を見てください。

frameworks/base/core/jni/android_util_Binder.cpp

class JavaBBinder : public BBinder
{
    ...
}

したがって、writeStrongBinder() は最終的には、パーセルを transact() に書き込むためのネイティブの BBinder になります。

3.2 BinderProxy.transact()

詳細についてはセクション 5 を参照してください

3.3 JavaBBinder.onTransact()

クライアントが transact() 経由で通信する場合、BBinder は最終的に onTransact() をトリガーします。Java サービスの場合、最後のトリガーはJavaBBinder.onTransact()です。

frameworks/base/core/jni/android_util_Binder.cpp

    status_t onTransact(
        uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0) override
    {
        ...
        jboolean res = env->CallBooleanMethod(mObject, gBinderOffsets.mExecTransact,
            code, reinterpret_cast<jlong>(&data), reinterpret_cast<jlong>(reply), flags);

gBinderOffsets.mExecTransact は、Binder.java の execTransact() を呼び出す int_register_android_os_Binder() によっても割り当てられますが、ここでは詳しく説明せず、最後に stub.onTransact を呼び出します。

4.ServiceManager.getService()

addService と同様に、getService は最終的に IServiceManager に呼び出されます。

      @Override public android.os.IBinder checkService(java.lang.String name) throws android.os.RemoteException
      {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        android.os.IBinder _result;
        try {
          ...
          boolean _status = mRemote.transact(Stub.TRANSACTION_checkService, _data, _reply, 0);
          if (!_status && getDefaultImpl() != null) {
            return getDefaultImpl().checkService(name);
          }
          _reply.readException();
          _result = _reply.readStrongBinder();
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }

        return _result;
      }

addService() と同様に、ネイティブ側から送信されるバインダー情報を_reply.readStrongBinder()を通じて取得します。

4.1 readStrongBinder()

最終的には JNI android_os_Parcel.cpp に呼び出されます。

frameworks/base/core/jni/android_os_Parcel.cpp

static jobject android_os_Parcel_readStrongBinder(JNIEnv* env, jclass clazz, jlong nativePtr)
{
    Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
    if (parcel != NULL) {
        return javaObjectForIBinder(env, parcel->readStrongBinder());
    }
    return NULL;
}

セクション2.1が分析され、最後に javaObjectForIBinder() を通じて Java 側のサービスの BinderProxy オブジェクトが取得されます。

5. BinderProxy.transact()

frameworks/base/core/java/android/os/BinderProxy.java

    public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
        Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");

        ....
        try {
            return transactNative(code, data, reply, flags);
        } finally {
            ...
        }
    }

実際、ここでの核心は、transactNative を呼び出すことです。

frameworks/base/core/jni/android_util_Binder.cpp

static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,
        jint code, jobject dataObj, jobject replyObj, jint flags) // throws RemoteException
{
    ...

    status_t err = target->transact(code, *data, reply, flags);
    ...
}

次のステップは BpBinder->transact です。詳細については、この記事シリーズの 3 番目の記事を参照してください: サービス登録

6. まとめ 

最後に、フレームワーク図に従ってまとめてみましょう。

  • Java クライアントは getService() を通じてサービスのプロキシを取得し、 getService() は最後にreadStrongBinder()を通じて BinderProxy オブジェクトを取得します 。
  • Java クライアントは、transact を通じてサービスと通信し、ネイティブ インターフェイスtransactNative()を通じてBpBinder を呼び出します。
  • Java サービスは addService() を通じて登録され、addService() はwriteStrongBinder()を通じてネイティブ インターフェイスを呼び出してJNI に登録します。
  • Java サービスが登録されると、JNI 内に JavaBBinderHolder が作成されます。これは、ネイティブと通信するJavaBBinder をカプセル化し、JavaBBinder は BBinder を継承します。
  • Java クライアント呼び出しは、最終的に Java サービスの onTransact() を呼び出し、Java 側の stub.onTransact() にコールバックします。

おすすめ

転載: blog.csdn.net/jingerppp/article/details/131420251