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通信原理!
更多精彩博文,加入我們,一同進步!