本頁介紹了 Android O 中對 Binder 驅動程式進行的更改、提供了有關使用 Binder IPC 的詳細資訊,并列出了必需的 SELinux 政策。
對 Binder 驅動程式進行的更改
從 Android O 開始,Android 架構和 HAL 現在使用 Binder 互相通信。由于這種通信方式極大地增加了 Binder 流量,是以 Android O 包含了幾項改進,旨在確定 Binder IPC 的速度。內建最新版 Binder 驅動程式的 SoC 供應商和原始裝置制造商 (OEM) 應該檢視這些改進的清單、用于 3.18、4.4 和 4.9 版核心的相關 SHA,以及所需的使用者空間更改。
多個 Binder 域(上下文)
在通用 3.10、3.18、4.4、4.9 版核心和上遊中
為了明确地拆分架構(與裝置無關)和供應商(與具體裝置相關)代碼之間的 Binder 流量,Android O 引入了“Binder 上下文”這一概念。每個 Binder 上下文都有自己的裝置節點和上下文(服務)管理器。您隻能通過上下文管理器所屬的裝置節點對其進行通路,并且在通過特定上下文傳遞 Binder 節點時,隻能由另一個程序從相同的上下文通路上下文管理器,進而確定這些域完全互相隔離。如需使用方法的詳細資訊,請參閱 vndbinder 和 vndservicemanager。
分散-集中
在通用 3.10、3.18、4.4、4.9 版核心和上遊中
在之前的 Android 版本中,Binder 調用中的每條資料都會被複制 3 次:
- 一次是在調用程序中将資料序列化為 Parcel
- 一次是在核心驅動程式中将 Parcel 複制到目标程序
- 一次是在目标程序中對 Parcel 進行反序列化
Android O 使用分散-集中優化機制将複制次數從 3 次減少到了 1 次。資料保留其原始結構和記憶體布局,且 Binder 驅動程式會立即将資料複制到目标程序中,而不是先在 Parcel 中對資料進行序列化。在目标程序中,這些資料的結構和記憶體布局保持不變,并且,在無需再次複制的情況下即可讀取這些資料。
精細鎖定
在通用 3.18、4.4、4.9 版核心和上遊中
在之前的 Android 版本中,Binder 驅動程式使用全局鎖來防範對重要資料結構的并發通路。雖然采用全局鎖時出現争用的可能性極低,但主要的問題是,如果低優先級線程獲得該鎖,然後實作了搶占,則會導緻同樣需要獲得該鎖的優先級較高的線程出現嚴重的延遲。這會導緻平台卡頓。
原先嘗試解決此問題的方法是在保留全局鎖的同時禁止搶占。但是,這更像是一種臨時應對手段而非真正的解決方案,最終被上遊拒絕并舍棄。後來嘗試的解決方法側重于提升鎖定的精細程度,自 2017 年 1 月以來,Pixel 裝置上一直采用的是更加精細的鎖定。雖然這些更改大部分已公開,但未來版本中還會有一些重大的改進。
在确定了精細鎖定實作中的一些小問題後,我們使用不同的鎖定架構設計了一種改進的解決方案,并在 3.18、4.4, 和 4.9 版通用分支中送出了相關更改。我們會繼續在大量不同的裝置上測試這種實作方式;由于目前看來這個方案不存在什麼問題,是以建議搭載 Android O 的裝置都使用這種實作方式。
注意:我們強烈建議針對精細鎖定安排充足的測試時間。
實時優先級繼承
在通用 3.18、4.4、4.9 版核心中(即将針對上遊推出)
Binder 驅動程式一直支援 nice 優先級繼承。随着 Android 中以實時優先級運作的程序日益增加,現在出現以下這種情形也屬正常:如果實時線程進行 Binder 調用,則處理該調用的程序中的線程同樣會以實時優先級運作。為了支援這些使用情景,Android O 現在在 Binder 驅動程式中實作了實時優先級繼承。
除了事務級優先級繼承之外,“節點優先級繼承”允許節點(Binder 服務對象)指定對該節點執行調用操作所需的最低優先級。之前版本的 Android 已經通過 nice 值支援節點優先級繼承,但 Android O 增加了對實時排程政策節點繼承的支援。
注意:Android 性能團隊發現,實時優先級繼承會對架構 Binder 域 (/dev/binder) 造成不必要的負面影響,是以已對該域停用實時優先級繼承。
使用者空間更改
Android O 納入了在通用核心中使用現有 Binder 驅動程式所需的所有使用者空間更改,但有一個例外:針對 /dev/binder 停用實時優先級繼承的原始實作使用的是 ioctl。由于後續開發将優先級繼承的控制方法改為了更加精細的方法(根據 Binder 模式,而非上下文),是以,ioctl 不存于 Android 通用分支中,而是送出到了我們的通用核心中。
此項更改的影響是,所有節點均預設停用實時優先級繼承。Android 性能團隊發現,為 hwbinder 域中的所有節點啟用實時優先級繼承會有一定好處。要達到同樣的效果,請在使用者空間中擇優實施此更改。
通用核心的 SHA
要擷取對 Binder 驅動程式所做的必要更改,請同步到下列 SHA(或更高版本):
-
通用 3.18 版
cc8b90c121de ANDROID: Binder:請勿在還原時檢查優先級權限。
-
通用 4.4 版
76b376eac7a2 ANDROID: Binder:請勿在還原時檢查優先級權限。
-
通用 4.9 版
ecd972d4f9b5 ANDROID: Binder:請勿在還原時檢查優先級權限。
使用 Binder IPC
一直以來,供應商程序都使用 Binder 程序間通信 (IPC) 技術進行通信。在 Android O 中,/dev/binder 裝置節點成為了架構程序的專屬節點,這意味着供應商程序将無法再通路該節點。供應商程序可以通路 /dev/hwbinder,但必須将其 AIDL 接口轉為使用 HIDL。對于想要繼續在供應商程序之間使用 AIDL 接口的供應商,Android 會按以下方式支援 Binder IPC。
vndbinder
Android O 支援供應商服務使用新的 Binder 域,這可通過使用 /dev/vndbinder(而非 /dev/binder)進行通路。添加 /dev/vndbinder 後,Android 現在擁有以下 3 個 IPC 域:
IPC 域 | 說明 |
---|---|
/dev/binder | 架構/應用程序之間的 IPC,使用 AIDL 接口 |
/dev/hwbinder | 架構/供應商程序之間的 IPC,使用 HIDL 接口 ;供應商程序之間的 IPC,使用 HIDL 接口 |
/dev/vndbinder | 供應商/供應商程序之間的 IPC,使用 AIDL 接口 |
為了顯示 /dev/vndbinder,請確定核心配置項 CONFIG_ANDROID_BINDER_DEVICES 設為 “binder,hwbinder,vndbinder”(這是 Android 通用核心樹的預設設定)。
通常,供應商程序不直接打開 Binder 驅動程式,而是連結到打開 Binder 驅動程式的 libbinder 使用者空間庫。為 ::android::ProcessState() 添加方法可為 libbinder 選擇 Binder 驅動程式。供應商程序應該在調用 ProcessState,、IPCThreadState 或發出任何普通 Binder 調用之前調用此方法。要使用該方法,請在供應商程序(用戶端和伺服器)的 main() 後放置以下調用:
vndservicemanager
以前,Binder 服務通過 servicemanager 注冊,其他程序可從中檢索這些服務。在 Android O 中,servicemanager 現在專用于架構和應用程序,供應商程序無法再對其進行通路。
不過,供應商服務現在可以使用 vndservicemanager,這是一個使用 /dev/vndbinder(作為建構基礎的源代碼與架構 servicemanager 相同)而非 /dev/binder 的 servicemanager 的新執行個體。供應商程序無需更改即可與 vndservicemanager 通信;當供應商程序打開 /dev/vndbinder 時,服務查詢會自動轉至 vndservicemanager。
vndservicemanager 二進制檔案包含在 Android 的預設裝置 Makefile 中。
SELinux 政策
想要使用 Binder 功能來互相通信的供應商程序需要滿足以下要求:
- 能夠通路 /dev/vndbinder。
- 将 Binder {transfer, call} 接入 vndservicemanager。
- 針對想要通過供應商 Binder 接口調用供應商域 B 的任何供應商域 A 執行 binder_call(A, B) 操作。
- 有權在 vndservicemanager 中對服務執行 {add, find} 操作。
要滿足要求 1 和 2,請使用 vndbinder_use() 宏:
vndbinder_use(some_vendor_process_domain);
要滿足要求 3,需要通過 Binder 通信的供應商程序 A 和 B 的 binder_call(A, B) 可以保持不變,且不需要重命名。
要滿足要求 4,您必須按照處理服務名稱、服務标簽和規則的方式進行更改。
有關 SELinux 的詳細資訊,請參閱 Android 中的安全增強型 Linux。有關 Android 8.0 中 SELinux 的詳細資訊,請參閱 SELinux for Android 8.0。
服務名稱
以前,供應商程序在 service_contexts 檔案中注冊服務名稱并添加用于通路該檔案的相應規則。來自 device/google/marlin/sepolicy 的 service_contexts 檔案示例:
AtCmdFwd u:object_r:atfwd_service:s0
cneservice u:object_r:cne_service:s0
qti.ims.connectionmanagerservice u:object_r:imscm_service:s0
rcs u:object_r:radio_service:s0
uce u:object_r:uce_service:s0
vendor.qcom.PeripheralManager u:object_r:per_mgr_service:s0
而在 Android O 中,vndservicemanager 會加載 vndservice_contexts 檔案。遷移到 vndservicemanager(且已經在舊的 service_contexts 檔案中)的供應商服務應該添加到新的 vndservice_contexts 檔案中。
服務标簽
以前,服務标簽(例如 u:object_r:atfwd_service:s0)在 service.te 檔案中定義。例如:
type atfwd_service, service_manager_type;
在 Android O 中,您必須将類型更改為 vndservice_manager_type 并将規則移動到 vndservice.te 檔案中。例如:
type atfwd_service, vndservice_manager_type;
Servicemanager 規則
以前,規則會授予域通路權限,以向 servicemanager 添加服務或在其中查找服務。例如:
allow atfwd atfwd_service:service_manager find;
allow some_vendor_app atfwd_service:service_manager add;
在 Android O 中,這樣的規則可繼續存在并使用相同的類。例如:
allow atfwd atfwd_service:service_manager find;
allow some_vendor_app atfwd_service:service_manager add;