IBinder對象在進程間傳遞的形式(二)

這篇文章是對IBinder對象在進程間傳遞的形式(一)這篇文章的補充,首先還是把service啟動過程的流程圖貼上來
 
 
 
Android中主要通過2種方法來獲得service IBinder:
 
1.       通過ServiceManager.getService(String Descriptor)來獲得Service Manager管理的service的IBinder。
 
2.       Client獲得Application Service的IBinder,如下圖描述的binder service流程。
 
 
 
 
    不管哪種方法,均涉及到IBinder在不同進程間的傳遞,因為不論是Application Service 還是System Service,它們在註冊時均會涉及到第三方進程,如Application Service註冊在Acitivity Manager中(system_server進程),System Service註冊在Service Manager中(servicemanager進程)。
 
    從Service Manager中獲得IBinder涉及到service和servicemanager進程以及client和servicemanager進程之間的IBinder傳遞;
 
    獲得Application Service的IBinder,更是涉及到ApplicationService和system_server進程以及client和system_server進程之間的IBinder傳遞,如上圖所示。
 
    研究這個主題的意義在於,雖然從表面上看,在用到IBinder時,我們在應用中使用的是同一個接口,但是它的實質是怎麼樣的?是一套什麼樣的機制保證在同進程間client調用的是service的實體,而在不同進程間則是調用的service的proxy。
 
    關於Binder的傳遞過程,首先需要瞭解一個service的類圖關系。下圖為JNI以上的類圖。涉及到JNI和JAVA層的Binder對應關系。JNI層以下的Binder不涉及到這個文章的主題。
 
    IISERVICEInterface表示aidl文件生成的或者用戶自定義的當前service的接口文件,SERVICE表示當前service的實現。IISERVICEInterface.Proxy表示當前service的代理。
 
    在JNI層,JavaBBinder繼承自BBinder,是Binder的實體,它是在BBinder的基礎上添加瞭成員變量jobjectmObject,該成員變量指向JAVA的SERVICE對象。這麼做的好處就是在同進程間傳遞時,接收方能夠直接向JAVA層返回SERVICE對象。
 
    JavaBBinderHolder類,可以說是JavaBBinder類的一個容器,當JAVA層的SERVICE對象被創建時,就會相應的創建一個JavaBBinderHolder實例(android_os_Binder_init@android_util_Binder.cpp),但是隻有SERVICE對象被IPC傳遞時,JavaBBinderHolder對象才會創建一個JavaBBinder實例(JavaBBinderHolder::get(env)@ android_util_Binder.cpp)。
 
 
 
 
   
1.      IBinder在IPC通信中的發送過程
 
   
 
   IBinder在IPC通信中,android通過Parcel類的成員函數writeStrongBinder()寫入IBinder,在向Parcel寫入IBinder時,首先會判斷JAVA層IBinder的類型並轉化為native的IBinder類型,下面介紹它的的2種類型。
 
    android_os_Parcel_writeStrongBinder()@android_util_Binder.cpp
 
       parcel->writeStrongBinder(ibinderForJavaObject(env, object));@ android_util_Binder.cpp
 
[java]
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) : NULL; 
    } 
 
    if (env->IsInstanceOf(obj, gBinderProxyOffsets.mClass)) { 
        return (IBinder*) 
            env->GetIntField(obj, gBinderProxyOffsets.mObject); 
    } 
 
    LOGW("ibinderForJavaObject: %p is not a Binder object", obj); 
    return NULL; 

 
 
1.1  IBinder是以JavaBBinder的形式傳遞的,即是一個ServiceIBinder的reference。如上圖中的第一個return語句。
 
這種較常見,作為傳輸介質的IBinder所處的service和發送方在同一進程中。
 

 

    Service處在Process A中,ProcessA為IBinder發送方。
 
1.2  IBinder是以BpBinder的形式傳遞的,即是一個ServiceIBinder的Proxy。如Service bind圖中的第二個return語句。
 
   
     Service 處在Process C中,Process A為IBinder發送方。
 
 
    這種情況比較典型的就是bindservice時會用到,如圖中最後一步中AMS通知client bind成功時,傳遞給client的就是一個service IBinder的Proxy。作為傳輸介質的IBinder所處的service和發送方在不同的進程中。
 
 
 
    Kernel中,Binder Module會根據IPC通信雙方是否處於同一個進程中,來設置binder object type值,下圖code描述。
 
 
    當IBinder的發送方和Service在同一進程的話,那麼傳遞給內核的binder_object的type為BINDER_TYPE_BINDER,由於IBinder發送到內核均是在進程間傳遞的,所以Binder Module driver會將type改為BINDER_TYPE_HANDLE.
 
 
 
    當IBinder的發送方和Service不在同一進程的話,那麼傳遞給內核的binder_object的type就為BINDER_TYPE_HANDLE,並且這種情況接收方和Service可能處在同一個進程中,因此當這種情況發生時,需要將其改為BINDER_TYPE_BINDER。這一點在下面會介紹。
 
 
    IBinder發送的流程:
 
 
 
1.      IBinder在IPC通信中的接收過程
    首先查看一下Parcel的unflatten_binder@Parcel.cpp(readStrongBinder()方法會調用到):
 
 
[java]
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; 

 
2.1   當從BinderModule中讀出的binder_object的type為BINDER_TYPE_BINDER,說明接收方收到的Service是同進程的,那麼此時直接將保存在cookie中的Service的實體傳遞給JNI層。
 
 
    此時,JNI層收到的Service實體為一個JavaBBinder對象,由上面的類圖可以知道,JavaBBinder對象中包含著JAVA層SERVICE對象的引用。
 
 
2.2 當從Binder Module中讀出的binder_object的type為BINDER_TYPE_HANDLER,說明接收方收到的Service不是同進程的,那麼會創建或者引用已有
 
的一個BpBinder對象給JNI層。
 
 
 
   
 
    Service 處在Process C中,Process B為IBinder接收方。
 
 
 
 
2.2.1         如何創建BpBinder對象
 
通過調用ProcessState的方法getStrongProxyForHandle(),來創建或者引用已有的一個BpBinder對象。
 
A.     首先,檢查當前所要創建的BpBinder是否已經在Vector mHandleToObject中存在,每個進程中均會維護這麼一個Vector,保存當前進程獲得的所有BpBinder對象。沒有分析出這麼做的原因,可能為瞭資源的重復利用,避免每次使用完都得回收的問題。
 
B.     如果沒有查找到BpBinder對象,創建一個新的BpBinder對象,並會向mHandleToObject保存這個對象。
 
由於每個Service均會對應一個唯一的handle,因此可以通過這個handle來標示BpBinder對象。
 
 
[java]
ProcessState::handle_entry* ProcessState::lookupHandleLocked(int32_t handle) 

    const size_t N=mHandleToObject.size(); 
    if (N <= (size_t)handle) { 
        handle_entry e; 
        e.binder = NULL; 
        e.refs = NULL; 
        status_t err = mHandleToObject.insertAt(e, N, handle+1-N); 
        if (err < NO_ERROR) return NULL; 
    } 
    return &mHandleToObject.editItemAt(handle); 

sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle) 

    sp<IBinder> result; 
    AutoMutex _l(mLock); 
    handle_entry* e = lookupHandleLocked(handle); 
    if (e != NULL) { 
        // We need to create a new BpBinder if there isn't currently one, OR we 
        // are unable to acquire a weak reference on this current one.  See comment 
        // in getWeakProxyForHandle() for more info about this. 
        IBinder* b = e->binder; 
        if (b == NULL || !e->refs->attemptIncWeak(this)) { 
            b = new BpBinder(handle);  
            e->binder = b; 
            if (b) e->refs = b->getWeakRefs(); 
            result = b; 
        } else { 
            // This little bit of nastyness is to allow us to add a primary 
            // reference to the remote proxy when this team doesn't have one 
            // but another team is sending the handle to us. 
            result.force_set(b); 
            e->refs->decWeak(this); 
        } 
    } 
    return result; 

 
 
2.2.2         JNI層的特殊處理
 
   
     javaObjectForIBinder@android_util_Binder.cpp
 
[java]
// Someone else's…  do we know about it? 
    jobject object = (jobject)val->findObject(&gBinderProxyOffsets); 
    if (object != NULL) { 
        jobject res = env->CallObjectMethod(object, gWeakReferenceOffsets.mGet); 
        if (res != NULL) { 
            LOGV("objectForBinder %p: found existing %p!\n", val.get(), res); 
            return res; 
        } 
        LOGV("Proxy object %p of IBinder %p no longer in working set!!!", object, val.get()); 
        android_atomic_dec(&gNumProxyRefs); 
        val->detachObject(&gBinderProxyOffsets); 
        env->DeleteGlobalRef(object); 
    } 
    object = env->NewObject(gBinderProxyOffsets.mClass, gBinderProxyOffsets.mConstructor); 
    if (object != NULL) { 
        LOGV("objectForBinder %p: created new %p!\n", val.get(), object); 
        // The proxy holds a reference to the native object. 
        env->SetIntField(object, gBinderProxyOffsets.mObject, (int)val.get()); 
        val->incStrong(object); 
        // The native object needs to hold a weak reference back to the 
        // proxy, so we can retrieve the same proxy if it is still active. 
        jobject refObject = env->NewGlobalRef( 
                env->GetObjectField(object, gBinderProxyOffsets.mSelf)); 
        val->attachObject(&gBinderProxyOffsets, refObject, 
                jnienv_to_javavm(env), proxy_cleanup); 
        // Note that a new object reference has been created. 
        android_atomic_inc(&gNumProxyRefs); 
        incRefsCreated(env); 
    } 
 
 
①   首先,在BpBinder對象中查找是否保存相關的BinderProxy的對象,如果有,向JAVA層返回這個對象;
 
②   如果沒有,創建一個BinderProxy對象;
 
③   將新創建的BinderProxy對象,attach到BpBinder對象中。
 
結合下面的關系圖,我們得出這樣的邏輯關系:
 
a.      每個進程中會保存多個當前進程調用過的BpBinder對象;
 
b.      每個BpBinder對象都會保存與之對應的JAVA層的BinderProxy。
 
    將創建的BinderProxyattach到BpBinder的意義在於:通過這種方式,JAVA應用層頻繁獲取同一service的IBinder時,獲取的是同一個BinderProxy。
 

摘自 杜文濤的專欄

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *