摘要:本節主要來講解Android10.0 Binder中守護進程ServiceManager是如何啓動、註冊、獲取服務
閱讀本文大約需要花費35分鐘。
文章首發微信公衆號:IngresGe
專注於Android系統級源碼分析,Android的平臺設計,歡迎關注我,謝謝!
[Android取經之路] 的源碼都基於Android-Q(10.0) 進行分析
[Android取經之路] 系列文章:
《系統啓動篇》
- Android系統架構
- Android是怎麼啓動的
- Android 10.0系統啓動之init進程
- Android10.0系統啓動之Zygote進程
- Android 10.0 系統啓動之SystemServer進程
- Android 10.0 系統服務之ActivityMnagerService
- Android10.0系統啓動之Launcher(桌面)啓動流程
- Android10.0應用進程創建過程以及Zygote的fork流程
- Android 10.0 PackageManagerService(一)工作原理及啓動流程
- Android 10.0 PackageManagerService(二)權限掃描
- Android 10.0 PackageManagerService(三)APK掃描
- Android 10.0 PackageManagerService(四)APK安裝流程
《日誌系統篇》
- Android10.0 日誌系統分析(一)-logd、logcat 指令說明、分類和屬性
- Android10.0 日誌系統分析(二)-logd、logcat架構分析及日誌系統初始化
- Android10.0 日誌系統分析(三)-logd、logcat讀寫日誌源碼分析
- Android10.0 日誌系統分析(四)-selinux、kernel日誌在logd中的實現
《Binder通信原理》
- Android10.0 Binder通信原理(一)Binder、HwBinder、VndBinder概要
- Android10.0 Binder通信原理(二)-Binder入門篇
- Android10.0 Binder通信原理(三)-ServiceManager篇
- Android10.0 Binder通信原理(四)-Native-C\C++實例分析
- Android10.0 Binder通信原理(五)-Binder驅動分析
- Android10.0 Binder通信原理(六)-Binder數據如何完成定向打擊
- Android10.0 Binder通信原理(七)-Framework binder示例
- Android10.0 Binder通信原理(八)-Framework層分析
- Android10.0 Binder通信原理(九)-AIDL Binder示例
- Android10.0 Binder通信原理(十)-AIDL原理分析-Proxy-Stub設計模式
- Android10.0 Binder通信原理(十一)-Binder總結
1.概述
在Android中,系統提供的服務被包裝成一個個系統級service,這些service往往會在設備啓動之時添加進Android系統。當我們某個應用想要調用系統某個服務的功能時,往往是向系統發出請求,調用該服務的外部接口。在上一節我們也瞭解到,這種外部接口,我們通常稱爲代理接口,也就是我們要拿到目標服務對應的代理對象。
但是Android中有很多個服務,如果都是直接向目標服務拿對象也不太現實,開銷會很大。因此在Android中,引入了一個ServiceManager的系統級服務--也可以稱之爲Binder的守護進程來管理這些服務對象。
上一節我們大體的瞭解了Binder的設計思路,瞭解了Binder機制的四要素:Client、Server、ServiceManager、Binder驅動。其中ServiceManager主要用來管理Server的名稱和對象。
ServiceManager本身的工作很簡單:註冊服務、查詢服務、列出所有服務,啓動一個死循環來解析Binder驅動讀寫動作,進行事務處理。
2.Binder架構
Binder通信流程如下:
1)首先服務端需要向ServiceManager進行服務註冊,ServiceManager有一個全局的service列表svcinfo,用來緩存所有服務的handler和name。
2)客戶端與服務端通信,需要拿到服務端的對象,由於進程隔離,客戶端拿到的其實是服務端的代理,也可以理解爲引用。客戶端通過ServiceManager從svcinfo中查找服務,ServiceManager返回服務的代理。
3)拿到服務對象後,我們需要向服務發送請求,實現我們需要的功能。通過 BinderProxy 將我們的請求參數發送給 內核,通過共享內存的方式使用內核方法 copy_from_user() 將我們的參數先拷貝到內核空間,這時我們的客戶端進入等待狀態。然後 Binder 驅動向服務端的 todo 隊列裏面插入一條事務,執行完之後把執行結果通過 copy_to_user() 將內核的結果拷貝到用戶空間(這裏只是執行了拷貝命令,並沒有拷貝數據,binder只進行一次拷貝),喚醒等待的客戶端並把結果響應回來,這樣就完成了一次通訊。
3.servicemanager的啓動
init進程啓動時,加載servicemanager.rc,啓動serviecmanager:
servicemanager.rc 文件如下:
/frameworks/native/cmds/servicemanager/servicemanager.rc
service servicemanager /system/bin/servicemanager
class core animation
user system
group system readproc
critical
...
從上面的rc文件可知道,servicemanager編譯後,位於手機的/system/bin中,這是一個Native C/C++的進程,C/C++的進程都會有一個main()的入口,順藤摸瓜,我們找到了servicemanager的入口:service_manager.c 的main()。
4.service_manager調用棧:
我們在看源碼或者調試死機的時候,最喜歡看的就是上下文的調用關係,在此,我列出了service_manager.c 的調用棧信息,暫時先忽略細節部分
5. 源碼分析
servicemanager的整體流程和 service管理的結構我們都有了一個整體認識,那麼細節部分就得靠看源碼來深入理解了。
5.1 主程序啓動main()
前面我們知道,servicemanager 是一個Native C/C++的程序,那麼會存在一個main()的入口,代碼如下:
int main(int argc, char** argv)
{
struct binder_state *bs;
union selinux_callback cb;
char *driver;
//0. servicemanager 和vndservicemanager 共享一套代碼,vndservicemanager傳入參數"/dev/vndbinder"
if (argc > 1) {
driver = argv[1]; //vndservicemanager 的driver爲 "/dev/vndbinder"
} else {
driver = "/dev/binder"; //servicemanager 訪問的driver爲"/dev/binder"
}
//1.通過系統調用 open "/dev/binder", mmap 申請一塊128K的內存空間
bs = binder_open(driver, 128*1024);
if (!bs) {
#ifdef VENDORSERVICEMANAGER
ALOGW("failed to open binder driver %s\n", driver);
while (true) {
sleep(UINT_MAX); //如果是"/dev/vndbinder" 打開失敗,陷入死循環繼續等待
}
#else
ALOGE("failed to open binder driver %s\n", driver); //如果是"/dev/binder" 打開失敗,直接跳出
#endif
return -1;
}
//3.註冊servicemanager爲binder 機制守護進程 ,其實就是把0號的handler給servicemanager使用,以後只要訪問0號的handler,binder驅動就知道是與servicemanager進行交互
if (binder_become_context_manager(bs)) {
ALOGE("cannot become context manager (%s)\n", strerror(errno));
return -1;
}
cb.func_audit = audit_callback;
selinux_set_callback(SELINUX_CB_AUDIT, cb);
#ifdef VENDORSERVICEMANAGER
cb.func_log = selinux_vendor_log_callback;
#else
cb.func_log = selinux_log_callback;
#endif
selinux_set_callback(SELINUX_CB_LOG, cb); //註冊selinux的callback操作
#ifdef VENDORSERVICEMANAGER
sehandle = selinux_android_vendor_service_context_handle();
#else
sehandle = selinux_android_service_context_handle();
#endif
selinux_status_open(true); //啓動selinux的檢查
if (sehandle == NULL) {
ALOGE("SELinux: Failed to acquire sehandle. Aborting.\n");
abort();
}
//如果selinux檢查不過,則不允許進行binder的操作
if (getcon(&service_manager_context) != 0) {
ALOGE("SELinux: Failed to acquire service_manager context. Aborting.\n");
abort();
}
//4. 啓動loop循環機制,等待"/dev/binder" 發來讀寫信息,解析這些內容,執行相應的操作,例如:註冊服務、獲取服務、列出服務信息
binder_loop(bs, svcmgr_handler);
return 0;
}
在Android8.0後,谷歌引入Treble機制,binder機制增加了hwbinder和vndbinder,其中vndbinder的守護進程爲vndservicemanager。
vndservicemanager和service共用同一份代碼,只是傳入的參數和宏控制的流程有部分差異。
vndservicemanager會傳入參數"/dev/vndbinder", servicemanager使用默認的"/dev/binder".
servicemanager主要做了以下幾件事:
1.打開binder驅動,申請了128K的內存空間
2.然後調用binder_become_context_manager()讓自己成爲整個系統中唯一的上下文管理器,其實也就是service管理器
3.調用binder_loop()進入無限循環,不斷監聽並解析binder驅動發來的命令。
4.binder_loop()通過binder_parse()進行命令解析,然後調用回調函數svcmgr_handler()進行處理,例如:註冊服務、獲取服務、列出服務信息
下面我們按步來看看各個回合工作的細節部分。
5.2 binder_open()
servicemanager啓動後,先通過binder_open()來打開"/dev/binder", 代碼如下:
struct binder_state *binder_open(const char* driver, size_t mapsize)
{
struct binder_state *bs;
struct binder_version vers;
bs = malloc(sizeof(*bs));
...
//1.打開Binder設備驅動,陷入內核
bs->fd = open(driver, O_RDWR | O_CLOEXEC);
...
//2.獲取Binder的版本信息,比較協議版本是否相同,不同則跳出
if ((ioctl(bs->fd, BINDER_VERSION, &vers) == -1) ||
(vers.protocol_version != BINDER_CURRENT_PROTOCOL_VERSION))
...
}
bs->mapsize = mapsize;
//3.mmap內存映射128K內存空間,mmap必須是page的整數倍
bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
...
return bs;
...
}
binder_open()的工作也比較簡單,分爲以下幾步:
1.通過系統調用open()來打開"/dev/binder",獲得一個句柄信息,在Binder驅動中對應的是函數binder_open()
2.通過ioctl 獲取binder的版本信息,比較binder協議版本是否相同,不同則跳出,在Binder驅動中對應的是函數binder_ioctl()
3.通過mmap內存映射128K的內存空間,即把binder驅動文件的128K字節映射到了內存空間,這128K內存空間爲servicemanager使用。在Binder驅動中對應的是函數binder_mmap()。
其他的binder服務進程會映射 BINDER_VM_SIZE ((1 * 1024 * 1024) - sysconf(_SC_PAGE_SIZE) * 2) 的內存空間,_SC_PAGE_SIZE表示一個page頁的大小,通常情況下爲4K,即(1M - 4K*2)=(1M-7K)
這個page的大小,不同廠家有時候也會調整大小,一般有1M,64K,4K,1KB,通常爲4K。
ServiceManager 進程mmap的內存大小可以通過adb shell命令得出:
其中 0x7457d61000 - 0x7457d41000 = 0x20000, 轉成10進制,即爲128K
ARM32內存映射:
虛擬空間的低3GB部分從0-0XBFFFFFFF的虛擬線性地址,用戶態和內核態都可以尋址,這部分也是每個進程的獨立空間。
虛擬空間的高1G部分從0XC0000000到0XFFFFFFFF的虛擬地址,只有內核態的進程才能訪問,這種限制由頁目錄和頁表描述符的權限標誌位決定,通過MMU自動控制。
虛擬地址空間爲4G,0-3G是用戶地址空間(用戶態和內核態都可以尋址,這也是每個進行的獨立空間),3G-4G內核地址空間,每個進程都是擁有4G地址空間,只是用戶態下面無法訪問高1G空間;內核空間是被所有進程共享的
ARM64內存映射:
默認情況下,32位系統默認只能支持4G的內存,在打開PAE後,最大可以擴展到64G的內存。隨着物理硬件的不斷升級,現在的內存越來越大,因此基本上都切換到了64位系統。
理論上講,64位的地址總線可以支持高達16EB(2^64)的內存。
2^64 次方太大了,Linux 內核只採用了 64 bits 的一部分(開啓 CONFIG_ARM64_64K_PAGES 時使用 42 bits,頁大小是 4K 時使用 39 bits),該文假設使用的頁大小是 4K(VA_BITS = 39)
ARM64 有足夠的虛擬地址,用戶空間和內核空間可以有各自的 2^39 = 512GB 的虛擬地址。
需要注意到,32 位應用仍然擁有 512GB 的內核虛擬地址空間,並且不與內核共享自己的 4GB 空間。但在 ARM32 上,32 位應用只有 3GB 的地址空間。
ARM32和ARM64內存地址比較:
ARM32 |
ARM64 | |
32位應用虛擬地址空間 |
3G |
4G |
64位應用虛擬地址空間 |
不支持 |
512G |
內核虛擬空間 |
1G |
512G |
5.3 binder_become_context_manager()
binder_become_context_manager()的作用是讓ServiceManager成爲整個系統中唯一的上下文管理器,其實也就是service管理器,這樣我們就可以把ServiceManager稱之爲守護進程。
int binder_become_context_manager(struct binder_state *bs)
{
struct flat_binder_object obj;
memset(&obj, 0, sizeof(obj));
obj.flags = FLAT_BINDER_FLAG_TXN_SECURITY_CTX;
//Android10.0 中引入BINDER_SET_CONTEXT_MGR_EXT,用來把ServiecManager設置成爲安全的上下文,
int result = ioctl(bs->fd, BINDER_SET_CONTEXT_MGR_EXT, &obj);
// fallback to original method
if (result != 0) {
android_errorWriteLog(0x534e4554, "121035042");
//如果安全上下文設置失敗,繼續使用原有的BINDER_SET_CONTEXT_MGR來進行控制
result = ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
}
return result;
}
對應的binder驅動中操作如下:
從用戶空間拷貝ioctl的參數,調用binder_ioctl_set_ctx_mgr()進行設置
BINDER_SET_CONTEXT_MGR_EXT帶參數,BINDER_SET_CONTEXT_MGR不帶參數
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
...
switch (cmd) {
case BINDER_SET_CONTEXT_MGR_EXT: {
struct flat_binder_object fbo;
if (copy_from_user(&fbo, ubuf, sizeof(fbo))) {
ret = -EINVAL;
goto err;
}
ret = binder_ioctl_set_ctx_mgr(filp, &fbo);
if (ret)
goto err;
break;
}
case BINDER_SET_CONTEXT_MGR:
ret = binder_ioctl_set_ctx_mgr(filp, NULL);
if (ret)
goto err;
break;
}
...
}
static int binder_ioctl_set_ctx_mgr(struct file *filp,
struct flat_binder_object *fbo)
{
int ret = 0;
//進程的binder_proc, 這裏是ServiceManager的 binder_proc,之前通過open("/dev/binder")得來
struct binder_proc *proc = filp->private_data;
struct binder_context *context = proc->context;
struct binder_node *new_node;
// 線程的uid
kuid_t curr_euid = current_euid();
//互斥鎖
mutex_lock(&context->context_mgr_node_lock);
//正常第一次爲null,如果不爲null則說明該進程已經設置過context mgr則直接退出
if (context->binder_context_mgr_node) {
pr_err("BINDER_SET_CONTEXT_MGR already set\n");
ret = -EBUSY;
goto out;
}
//檢查當前進程是否具有註冊Context Manager的SEAndroid安全權限
ret = security_binder_set_context_mgr(proc->tsk);
if (ret < 0)
goto out;
if (uid_valid(context->binder_context_mgr_uid)) {
//讀取binder_context_mgr_uid和當前的比,如果不一樣,報錯。
if (!uid_eq(context->binder_context_mgr_uid, curr_euid)) {
pr_err("BINDER_SET_CONTEXT_MGR bad uid %d != %d\n",
from_kuid(&init_user_ns, curr_euid),
from_kuid(&init_user_ns,
context->binder_context_mgr_uid));
ret = -EPERM;
goto out;
}
} else {
//將當前進程的uid賦值給context的binder_context_mgr_uid變量以作保存
context->binder_context_mgr_uid = curr_euid;
}
// 創建binder_node對象
new_node = binder_new_node(proc, fbo);
if (!new_node) {
ret = -ENOMEM;
goto out;
}
binder_node_lock(new_node);
// 增加強弱引用計數
new_node->local_weak_refs++;
new_node->local_strong_refs++;
new_node->has_strong_ref = 1;
new_node->has_weak_ref = 1;
context->binder_context_mgr_node = new_node; //把新創建的node對象賦值給context->binder_context_mgr_node,成爲serviceManager的binder管理實體
binder_node_unlock(new_node);
binder_put_node(new_node);
out:
//釋放鎖
mutex_unlock(&context->context_mgr_node_lock);
return ret;
}
binder_ioctl_set_ctx_mgr()的流程也比較簡單
1.先檢查當前進程是否具有註冊Context Manager的SEAndroid安全權限
2.如果具有SELinux權限,會爲整個系統的上下文管理器專門生成一個binder_node節點,使該節點的強弱應用加1
3.新創建的binder_node 節點,記入context->binder_context_mgr_node,即ServiceManager 進程的context binder節點,使之成爲serviceManager的binder管理實體
5.4 binder_loop()
下一步進行守護進程的循環處理,binder_loop()會先向binder驅動發出了BC_ENTER_LOOPER命令,告訴binder驅動"本線程要進入循環狀態了",接着進入一個for循環不斷調用ioctl()讀取發來的數據,接着解析這些數據
[frameworks/native/cmds/servicemanager/service_manager.c]
void binder_loop(struct binder_state *bs, binder_handler func)
{
int res;
struct binder_write_read bwr; //binder 讀寫結構,記錄buffer中讀和寫的數據信息
uint32_t readbuf[32]; //準備128= 32*4字節的讀取buffer
bwr.write_size = 0;
bwr.write_consumed = 0;
bwr.write_buffer = 0;
readbuf[0] = BC_ENTER_LOOPER;
// 向binder驅動發送命令協議BC_ENTER_LOOPER,告訴binder驅動"本線程要進入循環狀態了".
//binder_write 中把 binder_write_read的read的信息置空,這樣在binder_ioctl中,只會處理write的流程
binder_write(bs, readbuf, sizeof(uint32_t));
for (;;) {
bwr.read_size = sizeof(readbuf);
bwr.read_consumed = 0;
bwr.read_buffer = (uintptr_t) readbuf;
res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr); //進入循環,不斷地binder讀寫過程
if (res < 0) {
ALOGE("binder_loop: ioctl failed (%s)\n", strerror(errno));
break;
}
// 解析binder信息,傳入的回調func 函數爲 svcmgr_handler()
res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);
if (res == 0) {
ALOGE("binder_loop: unexpected reply?!\n");
break;
}
if (res < 0) {
ALOGE("binder_loop: io error %d %s\n", res, strerror(errno));
break;
}
}
}
其中最重要的一個結構體是binder_write_read,它用來記錄Binder buffer中讀和寫的數據信息結構體如下:
struct binder_write_read {
binder_size_t write_size; //要寫入的字節數,write_buffer的總字節數
binder_size_t write_consumed; //驅動程序佔用的字節數,write_buffer已消費的字節數
binder_uintptr_t write_buffer; //寫緩衝數據的指針
binder_size_t read_size; //要讀的字節數,read_buffer的總字節數
binder_size_t read_consumed; //驅動程序佔用的字節數,read_buffer已消費的字節數
binder_uintptr_t read_buffer; //讀緩存數據的指針
};
5.5 binder_parse()
在binder_loop()進入for循環之後,核心處理流程就是ioctl和binder_parse(), 即不停的從Binder驅動接收讀寫數據,進行binder解析後,進行處理.
[frameworks/native/cmds/servicemanager/binder.c]
int binder_parse(struct binder_state *bs, struct binder_io *bio,
uintptr_t ptr, size_t size, binder_handler func)
{
int r = 1;
uintptr_t end = ptr + (uintptr_t) size;
//每個命令中可能包含多個BR碼,因此需要用while循環來解析buffer中所有的BR_XX碼
while (ptr < end) {
uint32_t cmd = *(uint32_t *) ptr; //從ptr指向的地址讀取cmd(協議)命令
ptr += sizeof(uint32_t); //cmd佔用 readbuf的4個字節
#if TRACE
fprintf(stderr,"%s:\n", cmd_name(cmd));
#endif
switch(cmd) {
case BR_NOOP:
break;
case BR_TRANSACTION_COMPLETE:
break;
case BR_INCREFS:
case BR_ACQUIRE:
case BR_RELEASE:
case BR_DECREFS:
#if TRACE
fprintf(stderr," %p, %p\n", (void *)ptr, (void *)(ptr + sizeof(void *)));
#endif
ptr += sizeof(struct binder_ptr_cookie);
break;
case BR_TRANSACTION_SEC_CTX:
case BR_TRANSACTION: {
struct binder_transaction_data_secctx txn;
...
binder_dump_txn(&txn.transaction_data);
if (func) {
...
res = func(bs, &txn, &msg, &reply);
...
}
case BR_REPLY: {
struct binder_transaction_data *txn = (struct binder_transaction_data *) ptr;
if ((end - ptr) < sizeof(*txn)) {
ALOGE("parse: reply too small!\n");
return -1;
}
binder_dump_txn(txn);
if (bio) {
bio_init_from_txn(bio, txn);
bio = 0;
} else {
/* todo FREE BUFFER */
}
ptr += sizeof(*txn);
r = 0;
break;
}
case BR_DEAD_BINDER: {
//獲取binder_death數據
struct binder_death *death = (struct binder_death *)(uintptr_t) *(binder_uintptr_t *)ptr;
ptr += sizeof(binder_uintptr_t);
death->func(bs, death->ptr);
break;
}
case BR_FAILED_REPLY:
r = -1;
break;
case BR_DEAD_REPLY:
r = -1;
break;
default:
ALOGE("parse: OOPS %d\n", cmd);
return -1;
}
}
return r;
}
在binder_loop()中聲明瞭一個128字節的棧內存-readbuf, 用BINDER_WRITE_READ命令從驅動讀取一些內容,並傳入binder_parse(),binder_parse()根據binder驅動傳來的 "BR_XXX"協議碼,進行相關的邏輯處理,最主要的有三個"BR_XXX"協議:
BR_TRANSACTION : 事務處理,解析binder_transaction_data的數據,調用回調函數svcmgr_handler() 進行服務的註冊、獲取等操作
BR_REPLY : 消息回覆
BR_DEAD_BINDER : 死亡通知
只要binder_parse()解析正常,binder_loop()就會一直執行下去,ServiceManager進程不退出。
binder_parse()解析binder驅動傳來的readbuf的內存,readbuf擁有128字節的棧內存,每次可以只處理一個cmd,也可以有多個cmd,所以存在一個while循環,可以同時解析多個cmd,多個cmd的結構如下圖所示:
5.5.1 BR_XXX 協議碼分析
BR_XXX碼,也成爲Binder響應碼,這裏介紹了ServiceManager處理的一些響應碼的作用:
5.5.2 BR_TRANSACTION 解析
我們這裏單獨分析下 BR_TRANSACTION 的流程,這也是我們常用的一個流程,這是Binder驅動向Server端發送請求數據。
從readbuf中解析出binder_transaction_data的數據,最後對接收和發送數據進行了封裝,傳遞給svcmgr_handler()做詳細處理
int binder_parse(struct binder_state *bs, struct binder_io *bio,
uintptr_t ptr, size_t size, binder_handler func)
{
case BR_TRANSACTION_SEC_CTX:
case BR_TRANSACTION: {
//binder_transaction_data_secctx 比 binder_transaction_data 多一個 secctx成員,主要用來指向安全上下文
struct binder_transaction_data_secctx txn;
if (cmd == BR_TRANSACTION_SEC_CTX) {
if ((end - ptr) < sizeof(struct binder_transaction_data_secctx)) {
ALOGE("parse: txn too small (binder_transaction_data_secctx)!\n");
return -1;
}
memcpy(&txn, (void*) ptr, sizeof(struct binder_transaction_data_secctx));
ptr += sizeof(struct binder_transaction_data_secctx);
} else /* BR_TRANSACTION */ {
if ((end - ptr) < sizeof(struct binder_transaction_data)) {
ALOGE("parse: txn too small (binder_transaction_data)!\n");
return -1;
}
//readbuf 偏移4個自己的cmd後,拷貝一個binder_transaction_data 結構的數據給transaction_data
memcpy(&txn.transaction_data, (void*) ptr, sizeof(struct binder_transaction_data));
ptr += sizeof(struct binder_transaction_data); //readbuf偏移 binder_transaction_data的內容,這樣readbuf可以指向下一個BR_XX結構
txn.secctx = 0;
}
//打印binder_transaction_data數據
binder_dump_txn(&txn.transaction_data);
if (func) {
unsigned rdata[256/4];
struct binder_io msg; //收到數據封裝
struct binder_io reply; //發送數據封裝
int res;
bio_init(&reply, rdata, sizeof(rdata), 4); //reply初始化
bio_init_from_txn(&msg, &txn.transaction_data); //從txn.transaction_data 解析出binder_io的信息,存入msg
res = func(bs, &txn, &msg, &reply); //svcmgr_handler 回調處理
if (txn.transaction_data.flags & TF_ONE_WAY) {
//如果是TF_ONE_WAY處理,則釋放txn->data的數據
binder_free_buffer(bs, txn.transaction_data.data.ptr.buffer);
} else {
//如果不是TF_ONE_WAY處理,給binder驅動回覆數據
binder_send_reply(bs, &reply, txn.transaction_data.data.ptr.buffer, res);
}
}
break;
}
}
從上面的邏輯看,我們重點關注的是 binder_transaction_data 這個結構,binder_transaction_data說明了transaction到底在傳輸什麼語義,而語義碼就記錄在其code成員中。不同語義碼需要攜帶的數據也是不同的,這些數據由data指定.
結構體說明如下:
struct binder_transaction_data {
union {
__u32 handle; //binder_ref(即handle)對於發送數據包的一方,該成員指明發送目的地。
//由於目的是在遠端,所以這裏填入的是對Binder實體的引用,存放在target.handle中。如前述,Binder的引用在代碼中也叫句柄(handle)
binder_uintptr_t ptr; //Binder_node的內存地址,當數據包到達接收方時,驅動已將該成員修改成Binder實體,即指向Binder對象內存的指針,使用target.ptr來獲得
} target;
binder_uintptr_t cookie; //BBinder指針, 發送方忽略該成員;接收方收到數據包時,該成員存放的是創建Binder實體時由該接收方自定義的任意數值,做爲與Binder指針相關的額外信息存放在驅動中
__u32 code; //RPC代碼,代表Client與Server雙方約定的命令碼,例如:ADD_SERVICE_TRANSACTION
__u32 flags; //標誌位,比如TF_ONE_WAY代表異步,即不等待Server端回覆
pid_t sender_pid; //發送端進程的pid
uid_t sender_euid; //發送端進程的uid
binder_size_t data_size; //data數據的總大小
binder_size_t offsets_size; //IPC對象的大小
union {
struct {
binder_uintptr_t buffer; //數據區起始地址
binder_uintptr_t offsets;//數據區IPC對象偏移量
} ptr;
__u8 buf[8];
} data;//RPC數據
}
從上面binder_transaction_data的結構可以看出,data可存入的數據很少,主要採用了數據其實地址和數據偏移量,根據代碼上下文可知,調用了bio_init_from_txn(),從txn.transaction_data 解析出binder_io的信息,存入msg
5.5.2.1 bio_init_from_txn()
bio_init_from_txn()的作用就是把 binder_transaction_data 的“數據起始地址”、“偏移量”、“data數據的總大小”和“偏移數組中可用的條目”:
void bio_init_from_txn(struct binder_io *bio, struct binder_transaction_data *txn)
{
bio->data = bio->data0 = (char *)(intptr_t)txn->data.ptr.buffer;
bio->offs = bio->offs0 = (binder_size_t *)(intptr_t)txn->data.ptr.offsets;
bio->data_avail = txn->data_size;
bio->offs_avail = txn->offsets_size / sizeof(size_t); //計算 偏移數組中可用的條目
bio->flags = BIO_F_SHARED; //標誌需要釋放緩衝區
}
binder_io 這個結構從字面意思我們就可以看出要用它來讀取binder傳遞來的數據,結構如下:
struct binder_io
{
char *data; //指向數據 讀/寫的指針
binder_size_t *offs; //數據偏移量
size_t data_avail; //數據緩衝區中可用的字節數
size_t offs_avail; //偏移數組中可用的條目
char *data0; //數據緩衝區的起始地址
binder_size_t *offs0; //緩衝區偏移量的起始位置
uint32_t flags;
uint32_t unused;
};
binder_transaction_data 和 binder_io的關聯
初始化完 binder_io 的replay,並把transaction_data 轉換成了binder_io 的msg後,調用回調函數svcmgr_handler()進行最終邏輯處理
5.6 svcmgr_handler()
在BR_TRANSACTION的命令解析後,就把binder_transaction_data_secctx的數據傳給回調函數svcmgr_handler()進行處理。
根據不同的傳輸語義碼(txn->code) 來進行相應的操作:查詢服務,註冊服務,以及列舉所服務
源碼如下:
int svcmgr_handler(struct binder_state *bs,
struct binder_transaction_data_secctx *txn_secctx,
struct binder_io *msg,
struct binder_io *reply)
{
struct svcinfo *si;
uint16_t *s;
size_t len;
uint32_t handle;
uint32_t strict_policy;
int allow_isolated;
uint32_t dumpsys_priority;
//獲取binder_parse 過來的binder_transaction_data 數據
struct binder_transaction_data *txn = &txn_secctx->transaction_data;
if (txn->target.ptr != BINDER_SERVICE_MANAGER)
return -1;
if (txn->code == PING_TRANSACTION)
return 0;
//從 msg的 binder_io.data的起始地址讀取4個字節的內容,存入strict_policy,strict_policy現在不需要使用,可以直接忽略
//然後msg->data 往後偏移4個字節,即忽略了開頭的strict_policy;msg->data_avail 緩衝區的剩餘可用字節數減去4個字節。
//msg->data0一直不變,指向數據緩衝區的起始地址
strict_policy = bio_get_uint32(msg);
bio_get_uint32(msg); // 繼續偏移4個字節,忽略了header
//先讀取msg->data接下來的4個字節,即爲length,然後讀取((len + 1) * sizeof(uint16_t))字節的內容存入s。這裏len+1的目的:字符串都要包含最後一個’\0’,需要把這個偏移量加入
s = bio_get_string16(msg, &len);
if (s == NULL) {
return -1;
}
if ((len != (sizeof(svcmgr_id) / 2)) ||
memcmp(svcmgr_id, s, sizeof(svcmgr_id))) {
fprintf(stderr,"invalid id %s\n", str8(s, len));
return -1;
}
//檢查SELinux環境是否有任何改變
if (sehandle && selinux_status_updated() > 0) {
#ifdef VENDORSERVICEMANAGER
struct selabel_handle *tmp_sehandle = selinux_android_vendor_service_context_handle();
#else
struct selabel_handle *tmp_sehandle = selinux_android_service_context_handle();
#endif
if (tmp_sehandle) {
selabel_close(sehandle);
sehandle = tmp_sehandle;
}
}
//根據不同code進行邏輯處理
switch(txn->code) {
case SVC_MGR_GET_SERVICE:
case SVC_MGR_CHECK_SERVICE:
//獲取服務名字和長度,分別存在s和len
s = bio_get_string16(msg, &len);
if (s == NULL) {
return -1;
}
//根據服務名稱查找相應服務
handle = do_find_service(s, len, txn->sender_euid, txn->sender_pid,
(const char*) txn_secctx->secctx);
if (!handle)
break;
bio_put_ref(reply, handle);
return 0;
case SVC_MGR_ADD_SERVICE:
s = bio_get_string16(msg, &len);
if (s == NULL) {
return -1;
}
handle = bio_get_ref(msg);
allow_isolated = bio_get_uint32(msg) ? 1 : 0;
dumpsys_priority = bio_get_uint32(msg);
//註冊指定服務
if (do_add_service(bs, s, len, handle, txn->sender_euid, allow_isolated, dumpsys_priority,
txn->sender_pid, (const char*) txn_secctx->secctx))
return -1;
break;
case SVC_MGR_LIST_SERVICES: {
uint32_t n = bio_get_uint32(msg);
uint32_t req_dumpsys_priority = bio_get_uint32(msg);
//掃描服務列表
if (!svc_can_list(txn->sender_pid, (const char*) txn_secctx->secctx, txn->sender_euid)) {
ALOGE("list_service() uid=%d - PERMISSION DENIED\n",
txn->sender_euid);
return -1;
}
si = svclist;
// walk through the list of services n times skipping services that
// do not support the requested priority
while (si) {
if (si->dumpsys_priority & req_dumpsys_priority) {
if (n == 0) break;
n--;
}
si = si->next;
}
if (si) {
bio_put_string16(reply, si->name);
return 0;
}
return -1;
}
default:
ALOGE("unknown code %d\n", txn->code);
return -1;
}
bio_put_uint32(reply, 0);
return 0;
}
其中有幾個binder_io 數據獲取的方法要注意:
1) uint32_t bio_get_uint32(struct binder_io *bio)
作用:獲取bio->data開始後的4個字節。
說明:bio->data 往後偏移4個字節,bio-->data_avail 緩衝區的剩餘可用字節數減去4個字節。bio-->data0一直不變,指向數據緩衝區的起始地址
2) uint16_t *bio_get_string16(struct binder_io *bio, size_t *sz)
作用:在svcmgr_handler()主要是用來獲取服務名和length
說明:先讀取一個32bits的整數,這個整數值就是字符串的長度。然後再讀取((len + 1) * sizeof(uint16_t))字節。
這裏len+1的目的:字符串都要包含最後一個’\0’,需要把這個偏移量加入
3) static void *bio_get(struct binder_io *bio, size_t size)
作用:根據傳入的size,獲取對應的ptr內容
說明:bio->data 往後偏移4個字節,bio-->data_avail 緩衝區的剩餘可用字節數減去4個字節。bio-->data0一直不變,指向數據緩衝區的起始地址
4) uint32_t bio_get_ref(struct binder_io *bio)
作用:獲取一個服務端的handle
說明:從bio->data 中讀出一個flat_binder_object的結構數據,返回 flat_binder_object->handle
5) void bio_put_ref(struct binder_io *bio, uint32_t handle)
作用:在svcmgr_handler中主要是把handler存入reply
說明:bio->data 偏移size(flat_binder_object), 其中存入 flat_binder_object obj的數據,並把hanlder存入obj
6) void bio_put_string16(struct binder_io *bio, const uint16_t *str)
作用:把service的名稱 存入reply
svcmgr_handler() 對binder驅動傳來的binder_transaction_data進行解析讀取後,根據不同的code,執行不同的操作,下面一起看看“註冊服務”和“查找服務”是如何操作的。
5.7 ServiceManager 是如何管理service信息的?
通過前面的一些信息,我們瞭解到,ServiceManager用來管理服務信息,那麼它是如何進行管理的呢?
在ServiceMnager的進程裏有一個全局性的svclist變量,服務信息都存在於這裏
struct svcinfo *svclist = NULL;
結構:
struct svcinfo
{
struct svcinfo *next; // 下一個"服務的信息"
uint32_t handle; //實際上記錄的就是系統service對應的binder句柄值
struct binder_death death;
int allow_isolated;
uint32_t dumpsys_priority;
size_t len; //服務名的長度
uint16_t name[0]; //Service的名字,最後要保留一個\0的位置,buffer長度爲 (len + 1) * sizeof(uint16_t)
};
5.8 註冊服務
根據傳入的code:SVC_MGR_ADD_SERVICE得知,本次binder流程想要進行服務註冊。
步驟:
- 從binder_io msg中獲取服務名稱和長度
- 從binder_io msg 中獲取handle
- 檢查該服務是否有註冊的selinx權限
- 查詢服務列表svclist 是否存在該handle,如果有handle,就更新該服務的handle信息,通過這個handle我們最終就能找到遠端的service實體
- 如果svclist不存在該服務,申請一個svcinfo的空間,把服務名、長度、handle等信息存入其中
- 把svcinfo 加入svclist的鏈表中
- 再以BC_ACQUIRE命令,handle爲目標的信息,通過ioctl發送給binder驅動
- 最後以BC_REQUEST_DEATH_NOTIFICATION命令的信息,通過ioctl發送給binder驅動,主要用於清理內存等收尾工作
int svcmgr_handler(struct binder_state *bs,
struct binder_transaction_data_secctx *txn_secctx,
struct binder_io *msg,
struct binder_io *reply)
{
...
case SVC_MGR_ADD_SERVICE:
//獲取服務的名字s和長度len
s = bio_get_string16(msg, &len);
if (s == NULL) {
return -1;
}
//獲取handle,通過這個handle我們最終就能找到遠端的service實體
handle = bio_get_ref(msg);
allow_isolated = bio_get_uint32(msg) ? 1 : 0;
dumpsys_priority = bio_get_uint32(msg);
//註冊服務
if (do_add_service(bs, s, len, handle, txn->sender_euid, allow_isolated, dumpsys_priority,
txn->sender_pid, (const char*) txn_secctx->secctx))
return -1;
break;
...
}
int do_add_service(struct binder_state *bs, const uint16_t *s, size_t len, uint32_t handle,
uid_t uid, int allow_isolated, uint32_t dumpsys_priority, pid_t spid, const char* sid) {
struct svcinfo *si;
//service的name長度不能大於127
if (!handle || (len == 0) || (len > 127))
return -1;
//最終調用selinux_check_access() 進行selinux的權限檢查,檢查服務是否有進行服務註冊
if (!svc_can_register(s, len, spid, sid, uid)) {
ALOGE("add_service('%s',%x) uid=%d - PERMISSION DENIED\n",
str8(s, len), handle, uid);
return -1;
}
//查詢是否包含該name的svcinfo
si = find_svc(s, len);
if (si) {
if (si->handle) {
ALOGE("add_service('%s',%x) uid=%d - ALREADY REGISTERED, OVERRIDE\n",
str8(s, len), handle, uid);
svcinfo_death(bs, si); //服務已註冊時,釋放相應的服務
}
//更新服務 handle
si->handle = handle;
} else {
si = malloc(sizeof(*si) + (len + 1) * sizeof(uint16_t));
if (!si) {
//內存不足,無法分配足夠內存
ALOGE("add_service('%s',%x) uid=%d - OUT OF MEMORY\n",
str8(s, len), handle, uid);
return -1;
}
// 對svcinfo進行初始化賦值
si->handle = handle;
si->len = len;
memcpy(si->name, s, (len + 1) * sizeof(uint16_t));
si->name[len] = '\0';
si->death.func = (void*) svcinfo_death; //註冊死亡回調,當進程死亡後,進行通知
si->death.ptr = si;
si->allow_isolated = allow_isolated;
si->dumpsys_priority = dumpsys_priority;
si->next = svclist; // svclist保存所有已註冊的服務
svclist = si;
}
//以BC_ACQUIRE命令,handle爲目標的信息,通過ioctl發送給binder驅動
binder_acquire(bs, handle);
//以BC_REQUEST_DEATH_NOTIFICATION命令的信息,通過ioctl發送給binder驅動,主要用於清理內存等收尾工作
binder_link_to_death(bs, handle, &si->death);
return 0;
}
5.9 查找服務
根據傳入的code:SVC_MGR_GET_SERVICE、SVC_MGR_CHECK_SERVICE得知,本次binder流程想要進行服務獲取。
步驟:
- 獲取服務的名字s和長度len
- 從svclist中根據名字查到一個svcinfo的結構
- 進行selinx檢查
- 檢查通過,返回該服務的handle
- 把handle放入binder_io 的reply的data中
- 最後在binder_parse()中調用binder_send_reply(),以BC_REPLY命令, 通過ioctl發送給binder驅動,最終轉到client進程
int svcmgr_handler(struct binder_state *bs,
struct binder_transaction_data_secctx *txn_secctx,
struct binder_io *msg,
struct binder_io *reply)
{
case SVC_MGR_GET_SERVICE:
case SVC_MGR_CHECK_SERVICE:
//獲取服務的名字s和長度len
s = bio_get_string16(msg, &len);
if (s == NULL) {
return -1;
}
//從svclist中查找服務名稱對應的handle
handle = do_find_service(s, len, txn->sender_euid, txn->sender_pid,
(const char*) txn_secctx->secctx);
if (!handle)
break;
//把handle存入binder_io reply的信息中
bio_put_ref(reply, handle);
return 0;
}
uint32_t do_find_service(const uint16_t *s, size_t len, uid_t uid, pid_t spid, const char* sid)
{
//查詢相應的服務
struct svcinfo *si = find_svc(s, len);
if (!si || !si->handle) {
return 0;
}
if (!si->allow_isolated) {
// If this service doesn't allow access from isolated processes,
// then check the uid to see if it is isolated.
uid_t appid = uid % AID_USER;
//檢查該服務是否允許孤立於進程而單獨存在
if (appid >= AID_ISOLATED_START && appid <= AID_ISOLATED_END) {
return 0;
}
}
//調用selinux_check_access()進行檢查服務是否滿足查詢條件,是否有selinx權限問題
if (!svc_can_find(s, len, spid, sid, uid)) {
return 0;
}
//返回服務的handle
return si->handle;
}
查到服務的handle後,存入binder_io reply的data中,其中存入的type爲HANDLE: BINDER_TYPE_HANDLE
void bio_put_ref(struct binder_io *bio, uint32_t handle)
{
struct flat_binder_object *obj;
if (handle)
obj = bio_alloc_obj(bio);
else
obj = bio_alloc(bio, sizeof(*obj));
if (!obj)
return;
obj->flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
obj->hdr.type = BINDER_TYPE_HANDLE;
obj->handle = handle;
obj->cookie = 0;
}
服務的handle被放入到binder_io的reply中後,我們回到binder_parse(),當svcmgr_handler()處理完成後,執行binder_send_reply()的過程。
binder_send_reply()主要是將BC_FREE_BUFFER和BC_REPLY命令協議發送給Binder驅動,向client端發送reply. 其中data的數據區中保存的是TYPE爲HANDLE
void binder_send_reply(struct binder_state *bs,
struct binder_io *reply,
binder_uintptr_t buffer_to_free,
int status)
{
struct {
uint32_t cmd_free;
binder_uintptr_t buffer;
uint32_t cmd_reply;
struct binder_transaction_data txn;
} __attribute__((packed)) data;
data.cmd_free = BC_FREE_BUFFER;
data.buffer = buffer_to_free;
data.cmd_reply = BC_REPLY; // reply命令
data.txn.target.ptr = 0;
data.txn.cookie = 0;
data.txn.code = 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 { //svcmgr_handler執行成功,把reply的數據組裝進入txn
data.txn.flags = 0;
data.txn.data_size = reply->data - reply->data0;
data.txn.offsets_size = ((char*) reply->offs) - ((char*) reply->offs0);
data.txn.data.ptr.buffer = (uintptr_t)reply->data0;
data.txn.data.ptr.offsets = (uintptr_t)reply->offs0;
}
//向Binder驅動通信
binder_write(bs, &data, sizeof(data));
}
6.總結
ServiceManager 註冊成爲上下文管理者-守護進程,負責管理系統中的所有服務。ServiceManger進程跟所有向其註冊服務的死亡通知建立聯繫, 那麼當服務所在進程死亡後, 會只需告知ServiceManager.每個Client通過查詢ServiceManager可獲取Server進程的情況,降低所有Client進程直接檢測會導致負載過重。
ServiceManager的整體流程如下:
- 打開設備,mmap一個128K的內存空間,進行用戶空間和內核空間的內存綁定
- 註冊成爲上下文的管理者--守護進程
- 陷入死循環,標註線程的looper狀態爲enter
- 不停的操作binder讀寫過程
- 解析binder數據,進行查詢服務、註冊服務、列出服務的核心操作
- 由於在進行服務註冊的時候,把svcinfo_death()註冊到了binder_death的中,當收到BR_DEAD_BINDER時,表明應用層向Binder驅動發送Binder調用時,Binder應用層的另一個端已經死亡,進行死亡通知操作,調用svcinfo_death(),最終調用binder_release()發送BC_RELEASE 到內核進行處理。
7.代碼路徑:
frameworks/native/cmds/servicemanager/binder.c
frameworks/native/cmds/servicemanager/service_manager.c
frameworks/native/cmds/servicemanager/servicemanager.rc
我的微信公衆號:IngresGe
參考:
《淺談Service Manager成爲Android進程間通信(IPC)機制Binder守護進程之路》
《SEAndroid安全機制對Binder IPC的保護分析》