聽說你 Binder 機制學的不錯,來解決下這幾個問題(三)

看書的小蝸牛發表於2017-03-15

很多文章將Binder框架定義了四個角色:Server,Client,ServiceManager、以及Binder驅動,但這容易將人引導到歧途:好像所有的Binder服務都需要去ServiceManager去註冊才能使用,其實不是這樣。例如,平時APP開發通過bindService啟動的服務,以及有些自己定義的AIDL遠端呼叫,都不一定都ServiceManager註冊這條路,個人理解:ServiceManager主要功能是:管理系統服務,比如AMS、WMS、PKMS服務等,而APP通過的bindService啟動的Binder服務其實是由SystemServer的ActivityManagerService負責管理。這篇主要關注Android APP Java層Binder通訊一些奇葩點:

  • ServiceManager addService的限制(並非服務都能使用ServiceManager的addService)
  • bindService啟動Service與Binder服務實體的流程
  • Java層Binder實體與與BinderProxy是如何例項化及使用的,與Native層的關係是怎樣的
  • Parcel readStrongBinder與writeStrongBinder的原理(首先兩端知曉)

ServiceManager addService的限制--並非服務都能通過addService新增到ServiceManager

ServiceManager其實主要的物件導向是系統服務,大部分系統服務都是由SystemServer程式總新增到ServiceManager中去的,在通過ServiceManager新增服務的時候,是有些許可權校驗的,原始碼如下:

int svc_can_register(unsigned uid, uint16_t *name)
 {
    unsigned n;
    // 誰有許可權add_service 0程式,或者 AID_SYSTEM程式
    if ((uid == 0) || (uid == AID_SYSTEM))
        return 1;
     for (n = 0; n < sizeof(allowed) / sizeof(allowed[0]); n++)
        if ((uid == allowed[n].uid) && str16eq(name, allowed[n].name))
            return 1;
    return 0;
}複製程式碼

可以看到 (uid == 0) 或者 (uid == AID_SYSTEM)的程式都是可以新增服務的,uid=0,代表root使用者,而uid=AID_SYSTEM,代表系統使用者 。或者是一些特殊的配置程式。SystemServer程式在被Zygote建立的時候,就被分配了UID 是AID_SYSTEM(1000),

private static boolean startSystemServer()
        throws MethodAndArgsCaller, RuntimeException {
    /* Hardcoded command line to start the system server */
    String args[] = {
        "--setuid=1000",
        "--setgid=1000",
        "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,3001,3002,3003,3006,3007",
        "--capabilities=130104352,130104352",
        "--runtime-init",
        "--nice-name=system_server",
        "com.android.server.SystemServer",
    };複製程式碼

Android每個APP的UID,都是不同的,用了Linux的UID那一套,但是沒完全沿用,這裡不探討,總之,普通的程式是沒有許可權註冊到ServiceManager中的,那麼APP平時通過bindService啟動的服務怎麼註冊於查詢的呢?接管這個任務的就是SystemServer的ActivityManagerService

bindService啟動Service與Binder服務實體的流程 (ActivityManagerService)

  • bindService的框架
  • binder服務例項化與轉化
  • 業務邏輯的喚醒
  • 請求代理的轉化與喚醒

bindService比startService多了一套Binder通訊,其餘的流程基本相同,而startService的流程,同startActivity差不多,四大元件的啟動流程這裡不做分析點,主要看bindService中C/S通訊的建立流程,在這個流程裡面,APP與服務端互為C/S的特性更明顯,在APP開發的時候,binder服務是通過Service來啟動的。Service的啟動方式有兩種startService,與bindService,這裡只考慮後者,另外啟動的binder服務也分為兩種情況:第一種,client同server位於同一程式,可以看做內部服務,第二種,Client與Server跨程式,即使是位於同一個APP,第一桶可以不用AIDL來編寫,但是第二種必須通過AIDL實現跨程式通訊,看一個最簡單的AIDL例子,首先在定義一個aidl介面:

IMyAidlInterface.aidl

interface IMyAidlInterface {
void communicate(int count);
}

IMyAidlInterface.aidl定義了通訊的藉口,通過build之後,構建工具會自動為IMyAidlInterface.aidl生成一些輔助類,這些輔助類主要作用是生成Binder通訊協議框架,必須保證兩方通訊需要指令相同,才能解析通訊內容。天王蓋地虎,寶塔鎮河妖。Java層Binder的對應關係Binder與BinderProxy從這裡可以看出,binder採用了代理模式 stub與proxy對應,使用aidl實現的服務時候,Client如果想要獲得Binder實體的代理可以通過asInterface來處理,比如如果在同一程式就是實體,不在就新建代理物件

    public interface IMyAidlInterface extends android.os.IInterface {

    public static abstract class Stub extends android.os.Binder implements com.snail.labaffinity.IMyAidlInterface {
        private static final java.lang.String DESCRIPTOR = "com.snail.labaffinity.IMyAidlInterface";

        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        public static com.snail.labaffinity.IMyAidlInterface asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.snail.labaffinity.IMyAidlInterface))) {
                return ((com.snail.labaffinity.IMyAidlInterface) iin);
            }
            return new com.snail.labaffinity.IMyAidlInterface.Stub.Proxy(obj);
        }

        @Override
        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_communicate: {
                    data.enforceInterface(DESCRIPTOR);
                    int _arg0;
                    _arg0 = data.readInt();
                    this.communicate(_arg0);
                    reply.writeNoException();
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements com.snail.labaffinity.IMyAidlInterface {
            private android.os.IBinder mRemote;

            Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }

            @Override
            public android.os.IBinder asBinder() {
                return mRemote;
            }

            public java.lang.String getInterfaceDescriptor() {
                return DESCRIPTOR;
            }

            @Override
            public void communicate(int count) 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.writeInt(count);
                    mRemote.transact(Stub.TRANSACTION_communicate, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
        }

        static final int TRANSACTION_communicate = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    }

    public void communicate(int count) throws android.os.RemoteException;
}複製程式碼

啟動Binder服務端封裝Service,之所以成為封裝Service,是因為Service對於Binder實體的最大作用是個作為新建服務的入口:

public class AidlService extends Service {
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return new BBinderService();
    }

    public class BBinderService extends IMyAidlInterface.Stub {

        @Override
        public void communicate(int count) throws RemoteException {
        }
    }
}複製程式碼

而啟動的入口:

public class MainActivity extends AppCompatActivity {
    ...
   void bind(){
    Intent intent = createExplicitFromImplicitIntent(MainActivity.this, new Intent("com.snail.labaffinity.service.AidlService"));
    bindService(intent, new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            IMyAidlInterface  iMyAidlInterface = IMyAidlInterface.Stub.asInterface(iBinder);
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
        }
    }, Context.BIND_AUTO_CREATE);
    }
 }複製程式碼

以上四個部分就組成了AIDL跨程式服務的基本元件,現在從ActivitybindService入口開始分析:bindService大部分的流程與startActivity類似,其實都是通過AMS啟動元件,這裡只將一些不同的地方,Activity啟動只需要Intent就可以了,而Service的bind需要一個ServiceConnection物件,這個物件其實是為了AMS端在啟動Service後回撥用的,ServiceConnection是個介面,其例項在ContextImpl的:

private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags,
                                  UserHandle user) {
    IServiceConnection sd;
    if (conn == null) {
        throw new IllegalArgumentException("connection is null");
    }
    if (mPackageInfo != null) {
        sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(),
                mMainThread.getHandler(), flags);
    } else {
        throw new RuntimeException("Not supported in system context");
    }
    validateServiceIntent(service);
    try {
        IBinder token = getActivityToken();
        if (token == null && (flags & BIND_AUTO_CREATE) == 0 && mPackageInfo != null
                && mPackageInfo.getApplicationInfo().targetSdkVersion
                < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
            flags |= BIND_WAIVE_PRIORITY;
        }
        service.prepareToLeaveProcess();
        int res = ActivityManagerNative.getDefault().bindService(
                mMainThread.getApplicationThread(), getActivityToken(), service,
                service.resolveTypeIfNeeded(getContentResolver()),
                sd, flags, getOpPackageName(), user.getIdentifier());
        if (res < 0) {
            throw new SecurityException(
                    "Not allowed to bind to service " + service);
        }
        return res != 0;
    } catch (RemoteException e) {
        throw new RuntimeException("Failure from system", e);
    }
}複製程式碼

mPackageInfo是一個LoadApk類,通過它的getServiceDispatcher獲得一個IServiceConnection物件,這個物件一個Binder實體,看一下具體原理

public final IServiceConnection getServiceDispatcher(ServiceConnection c,
        Context context, Handler handler, int flags) {
    synchronized (mServices) {
        LoadedApk.ServiceDispatcher sd = null;
        ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> map = mServices.get(context);
        if (map != null) {
            sd = map.get(c);
        }
        if (sd == null) {
            sd = new ServiceDispatcher(c, context, handler, flags);
            if (map == null) {
                map = new ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>();
                mServices.put(context, map);
            }
            map.put(c, sd);
        } else {
            sd.validate(context, handler);
        }
        return sd.getIServiceConnection();
    }
}複製程式碼

在LoadApk中IServiceConnection物件是通過context鍵值來儲存ServiceDispatcher物件,而ServiceDispatcher物件記憶體會有個InnerConnection物件,該物件就是getServiceDispatcher的返回物件。因此bindServiceCommon最終呼叫
ActivityManagerNative.getDefault().bindService(x,x,x,x,x sd, x, x, x) 的時候,傳遞的引數sd其實就是一個InnerConnection物件,這是個Binder實體。但是,Binder.java中的Binder只是對native層BBinder的一個簡單封裝,真正的例項化還是通過JNI到native層去建立一個JavaBBinderHolder物件,並初始化gBinderOffsets,讓其能對映Java層Binder物件,而JavaBBinderHolder中又可以例項化BBinder的例項JavaBBinder,不過BBinder的例項化時機並不在這裡,而是在Parcel物件writeStrongBinder的時候,

static struct bindernative_offsets_t
{
    // Class state.
    jclass mClass;
    jmethodID mExecTransact;

    // Object state.
    jfieldID mObject;

} gBinderOffsets;

static void android_os_Binder_init(JNIEnv* env, jobject obj)
{
    JavaBBinderHolder* jbh = new JavaBBinderHolder();
    jbh->incStrong((void*)android_os_Binder_init);
    env->SetIntField(obj, gBinderOffsets.mObject, (int)jbh);
}複製程式碼

繼續往下看bindService,會呼叫到ActivityManagerProxy的bindService

public int bindService(IApplicationThread caller, IBinder token,
        Intent service, String resolvedType, IServiceConnection connection,
        int flags, int userId) throws RemoteException {
    Parcel data = Parcel.obtain();
    Parcel reply = Parcel.obtain();
    data.writeInterfaceToken(IActivityManager.descriptor);
    data.writeStrongBinder(caller != null ? caller.asBinder() : null);
    data.writeStrongBinder(token);
    service.writeToParcel(data, 0);
    data.writeString(resolvedType);
    data.writeStrongBinder(connection.asBinder());
    data.writeInt(flags);
    data.writeInt(userId);
    mRemote.transact(BIND_SERVICE_TRANSACTION, data, reply, 0);
    reply.readException();
    int res = reply.readInt();
    data.recycle();
    reply.recycle();
    return res;
}複製程式碼

利用Parcel的writeStrongBinder會將Binder實體寫入到Parcel中去,這裡首先看一下 Parcel data = Parcel.obtain();在java層Parcel只是一個容器,具體Parcel相關的操作都在Native層

static jint android_os_Parcel_create(JNIEnv* env, jclass clazz)
{
    Parcel* parcel = new Parcel();
    return reinterpret_cast<jint>(parcel);
}複製程式碼

這裡的返回值,其實就是Parcel物件的地址,被賦值給了Parcel.java的mNativePtr成員變數,方便Native呼叫,接著看writeStrongBinder的實現,其實就是呼叫Parcel.cpp中的對應方法,通過flatten_binder將Binder實體物件打扁,建立flat_binder_object寫入Parcel中,

static void android_os_Parcel_writeStrongBinder(JNIEnv* env, jclass clazz, jint 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主要為Java層Binder例項化native binder物件:

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

    if (env->IsInstanceOf(obj, gBinderOffsets.mClass)) {
        JavaBBinderHolder* jbh = (JavaBBinderHolder*)
            env->GetIntField(obj, gBinderOffsets.mObject);
        return jbh != NULL ? jbh->get(env, obj) : NULL;
    }

    if (env->IsInstanceOf(obj, gBinderProxyOffsets.mClass)) {
        return (IBinder*)
            env->GetIntField(obj, gBinderProxyOffsets.mObject);
    }
    return NULL;
}複製程式碼

如果BBinder還沒例項化,要通過JavaBBinderHolder的get函式例項化一個BBinder物件,這裡就是JavaBBinder物件,綜上分析Java層與Native的Binder其對應關係如下:

聽說你 Binder 機制學的不錯,來解決下這幾個問題(三)
Java層Binder與native 層BBiner.png

BBinder物件被Parcel轉換成flat_binder_object,經過一次拷貝寫入目標程式,並執行BINDER_TYPE_BINDER與BINDER_TYPE_HANDLE的轉換,如下:

static void
binder_transaction(struct binder_proc *proc, struct binder_thread *thread,
    struct binder_transaction_data *tr, int reply)
    ...
 fp = (struct flat_binder_object *)(t->buffer->data + *offp);

    switch (fp->type) {
        case BINDER_TYPE_BINDER:
        case BINDER_TYPE_WEAK_BINDER: {..
            if (fp->type == BINDER_TYPE_BINDER)
                fp->type = BINDER_TYPE_HANDLE;
            else
                fp->type = BINDER_TYPE_WEAK_HANDLE;
            fp->handle = ref->desc;
        } break;
        case BINDER_TYPE_HANDLE:
        case BINDER_TYPE_WEAK_HANDLE: {..
            struct binder_ref *ref = binder_get_ref(proc, fp->handle);
            if (ref->node->proc == target_proc) {
                if (fp->type == BINDER_TYPE_HANDLE)
                    fp->type = BINDER_TYPE_BINDER;
                else
                    fp->type = BINDER_TYPE_WEAK_BINDER;
                fp->binder = ref->node->ptr;
                fp->cookie = ref->node->cookie;
            } else {
                struct binder_ref *new_ref;
                new_ref = binder_get_ref_for_node(target_proc, ref->node);
                fp->handle = new_ref->desc;
            }
        } break;
}複製程式碼

在核心中,bindService中的InnerConnection會由BINDER_TYPE_BINDER轉換成BINDER_TYPE_HANDLE,之後,AMS執行緒被喚醒後,執行後面的流程,在前文分析Parcel資料轉換的時候,在Binder執行緒被喚醒繼續執行的時候,會將資料對映到一個natvie Parcel物件中

status_t IPCThreadState::executeCommand(int32_t cmd)
 {
    BBinder* obj;
    switch (cmd) {
     ..
    // read到了資料請求,這裡是需要處理的邏輯 ,處理完畢,
    case BR_TRANSACTION:
        {
            binder_transaction_data tr;
            result = mIn.read(&tr, sizeof(tr));
            Parcel buffer;
            <!--關鍵點1 -->
            buffer.ipcSetDataReference(
                reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
                tr.data_size,
                reinterpret_cast<const size_t*>(tr.data.ptr.offsets),
                tr.offsets_size/sizeof(size_t), freeBuffer, this);
            ...
          <!--關鍵點2 -->
        if (tr.target.ptr) {
            sp<BBinder> b((BBinder*)tr.cookie);
            const status_t error = b->transact(tr.code, buffer, &reply, tr.flags);
            if (error < NO_ERROR) reply.setError(error);
        }
        ..
      }
   }        複製程式碼

首先看一下關鍵點1 ,這裡將核心資料對映到一個使用者空間的Parcel物件中去,之後在呼叫目標Service的transact函式,進而呼叫他的onTrasanct函式 , 通過前面的分析知道,Java層Binder在註冊時候,最終註冊的是JavaBBinder物件,看一下它的onTrasanct函式:

virtual status_t onTransact(
        uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0)
    {
        JNIEnv* env = javavm_to_jnienv(mVM);
        IPCThreadState* thread_state = IPCThreadState::self();
        const int strict_policy_before = thread_state->getStrictModePolicy();
        thread_state->setLastTransactionBinderFlags(flags);
        ..
        jboolean res = env->CallBooleanMethod(mObject, gBinderOffsets.mExecTransact,
            code, (int32_t)&data, (int32_t)reply, flags);
         ..
        return res != JNI_FALSE ? NO_ERROR : UNKNOWN_TRANSACTION;
    }複製程式碼

關鍵程式碼只有一句:env->CallBooleanMethod(mObject, gBinderOffsets.mExecTransact, code, (int32_t)&data, (int32_t)reply, flags),其實就是呼叫Binder.java的execTransact函式,該函式首先將Native的Parcel對映成Jave層Parcel,之後呼叫BBinder子類的onTransact函式執行對應的業務邏輯:

private boolean execTransact(int code, int dataObj, int replyObj,
            int flags) {
        Parcel data = Parcel.obtain(dataObj);
        Parcel reply = Parcel.obtain(replyObj);
        boolean res;
        try {
            res = onTransact(code, data, reply, flags);
        } ...
        reply.recycle();
        data.recycle();
        return res;
    }}複製程式碼

對於AMS而bindService對應的操作如下

public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
    throws RemoteException {
    。。
    case BIND_SERVICE_TRANSACTION: {
    data.enforceInterface(IActivityManager.descriptor);
    IBinder b = data.readStrongBinder();
    IApplicationThread app = ApplicationThreadNative.asInterface(b);
    IBinder token = data.readStrongBinder();
    Intent service = Intent.CREATOR.createFromParcel(data);
    String resolvedType = data.readString();
    b = data.readStrongBinder();
    int fl = data.readInt();
    int userId = data.readInt();
    IServiceConnection conn = IServiceConnection.Stub.asInterface(b);
    int res = bindService(app, token, service, resolvedType, conn, fl, userId);
    reply.writeNoException();
    reply.writeInt(res);
    return true;
}複製程式碼

b = data.readStrongBinder()會先讀取Binder物件,這裡會呼叫本地函式nativeReadStrongBinder(mNativePtr),mNativePtr就是Native層Parcel的首地址:

public final IBinder readStrongBinder() {
    return nativeReadStrongBinder(mNativePtr);
}複製程式碼

nativeReadStrongBinder(mNativePtr)會將本地Binder物件轉化成Java層物件,其實就是將傳輸的InnerConnection讀取出來,不過由於Binder驅動將BINDER_TYPE_BINDER轉換成了BINDER_TYPE_HANDLE,對於AMS其實是例項化BinderProxy

static jobject android_os_Parcel_readStrongBinder(JNIEnv* env, jclass clazz, jint nativePtr)
{
    Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
    if (parcel != NULL) {

        // /parcel->readStrongBinder() 其實就會建立BpBInder、
        return javaObjectForIBinder(env, parcel->readStrongBinder());
    }
    return NULL;
}複製程式碼

首先會利用Parcel.cpp的parcel->readStrongBinder(),讀取binder物件,這裡會根據flat_binder_object的型別,分別進行BBinder與BpBinder對映,如果是Binder實體直接將指標賦值out,如果不是,則根據handle獲取或者新建BpBinder返回給out。

status_t unflatten_binder(const sp<ProcessState>& proc,
    const Parcel& in, sp<IBinder>* out)
{
    const flat_binder_object* flat = in.readObject(false);

    if (flat) {
        switch (flat->type) {
            case BINDER_TYPE_BINDER:
                *out = static_cast<IBinder*>(flat->cookie);
                return finish_unflatten_binder(NULL, *flat, in);
            case BINDER_TYPE_HANDLE:
                *out = proc->getStrongProxyForHandle(flat->handle);
                return finish_unflatten_binder(
                    static_cast<BpBinder*>(out->get()), *flat, in);
        }        
    }
    return BAD_TYPE;
}複製程式碼

之後會牽扯一個將native binder轉換成java層Binder的操作,javaObjectForIBinder,這個函式很關鍵,是理解Java層BinderProxy或者BBinder實體的關鍵:

jobject javaObjectForIBinder(JNIEnv* env, const sp<IBinder>& val)
{
    if (val == NULL) return NULL;
    <!--關鍵點1-->
    if (val->checkSubclass(&gBinderOffsets)) {
        jobject object = static_cast<JavaBBinder*>(val.get())->object();
        return object;
    }
    AutoMutex _l(mProxyLock);
    <!--關鍵點2-->
    jobject object = (jobject)val->findObject(&gBinderProxyOffsets);
    if (object != NULL) {
        android_atomic_dec(&gNumProxyRefs);
        val->detachObject(&gBinderProxyOffsets);
        env->DeleteGlobalRef(object);
    }
    <!--關鍵點3-->
    object = env->NewObject(gBinderProxyOffsets.mClass, gBinderProxyOffsets.mConstructor);
    if (object != NULL) {
        env->SetIntField(object, gBinderProxyOffsets.mObject, (int)val.get());
        val->incStrong((void*)javaObjectForIBinder);
        jobject refObject = env->NewGlobalRef(
                env->GetObjectField(object, gBinderProxyOffsets.mSelf));
        val->attachObject(&gBinderProxyOffsets, refObject,
                jnienv_to_javavm(env), proxy_cleanup);
        sp<DeathRecipientList> drl = new DeathRecipientList;
        drl->incStrong((void*)javaObjectForIBinder);
        env->SetIntField(object, gBinderProxyOffsets.mOrgue, reinterpret_cast<jint>(drl.get()));
        android_atomic_inc(&gNumProxyRefs);
        incRefsCreated(env);
    }
    return object;
}複製程式碼

先看關鍵點1, checkSubclass預設返回false,但是JavaBBinder,該類對此函式進行了覆蓋,如果是JavaBBinder,就會返回true,但如果是BpBinder,則會返回false,

bool    checkSubclass(const void* subclassID) const
{
    return subclassID == &gBinderOffsets;
}複製程式碼

再看關鍵點2,如果是BpBinder,則需要首先在gBinderProxyOffsets中查詢,是不是已經新建了Java層代理BinderProxy物件,如果沒有,則新建即可,如果新建過,就看是否還存在快取有效的BinderProxy。最後看關鍵點3 :
env->NewObject(gBinderProxyOffsets.mClass, gBinderProxyOffsets.mConstructor)

其實就是新建BinderProxy物件,Java層的BinderProxy都是Native新建的,Java層並沒有BinderProxy的新建入口,之後,再通過IServiceConnection.Stub.asInterface(b)進行轉換,例項化一個IServiceConnection.Proxy代理對,該物件在Binder通訊的基礎上封裝了業務邏輯,其實就是一些具體的操作。

 public static XXXAidlInterface asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof XXXAidlInterface))) {
                return ((XXXAidlInterface) iin);
            }
            return new XXXAidlInterface.Stub.Proxy(obj);
        }複製程式碼

這裡注意一點杜宇BinderProxy,obj.queryLocalInterface(DESCRIPTOR)返回為null,對於Binder實體,返回的是Binder自身,這樣就能為上層區分出是生成代理還是存根自身,整體物件轉換流程如下:

聽說你 Binder 機制學的不錯,來解決下這幾個問題(三)
ServiceConnection的回撥作用

到這裡分析了一半,Java層命令及回撥Binder入口已經被傳遞給AMS,AMS之後需要負責啟動Service,並通過回撥入口為Client繫結服務,跟蹤到AMS原始碼

public int bindService(IApplicationThread caller, IBinder token,
        Intent service, String resolvedType,
        IServiceConnection connection, int flags, int userId) {
    ...
    synchronized(this) {
        return mServices.bindServiceLocked(caller, token, service, resolvedType,
                connection, flags, userId);
    }
}複製程式碼

最後呼叫ActiveService的bindServiceLocked,這裡會分三中情況,

  • Service已經經啟動
  • Service未啟動,但是程式已經啟動
  • Service與程式君未啟動

不過這裡只討論“ Service未啟動,但是程式已經啟動的情況”,關鍵程式碼如下

 int bindServiceLocked(IApplicationThread caller, IBinder token,
            Intent service, String resolvedType,
            IServiceConnection connection, int flags, int userId) {

        try {
            .。。

            if ((flags&Context.BIND_AUTO_CREATE) != 0) {
                s.lastActivity = SystemClock.uptimeMillis();
          <!--關鍵點1-->
                if (bringUpServiceLocked(s, service.getFlags(), false) != null) {
                    return 0;
                }
            }
          <!--關鍵點2-->
           ..
           requestServiceBindingLocked(s, b.intent, false);
           ..
        }
}複製程式碼

關鍵點1其實就是啟動Service,主要是通過ApplicationThread的binder通訊通知App端啟動Service,這個流程同Activity啟動一樣。關鍵點2是Service特有的:requestServiceBindingLocked,這個命令是告訴APP端:“在Service啟動後需要向AMS發訊息,之後AMS才能向其他需要繫結該Service的Client傳送反饋”。

AMS端
private final boolean requestServiceBindingLocked(ServiceRecord r,
        IntentBindRecord i, boolean rebind) {
    if ((!i.requested || rebind) && i.apps.size() > 0) {
       ..
          r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind);
       ..
      }        }
    return true;
}

 APP端
 private void handleBindService(BindServiceData data) {
    Service s = mServices.get(data.token);
    ...
    if (!data.rebind) {
        IBinder binder = s.onBind(data.intent);
        ActivityManagerNative.getDefault().publishService(
                data.token, data.intent, binder);
    }
}複製程式碼

ActivityManagerNative.getDefault().publishService會將啟動的Binder服務實體傳遞給AMS,上面分析過Binder實體傳輸,這裡的原理是一樣的,AMS端在傳輸結束後,會獲得Service端服務實體的引用,這個時候,就能通過最初的InnerConnection的回撥將這個服務傳遞給Client端。Binder實體與引用的整體流程圖如下:

聽說你 Binder 機制學的不錯,來解決下這幾個問題(三)
bindSerivce整體流程圖

如果要深究Activity的bindService流程,可以按以下幾步來分析

  • 1、Activity呼叫bindService:通過Binder通知ActivityManagerService,要啟動哪個Service
  • 2、ActivityManagerService建立ServiceRecord,並利用ApplicationThreadProxy回撥,通知APP新建並啟動Service啟動起來
  • 3、ActivityManagerService把Service啟動起來後,繼續通過ApplicationThreadProxy,通知APP,bindService,其實就是讓Service返回一個Binder物件給ActivityManagerService,以便AMS傳遞給Client
  • 4、ActivityManagerService把從Service處得到這個Binder物件傳給Activity,這裡是通過IServiceConnection binder實現。
  • 5、Activity被喚醒後通過Binder Stub的asInterface函式將Binder轉換為代理Proxy,完成業務代理的轉換,之後就能利用Proxy進行通訊了。

聽說你 Binder 機制學的不錯,來解決下這幾個問題(三)
bindService流程

聽說你Binder機制學的不錯,來解決下這幾個問題(一)
聽說你 Binder 機制學的不錯,來解決下這幾個問題(二)
聽說你 Binder 機制學的不錯,來解決下這幾個問題(三)

僅供參考,歡迎指正

相關文章