天天看點

使用 Binder IPC

本頁介紹了 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 功能來互相通信的供應商程序需要滿足以下要求:

  1. 能夠通路 /dev/vndbinder。
  2. 将 Binder {transfer, call} 接入 vndservicemanager。
  3. 針對想要通過供應商 Binder 接口調用供應商域 B 的任何供應商域 A 執行 binder_call(A, B) 操作。
  4. 有權在 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;