Android Binder淺析 --- Client獲取CameraService

Android Binder淺析 — Client獲取CameraService

繼前一章Android Binder淺析 — CameraService往ServiceManager添加服務後,CameraService已經添加到ServiceManager的svinfo服務列表中去,我們現在要從客戶端向ServiceManager拿到這個服務並使用,源碼分析下客戶端是如何拿到這個服務的?

在這裏插入圖片描述



ServiceManger代碼架構

爲什麼要先說ServiceManager架構呢?
因爲在Camera相機這個模塊,最終會使用到其底層的CamerService服務,這些服務都是被註冊到底層ServiceManager(c++)上去,而java層要使用到這些服務,會經過java層的ServiceManager然後跨進程binder去獲取服務,調用服務等,而java層這塊ServiceManager代碼架構理解後有助於對整體代碼的認識,並且我們看源碼不僅僅是看代碼邏輯如何調用,更多的是理解其各個模塊的設計架構精髓,這纔是最重要的!

首先看看我總結ServiceManager的架構:
在這裏插入圖片描述
架構解釋:
上圖可以分爲三個模塊:Binder模塊、ServiceManager模塊以及CamerManager模塊,從邏輯調用的方向從外向內依次解釋各個模塊的作用:


CameraManager模塊

主體包含上圖Camera關鍵字的幾個類和IBinder.DeathRecipient類!

CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
manager.openCamera(cameraId, callback,handler);

CameraManager是我們做相機開發時會用到的類,通過調用他的方法openCamera可以幫助我們打開相機,這個類主要是提供給外部模塊使用。
但是openCamera如何執行,真正工作的則是CameraManagerGlobal,它繼承ICameraServiceListener.Stub和實現IBinder.DeathRecipient接口,ICameraServiceListener.Stub使用了aidl方式的的Stub類,說明它是一個binder服務端的類,這裏的作用主要是會將CameraManagerGlobal自己這個服務Listener監聽註冊到CamerService服務端,待CameraService狀態變化回調自己,後面代碼講到時具體講解;同理IBinder.DeathRecipient目的也是一樣的,它註冊到ServiceManager那端,主要是服務死亡時斷開連接。

同時,CameraManagerGlobal可以任意調用ServiceManager類的方法,繼續看下面


ServiceManager模塊

ServiceManager模塊主要是上圖包含servicemanager關鍵字的類,IServiceManager接口定義了該模塊工作的能力,可以做哪些事情。

public interface IServiceManager
{
public IBinder getService(String name) throws RemoteException;
public IBinder checkService(String name) throws RemoteException;
public void addService(String name, IBinder service, boolean allowIsolated)
                throws RemoteException;
public String[] listServices() throws RemoteException;
}

該模塊如上代碼所示,可以進場查詢、添加和展示binder服務等工作,以上代碼只是部分哈,還有一些其他功能!實現這些功能主要有兩個類:ServiceManagerNative和ServiceManagerProxy;如前一篇文章Android Binder淺析 — CameraService往ServiceManager添加服務裏面講到的binder服務架構,在java層ServiceManagerNative更像binder服務架構裏面的BnServiceManager,隸屬於服務端真正實現的類,這裏並沒有用到它;而ServiceManagerProxy則是在客戶端的代理使用類BpServiceManager,通過ServiceManagerProxy向我們底層真正ServiceManager發起getService、addService等等

那ServiceManagerProxy是怎麼get/addService的呢?

 public IBinder getService(String name) throws RemoteException {
      Parcel data = Parcel.obtain();
      Parcel reply = Parcel.obtain();
      data.writeInterfaceToken(IServiceManager.descriptor);
      data.writeString(name);
      //mRemote類型爲IBinder
      mRemote.transact(GET_SERVICE_TRANSACTION, data, reply, 0); 
      IBinder binder = reply.readStrongBinder();
      reply.recycle();
      data.recycle();
      return binder;
}  

原因在於ServiceManagerProxy有一個IBinder成員mRemote,如getService方法就是把參數和方法封裝到Parcel,在調用mRemote的transact方法去跨進程IPC獲取我們的service,先透露一下,這個mRemote成員實質是Binder模塊的BinderProxy類,具體內部如何IPC跨進程,那就看看後面的Binder模塊吧


Binder模塊

同上,Binder主體也是上圖包含binder關鍵字的幾個類,IBinder也是該模塊的定義的能力接口;其子類Binder主要是用於他的繼承者,表明這是一個Binder服務的類,暫時用不到;BinderProxy主要重寫了transact,向Native層發起調用,與此同時BinderProxy包含許多native方法如transactNative,java向native發起調用工作;一般來說一個BinderProxy對應一個BpBinder(c++),也就是BinderProxy的mObject持有c++對象BpBinder的指針,如在這裏ServiceManager架構中,BinderProxy如下:

final class BinderProxy implements IBinder {
mObject就是持有底層ServiceManager服務,handle=0就是他的唯一標識
private long mObject = BpBinder(handle = 0)
}

以上mObject是查看源碼得知的,他是如何給mObject賦值的呢?,大致是這樣一個過程,從源碼這裏入口:

private static IServiceManager getIServiceManager() {
    if (sServiceManager != null) {
        return sServiceManager;
    }   
    // Find the service manager
    sServiceManager = ServiceManagerNative
            .asInterface(Binder.allowBlocking(BinderInternal.getContextObject()));
    return sServiceManager;
}  

以上代碼是java層ServiceManager中獲取底層C++的ServiceManager服務過程,其中關鍵代碼是BinderInternal.getContextObject()這句代碼

這是一個native方法
public static final native IBinder getContextObject();

繼續看他的Native方法,這個native方法採用動態註冊的方法,在jni_onLoad的時候把java的函數和native函數連接在一起,分析之,其native對應方法爲:

static jobject android_os_BinderInternal_getContextObject(JNIEnv* env, jobject clazz)
{
	返回的就是BpBinder(0),前文已經分析過
    sp<IBinder> b = ProcessState::self()->getContextObject(NULL);
    將b轉換成一個java對象,這個方法是重點
    return javaObjectForIBinder(env, b);
}

jobject javaObjectForIBinder(JNIEnv* env, const sp<IBinder>& val)
{
    if (val == NULL) return NULL;
	BpBinder返回false,不執行
    if (val->checkSubclass(&gBinderOffsets)) {
        // One of our own!
        jobject object = static_cast<JavaBBinder*>(val.get())->object();
        LOGDEATH("objectForBinder %p: it's our own %p!\n", val.get(), object);
        return object;
    }

    AutoMutex _l(mProxyLock);
    創建BinderProxy的java對象,gBinderProxyOffsets.mClass是BinderProxy的類和構造方法
    object = env->NewObject(gBinderProxyOffsets.mClass, gBinderProxyOffsets.mConstructor);
    if (object != NULL) {
        將BpBinder指針設置到mObject去
        env->SetLongField(object, gBinderProxyOffsets.mObject, (jlong)val.get());
        爲這個sp智能指針引用加1
        val->incStrong((void*)javaObjectForIBinder);
        返回BinderProxy java層的弱引用,同時爲他創建全局引用,native層持有這個所引用
        jobject refObject = env->NewGlobalRef(
                env->GetObjectField(object, gBinderProxyOffsets.mSelf));
        爲這個弱引用綁定一個代理清理函數proxy_cleanup        
        val->attachObject(&gBinderProxyOffsets, refObject,
                jnienv_to_javavm(env), proxy_cleanup);

        // Also remember the death recipients registered on this proxy
        sp<DeathRecipientList> drl = new DeathRecipientList;
        drl->incStrong((void*)javaObjectForIBinder);
        env->SetLongField(object, gBinderProxyOffsets.mOrgue, reinterpret_cast<jlong>(drl.get()));

        // Note that a new object reference has been created.
        android_atomic_inc(&gNumProxyRefs);
        incRefsCreated(env);
    }
	這個object也就是BinderProxy,其內部mObject=BpBinder(0)
    return object;
}
管理android.os.BinderProxy的結構
static struct binderproxy_offsets_t
{
    // Class state.
    jclass mClass;      android.os.BinderProxy的class
    jmethodID mConstructor;     構造方法
    jmethodID mSendDeathNotice; senddeathNotice函數

    // Object state.
    jfieldID mObject;
    jfieldID mSelf;
    jfieldID mOrgue;

} gBinderProxyOffsets;
const char* const kBinderProxyPathName = "android/os/BinderProxy";
static int int_register_android_os_BinderProxy(JNIEnv* env)
{
    jclass clazz = FindClassOrDie(env, kBinderProxyPathName); 獲取class
    因爲clazz是一個局部引用,不能全局使用,所以要爲它創建全局引用
    gBinderProxyOffsets.mClass = MakeGlobalRefOrDie(env, clazz);
    獲取BinderProxy的構造方法
    gBinderProxyOffsets.mConstructor = GetMethodIDOrDie(env, clazz, "<init>", "()V");
    gBinderProxyOffsets.mSendDeathNotice = GetStaticMethodIDOrDie(env, clazz, "sendDeathNotice",
            "(Landroid/os/IBinder$DeathRecipient;)V");
	獲取BinderProxy的long類型,名字爲mObject成員,以下同理
    gBinderProxyOffsets.mObject = GetFieldIDOrDie(env, clazz, "mObject", "J");
    gBinderProxyOffsets.mSelf = GetFieldIDOrDie(env, clazz, "mSelf",
                                                "Ljava/lang/ref/WeakReference;");
    gBinderProxyOffsets.mOrgue = GetFieldIDOrDie(env, clazz, "mOrgue", "J");

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

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

至此,BinderInternal.getContextObject分析完成!得到一個BinderProxy,其內部mObject=BpBinder(0);可以理解爲native層的BpBinder在java層的代理就是BinderProxy,而BinderProxy的mObject持有了native層BpBinder指針,最終調用BinderProxy可以操作底層的業務訪問!

插入一個java-native之間的互通模型:
主要解決兩者層之間快速的互通數據模型,連接初始化這些結構體的值參考上面的代碼:
在這裏插入圖片描述
小結這個架構設計,上面這種架構設計應該很常見,IXX接口定義該模塊的能力,XX實現該接口能力,XXManger持有IXX成員,並且提供一些訪問方法暴露給外部接口調用,這樣做的好處就是內外分離,職責明確,可替代性好,當模塊內部邏輯了,我們重現實現一個XXX類,在把他設置到XXManager中去就行,不需要改變其他代碼



Binder客戶端獲取CameraService的調用邏輯

以上只是談談了ServiceManager架構,再次我們進入本文主題,獲取CameraService;先看看我總結的調用圖:

在這裏插入圖片描述
圖片查看解釋:
灰色字體表示某個類,如CameraManagerGlobal是一個java的類,類下面豎直虛線表示該類的方法,方法之間的箭頭表明了他們的調用邏輯;紅色字體表示該大虛線左右兩側分屬不同的場景,如java到native兩個場景

從最外層CameraManager的openCamera調用開始,會調用其真正CameraManagerGlobal類發起連接相機設備服務,但是前提要先獲取服務端CameraService纔行,上圖就是從CameraManagerGlobal獲取CameraService的調用邏輯,部分源碼可以按照我上圖展示的邏輯去查看即可,這裏只闡述其中關鍵的一些代碼:


從IPC binder返回後的邏輯

binder跨進程IPC訪問,一般都是把結果寫到Parcel對象reply裏面去,這裏也不例外,從上面邏輯圖可知,當在IPCThreadState::transact的IPC調用執行完成後,退回到ServiceMangerProxy類時,會對reply結果進行處理,主要是對結果readStrongBinder,然後邏輯會走到Native層的Parcel對象中去,我們就從這裏開始:

 sp<IBinder> Parcel::readStrongBinder() const
{
    sp<IBinder> val; 
    // Note that a lot of code in Android reads binders by hand with this
    // method, and that code has historically been ok with getting nullptr
    // back (while ignoring error codes).
    readNullableStrongBinder(&val);
    這裏雖然返回的是sp局部變量,但是泛型IBinder在unflatten_binder是new出來的,不會被棧回收
    return返回會對sp拷貝,所以沒得問題
    return val; 
}
status_t Parcel::readNullableStrongBinder(sp<IBinder>* val) const
{
    return unflatten_binder(ProcessState::self(), *this, val);
}
binder實體都是寫到flat_binder_object對象的,所以要反序列化把他讀出來
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) {
          此類型說明返回的是binder實體,通常用於和服務端同屬一個進程
            case BINDER_TYPE_BINDER:
                *out = reinterpret_cast<IBinder*>(flat->cookie);
                return finish_unflatten_binder(NULL, *flat, in);
            返回的是handle句柄,跨進程會使用到,執行的是這裏    
            case BINDER_TYPE_HANDLE:
                flat.handle就是服務端的唯一標識句柄,getStrong方法就是返回了BpBinder
                *out = proc->getStrongProxyForHandle(flat->handle);
                return finish_unflatten_binder(
                    static_cast<BpBinder*>(out->get()), *flat, in);
        }
    }
    return BAD_TYPE;
}

不明白看代碼註釋,readStrongBinder返回後還會在android_os_Parcel類文件中調用javaObjectForBinder方法,主要就是將c的BpBinder指針轉換爲java層的BinderProxy對象,這個轉換過程本文上面已經講過,就不在闡述了;最後在CameraManagerGlobal中對返回的BinderProxy對象進行了一次轉換,調用ICameraService.Stub.asInterface方法,主要就是對BinderProxy在進行一次包裹,然後使用包裹後的CameraService調用方法時,會先將方法名、參數封裝到Parcel對象,然後在調用BinderProxy的transact進行跨進程方法,可以理解包裹類就是BpCameraService;
對我後面這層包裹不理解的,可以看看我這篇文章的aidl淺談,或者直接寫個aidl,查看他生成的類,裏面就有描述;



驅動層如何進程數據交換

從上面IPCThreadState的transact方法開始,和前文addService一樣,依次是客戶端進程把數據封裝爲binder_transaction_data結構,在封裝binder_write_read結構,內和空間會進行解封裝參數拷貝,最後封裝爲binder_transaction結構,並將其賦值到目標進程binder_proc結構體上,然後喚醒目標進程;

目標進程被喚醒後,從binder_proc的todo裏面取出待處理事項,解析binder_transaction結構體,依次對請求參數封裝爲binder_transaction_data和binder_write_read,最後返回到目標進程serviceManger的用戶空間binder_loop循環,對參數進程解析binder_parse;

是不是和前文addService一樣?
唯一不同的是方法code變成了GET_SERVICE,還有驅動層的數據交換函數binder_transaction執行邏輯稍許不同;
我們從binder_loop解析客戶端進程請求參數開始分析:

void binder_loop(struct binder_state *bs, binder_handler func)
{
struct binder_write_read bwr;
uint32_t readbuf[32];
for (;;) {
	bwr.read_size = sizeof(readbuf);
	bwr.read_consumed = 0;
	bwr.read_buffer = (uintptr_t) readbuf;
	
	res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
	
	if (res < 0) {
	    ALOGE("binder_loop: ioctl failed (%s)\n", strerror(errno));
	    break;
	}
	readbuf就是從內核空間返回的binder_write_read的讀取參數部分
	res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);
}
}

int binder_parse(struct binder_state *bs, struct binder_io *bio,
                 uintptr_t ptr, size_t size, binder_handler func)
{
    int r = 1;
    確定ptr的指針範圍
    uintptr_t end = ptr + (uintptr_t) size;
    while (ptr < end) {
    	讀取ptr前面4字節的數據,在內核空間給他賦值爲BR_TRANSACTION
		uint32_t cmd = *(uint32_t *) ptr;
        ptr += sizeof(uint32_t);
		case BR_TRANSACTION: {
			指針強轉爲binder_transaction_data
            struct binder_transaction_data *txn = (struct binder_transaction_data *) ptr;
            if ((end - ptr) < sizeof(*txn)) {
                ALOGE("parse: txn too small!\n");
                return -1;
            }
            判斷回調函數func是否爲空
            if (func) {
                unsigned rdata[256/4];
                struct binder_io msg;
                struct binder_io reply;
                int res;
				爲replay結構體分配內存,內存就是rdata
                bio_init(&reply, rdata, sizeof(rdata), 4);
                使用txn初始化msg
                bio_init_from_txn(&msg, txn);
                回調func函數
                res = func(bs, txn, &msg, &reply);
                if (txn->flags & TF_ONE_WAY) {
                    binder_free_buffer(bs, txn->data.ptr.buffer);
                } else {
                    binder_send_reply(bs, &reply, txn->data.ptr.buffer, res);
                }
            }
            ptr += sizeof(*txn);
            break;
        }
	}
}

以上就是對內和空間返回的binder_transaction_data轉換爲binder_io結構體並初始化;繼續看看回調函數func

int svcmgr_handler(struct binder_state *bs,
                   struct binder_transaction_data *txn,
                   struct binder_io *msg,
                   struct binder_io *reply)
{
GetService就是執行這裏
switch(txn->code) {
 	case SVC_MGR_GET_SERVICE:
    case SVC_MGR_CHECK_SERVICE:
    	取出來的s等於meida.camera
        s = bio_get_string16(msg, &len);
        if (s == NULL) {
            return -1;
        }
        do_find_service會遍歷查找服務列表,找到名字相同且長度一樣的服務,返回handle
        handle = do_find_service(s, len, txn->sender_euid, txn->sender_pid);
        if (!handle)
            break;
        把handle值寫入到reply結構體重
        bio_put_ref(reply, handle);
        return 0;

}
}

void bio_put_ref(struct binder_io *bio, uint32_t handle)
{
    struct flat_binder_object *obj;

    if (handle)
    	會從bio裏面去分配內存給obj
        obj = bio_alloc_obj(bio);
    else
        obj = bio_alloc(bio, sizeof(*obj));

    if (!obj)
        return;

    obj->flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
    注意這個類型爲handle
    obj->type = BINDER_TYPE_HANDLE;
    obj->handle = handle;
    obj->cookie = 0;
}

以上查找到服務後就將服務的handle值寫入到reply變量中,最後返回到binder_parse中去,執行binder_send_reply方法,看看:

void binder_send_reply(struct binder_state *bs,
                       struct binder_io *reply,
                       binder_uintptr_t buffer_to_free,
                       int status)
{
	data結構體依次爲cmd-buffer-cmd-buffer
    struct {
        uint32_t cmd_free;
        binder_uintptr_t buffer;
        uint32_t cmd_reply;
        struct binder_transaction_data txn;
    } __attribute__((packed)) data;
	以下是賦值,BC_FREE_BUFFER命令,在驅動層會對buffer_to_free釋放掉buffer_to_free內存
    data.cmd_free = BC_FREE_BUFFER;
    data.buffer = buffer_to_free;
    BC_REPLY命令則會對txn數據發送出去
    data.cmd_reply = BC_REPLY;
    data.txn.target.ptr = 0;
    data.txn.cookie = 0;
    data.txn.code = 0;
    status爲0
    if (status) {
        data.txn.flags = TF_STATUS_CODE;
        data.txn.data_size = sizeof(int);
        data.txn.offsets_size = 0;
        data.txn.data.ptr.buffer = (uintptr_t)&status;
        data.txn.data.ptr.offsets = 0;
    } else {
    執行else
        data.txn.flags = 0;
        數據長度
        data.txn.data_size = reply->data - reply->data0;
        數據偏移長度
        data.txn.offsets_size = ((char*) reply->offs) - ((char*) reply->offs0);
        存放的數據起始地址,這是一個flat_binder_object結構體類型
        data.txn.data.ptr.buffer = (uintptr_t)reply->data0;
        偏移起始地址
        data.txn.data.ptr.offsets = (uintptr_t)reply->offs0;
    }
    進入內核寫數據
    binder_write(bs, &data, sizeof(data));
}

內核寫數據開始:

static int binder_thread_write(struct binder_proc *proc,
                        struct binder_thread *thread,
                        binder_uintptr_t binder_buffer, size_t size,
                        binder_size_t *consumed)
{
	uint32_t cmd; 
	struct binder_context *context = proc->context;
	void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
	void __user *ptr = buffer + *consumed;
	void __user *end = buffer + size;	
	while (ptr < end && thread->return_error == BR_OK) {
		會從數據中取出命令在case條件執行,上面傳入了兩個命令,一個BC_FREE_BUFFER
		命令,一個BC_REPLY;第一個命令就不分析了,主要看第二個命令
		if (get_user(cmd, (uint32_t __user *)ptr))
                        return -EFAULT;
                ptr += sizeof(uint32_t);
         case BC_TRANSACTION:
         case BC_REPLY: {
                 struct binder_transaction_data tr;
				拷貝參數
                 if (copy_from_user(&tr, ptr, sizeof(tr)))
                         return -EFAULT;
                 ptr += sizeof(tr);
                 數據轉換爲binder_transaction並交換
                 binder_transaction(proc, thread, &tr,
                                    cmd == BC_REPLY, 0);
                 break;
         }        
	}
}

static void binder_transaction(struct binder_proc *proc,
                               struct binder_thread *thread,
                               struct binder_transaction_data *tr, int reply,
                               binder_size_t extra_buffers_size)
{
	int ret;
       struct binder_transaction *t;
       struct binder_work *tcomplete;
       binder_size_t *offp, *off_end, *off_start;
       binder_size_t off_min;
       u8 *sg_bufp, *sg_buf_end;
       struct binder_proc *target_proc;
       struct binder_thread *target_thread = NULL;
       struct binder_node *target_node = NULL;
       struct list_head *target_list;
       wait_queue_head_t *target_wait;
       struct binder_transaction *in_reply_to = NULL;
       struct binder_context *context = proc->context;

       if (reply) {
       		   thread爲servicemanger的進程線程,transaction_stack是客戶端之前的結構體
               in_reply_to = thread->transaction_stack;
               thread->transaction_stack = in_reply_to->to_parent;
               獲取到了我們客戶端進程的線程
               target_thread = in_reply_to->from;
          	   獲取到了我們客戶端進程的進程結構體binder_proc
               target_proc = target_thread->proc;
       } else {
       }
       if (target_thread) {
       		獲取目標線程,也就是客戶端線程的任務列表,中斷參數
               e->to_thread = target_thread->pid;
               target_list = &target_thread->todo;
               target_wait = &target_thread->wait;
       } else {
       }
      直接從內核空間分配t結構體,t是內核空間進程間傳遞的參數結構
       t = kzalloc(sizeof(*t), GFP_KERNEL);
	   同上,只是tcomplete會被用到當前進程用於處理收尾工作
       tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL);
      爲t賦值
       t->sender_euid = task_euid(proc->tsk);
       t->to_proc = target_proc;
       t->to_thread = target_thread;
       t->code = tr->code;
       t->flags = tr->flags;
       t->priority = task_nice(current);
		從目標進程也就是客戶端進程分配binder_buffer節點,binder_buffer管理了內核映射內存節點
		和進程空間部分內存一一對應
       t->buffer = binder_alloc_buf(target_proc, tr->data_size,
               tr->offsets_size, extra_buffers_size,
               !reply && (t->flags & TF_ONE_WAY));
     	置buffer節點已經被使用,不能給別人使用了
       t->buffer->allow_user_free = 0;
       t->buffer->debug_id = t->debug_id;
       t->buffer->transaction = t;
       t->buffer->target_node = target_node;
       增加引用計數
       if (target_node)
               binder_inc_node(target_node, 1, 0, NULL);
		計算buffer中存放數據的起始地址
       off_start = (binder_size_t *)(t->buffer->data +
                                     ALIGN(tr->data_size, sizeof(void *)));
       offp = off_start;
		將數據從tr中buffer數據拷貝到buffer中去
       if (copy_from_user(t->buffer->data, (const void __user *)(uintptr_t)
                          tr->data.ptr.buffer, tr->data_size)) {
               binder_user_error("%d:%d got transaction with invalid data ptr\n",
                               proc->pid, thread->pid);
               return_error = BR_FAILED_REPLY;
               goto err_copy_data_failed;
       }
       將數據從tr中偏移數據拷貝到buffer中去,也就是tr中的buffer和offsets都存放到
       buffer中同一個位置,offsets在前,buffer在後
       if (copy_from_user(offp, (const void __user *)(uintptr_t)
                          tr->data.ptr.offsets, tr->offsets_size)) {
               binder_user_error("%d:%d got transaction with invalid offsets ptr\n",
                               proc->pid, thread->pid);
               return_error = BR_FAILED_REPLY;
               goto err_copy_data_failed;
       }
       
       off_end = (void *)off_start + tr->offsets_size;
       for (; offp < off_end; offp++) {
               struct binder_object_header *hdr;
               size_t object_size = binder_validate_object(t->buffer, *offp);

               //這個hdr讀取的實質是flat_binder_object裏面的type
               hdr = (struct binder_object_header *)(t->buffer->data + *offp);
               off_min = *offp + object_size;
               switch (hdr->type) {
               我們在bio_put_ref函數中設置了這個type爲BINDER_TYPE_HANDLE
               case BINDER_TYPE_HANDLE:
               case BINDER_TYPE_WEAK_HANDLE: {
                      struct flat_binder_object *fp;
                      獲取fp指針,fp實質也是上面bio_put_ref進去的那些值
                       fp = to_flat_binder_object(hdr);
                       根據fp進行對象轉換,這個函數很重要,看後面代碼
                       ret = binder_translate_handle(fp, t, thread);
                       if (ret < 0) {
                               return_error = BR_FAILED_REPLY;
                               goto err_translate_failed;
                       }
               } break;

       }
       if (reply) {
               BUG_ON(t->buffer->async_transaction != 0);
               將in_reply_to從目標線程中彈出
               binder_pop_transaction(target_thread, in_reply_to);
       } else if (!(t->flags & TF_ONE_WAY)) {
       } else {
       }
       將事務添加到客戶端進程上去
       t->work.type = BINDER_WORK_TRANSACTION;
       list_add_tail(&t->work.entry, target_list);
       將事務添加到servicemanager進程上去
       tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
       list_add_tail(&tcomplete->entry, &thread->todo);
       喚醒客戶端進程
       if (target_wait)
               wake_up_interruptible(target_wait);
       return;
}

static int binder_translate_handle(struct flat_binder_object *fp,
                                   struct binder_transaction *t,
                                   struct binder_thread *thread)
{
        struct binder_ref *ref;
        struct binder_proc *proc = thread->proc;
        struct binder_proc *target_proc = t->to_proc;
		根據handle值,從當前binder_proc結構體對binder_node引用樹去搜尋對應的引用節點;
		還記得addService時,爲服務創建binder_node節點,並讓serviceManager進程的binder_proc持有
		這個binder_node引用,並且把他返回的描述desc賦值給handle;所以這裏是反向操作,根據handle去
		查找節點引用
        ref = binder_get_ref(proc, fp->handle,
                             fp->hdr.type == BINDER_TYPE_HANDLE);
		如果這個節點的proc和目標進程相同,說明客戶端和服務端同屬一個進程
        if (ref->node->proc == target_proc) {
                if (fp->hdr.type == BINDER_TYPE_HANDLE)
                        fp->hdr.type = BINDER_TYPE_BINDER;
                else
                        fp->hdr.type = BINDER_TYPE_WEAK_BINDER;
                就會存儲binder服務的指針地址,同一個進程直接通過指針調用        
                fp->binder = ref->node->ptr;
                fp->cookie = ref->node->cookie;
                binder_inc_node(ref->node, fp->hdr.type == BINDER_TYPE_BINDER,
                                0, NULL);
                trace_binder_transaction_ref_to_node(t, ref);
                binder_debug(BINDER_DEBUG_TRANSACTION,
                             "        ref %d desc %d -> node %d u%016llx\n",
                             ref->debug_id, ref->desc, ref->node->debug_id,
                             (u64)ref->node->ptr);
        } else {
        		不屬於同一個進程,就要通過handle句柄來調用
                struct binder_ref *new_ref;
				客戶端創建一個binder_ref引用,連接到服務端也就是CameraService的binder_node節點;
				後續我們在客戶端請求服務端時,可以根據這個引用關係,在內核空間將參數拷貝賦值到服務端進程
                new_ref = binder_get_ref_for_node(target_proc, ref->node);
                if (!new_ref)
                        return -EINVAL;
				將引用賦值給fp返回
                fp->binder = 0;
                fp->handle = new_ref->desc;
                fp->cookie = 0;
                binder_inc_ref(new_ref, fp->hdr.type == BINDER_TYPE_HANDLE,
                               NULL);
        }
        return 0;
}

以上代碼主要是從serviceManager進程查找到服務後,將服務handle句柄封裝到binder_io結構,在轉化爲binder_transaction_data結構體,在binder_translate_handle函數中,使用handle查找到binder_proc的服務binder_node節點,這個節點是真正的CameraService服務在驅動層對應的節點,該節點存放了CameraService的指針以及描述handle,拿到這個節點後,爲我們客戶端進程創建一個引用binder_ref指向這個節點,後續方法調用就可以利用這個引用找到CameraService驅動層的節點,併爲服務進程添加處理方法事項,就可以跨進程訪問了;客戶端拿到服務端CameraService服務後,如何調用服務端方法也是在這個binder_transaction函數中,如下代碼:

此時target.handle屬於CameraService的handle句柄,而且不等於0,所以執行這個條件
if (tr->target.handle) {
        struct binder_ref *ref;
		從本進程proc中找到這個handle的引用,也就是上面binder_translate_handle中先前創建愛你
         ref = binder_get_ref(proc, tr->target.handle, true);
         if (ref == NULL) {
                 binder_user_error("%d:%d got transaction to invalid handle\n",
                         proc->pid, thread->pid);
                 return_error = BR_FAILED_REPLY;
                 goto err_invalid_target_handle;
         }
         ref.node就是CameraService在驅動層的binder_node節點,後續就是往這個target_node上
         做事情
         target_node = ref->node;
 } else {
         target_node = context->binder_context_mgr_node;
         if (target_node == NULL) {
                 return_error = BR_DEAD_REPLY;
                 goto err_no_context_mgr_node;
         }
 }

上面執行完後,客戶端進程被喚醒,在驅動程序中拿到binder_transaction數據,取出裏面的值,最後回到IPCThreadState中根據BC_REPLY命令解析參數,然後退出IPCThreadState類,就返回到應用層了,就可以接上本文前明的邏輯了;



總結

Android的binder通信博大精深,博主也是瞭解了一些粗淺的原理!binder通信依靠底層的內存映射技術,同一塊物理內存同時映射兩塊虛擬空間-內核空間與用戶空間,並且內核空間與用戶空間存在簡單的對應關係,當內核空間內存發生變化,與他對應的用戶空間內存也發生相應的變化,進程間數據傳遞時,減少內存拷貝次數!binder驅動創建了許多結構體來維護這些映射的內存節點,以及維護進程間數據傳遞的結構體,牢記這些結構體的意義以及用法將幫助我們快速理解binder通信原理!

更多精彩博文,加入我們,一同進步!

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章