使用繫結器處理序間通訊 (IPC)

本頁面說明 Android 8 中 binder 驅動程式的變更,並提供使用 binder IPC 的詳細資訊,以及必要的 SELinux 政策。

繫結器驅動程式的變更

自 Android 8 起,Android 架構和 HAL 會使用繫結器相互通訊。由於這項通訊會大幅增加 Binder 流量,Android 8 包含了幾項改善功能,旨在讓 Binder IPC 保持快速。SoC 供應商和原始設備製造商 (OEM) 應直接從 kernel/common 專案的 android-4.4、android-4.9 以上版本的相關分支合併。

多個繫結器網域 (情境)

Common-4.4 以上版本,包括上游版本

為了在架構 (裝置獨立) 和供應商 (裝置專屬) 程式碼之間清楚劃分 Binder 流量,Android 8 引入了 Binder 內容的概念。每個 Binder 內容都有自己的裝置節點和內容 (服務) 管理員。您只能透過所屬裝置節點存取情境管理器,而且當您透過特定情境傳遞繫結器節點時,只有另一個程序才能從該情境存取該節點,因此可完全隔離各個網域。如需詳細資訊,請參閱 vndbindervndservicemanager

散佈-收集

Common-4.4 以上版本,包括上游版本

在先前版本的 Android 中,繫結器呼叫中的每個資料都會複製三次:

  • 在呼叫程序中將其序列化為 Parcel
  • 在核心驅動程式中將 Parcel 複製到目標程序
  • 在目標程序中解序列 Parcel 一次

Android 8 會使用散布-收集最佳化,將副本數量從 3 減少為 1。系統不會先在 Parcel 中序列化資料,而是讓資料保留在原始結構和記憶體版面配置中,然後由驅動程式立即複製至目標程序。資料進入目標程序後,結構和記憶體版面配置都會相同,因此無須另外複製,即可讀取資料。

精細的鎖定

Common-4.4 以上版本,包括上游版本

在先前的 Android 版本中,Binder 驅動程式會使用全域鎖定機制,防止同時存取重要資料結構。雖然鎖定機制爭奪的情況很少,但主要問題是,如果低優先順序的執行緒取得鎖定機制,然後遭到搶先,可能會嚴重延遲需要取得相同鎖定機制的高優先順序執行緒。這會導致平台出現卡頓現象。

最初嘗試解決這個問題時,我們會在保留全域鎖定時停用預取。不過,這項做法更像是駭客攻擊,而非真正的解決方案,最終遭到上游拒絕並遭到淘汰。後續的嘗試則著重於讓鎖定功能更精細,自 2017 年 1 月起,Pixel 裝置就已開始執行這項功能。雖然大部分的變更都已公開,但後續版本也進行了大幅改善。

在找出精細鎖定實作中的小問題後,我們設計了採用不同鎖定架構的改善解決方案,並提交所有常見核心分支的變更。我們會持續在大量不同裝置上測試這項實作方式;由於我們未發現任何未解決的問題,因此這是搭載 Android 8 的裝置的建議實作方式。

即時優先順序繼承

Common-4.4 和 common-4.9 (上游即將推出)

Binder 驅動程式一向支援優先順序繼承。隨著 Android 中以即時優先順序執行的程序數量增加,在某些情況下,如果即時執行緒發出 Binder 呼叫,處理該呼叫的程序中的執行緒也會以即時優先順序執行,這也是合理的做法。為支援這些用途,Android 8 現在會在 Binder 驅動程式中實作即時優先順序繼承功能。

除了交易層級優先順序繼承之外,節點優先順序繼承功能還可讓節點 (Binder 服務物件) 指定應執行對此節點的呼叫的最低優先順序。先前版本的 Android 已支援節點優先順序繼承,並提供不錯的值,但 Android 8 則新增了對即時排程政策節點繼承的支援。

使用者空間變更

Android 8 包含所有使用者空間變更,可與通用核心中的目前 Binder 驅動程式搭配運作,但有一個例外狀況:為 /dev/binder 停用即時優先順序繼承功能的原始實作方式使用了 ioctl。後續開發作業將優先順序繼承的控制權改為更精細的方法,也就是依 Binder 模式 (而非依情境) 進行。因此,ioctl 不在 Android 通用分支中,而是提交至通用核心

這項變更的影響是,系統會預設為停用每個節點的即時優先順序繼承功能。Android 效能團隊發現,為 hwbinder 網域中的所有節點啟用即時優先順序繼承功能相當有益。如要達到相同的效果,請在使用者空間中挑選這個變更

常見核心的 SHA

如要取得 Binder 驅動程式的必要變更,請同步至適當的 SHA:

  • Common-3.18
    cc8b90c121de ANDROID:Binder:請勿在還原時檢查 prio 權限。
  • Common-4.4
    76b376eac7a2 ANDROID:Binder:請勿在還原時檢查優先權限。
  • Common-4.9
    ecd972d4f9b5 ANDROID:binder:請勿在還原時檢查 prio 權限。

使用 Binder IPC

以往,供應商程序會使用 Binder 處理序間通訊 (IPC) 進行通訊。在 Android 8 中,/dev/binder 裝置節點會專屬於架構程序,這表示供應商程序不再有存取權。供應商程序可以存取 /dev/hwbinder,但必須將 AIDL 介面轉換為使用 HIDL。如果供應商想繼續在供應商程序之間使用 AIDL 介面,Android 會支援下文所述的繫結器 IPC。在 Android 10 中,Stable AIDL 允許所有程序使用 /dev/binder,同時解決 HIDL 和 /dev/hwbinder 解決的穩定性保證問題。如要瞭解如何使用穩定版 AIDL,請參閱「HAL 專用 AIDL」一文。

vndbinder

Android 8 支援新的繫結器網域,供供應商服務使用,可透過 /dev/vndbinder 存取,而非 /dev/binder。隨著 /dev/vndbinder 的加入,Android 現已擁有以下三個 IPC 網域:

IPC 網域 說明
/dev/binder 使用 AIDL 介面的架構/應用程式程序之間的 IPC
/dev/hwbinder 具有 HIDL 介面的架構/供應商程序之間的 IPC
具有 HIDL 介面的供應商程序之間的 IPC
/dev/vndbinder 使用 AIDL 介面的供應商/供應商程序之間的 IPC

如要顯示 /dev/vndbinder,請確認核心設定項目 CONFIG_ANDROID_BINDER_DEVICES 已設為 "binder,hwbinder,vndbinder" (這是 Android 常見核心樹狀結構中的預設值)。

通常,供應商程序不會直接開啟 Binder 驅動程式,而是連結至 libbinder 使用者空間程式庫,後者會開啟 Binder 驅動程式。新增 ::android::ProcessState() 的方法會選取 libbinder 的繫結器驅動程式。供應商程序應在呼叫 ProcessState, IPCThreadState 之前,或在一般情況下呼叫任何 Binder 之前,呼叫這個方法。如要使用,請在供應商程序 (用戶端和伺服器) 的 main() 後方放置下列呼叫:

ProcessState::initWithDriver("/dev/vndbinder");

vndservicemanager

先前,繫結器服務會註冊至 servicemanager,其他程序可從該處擷取這些服務。在 Android 8 中,servicemanager 現已專供架構和應用程式程序使用,供應商程序則無法再存取該程序。

不過,供應商服務現在可以使用 vndservicemanager,這是 servicemanager 的新例項,使用 /dev/vndbinder 而非 /dev/binder,且與架構 servicemanager 使用相同的來源建構。供應商程序不需要變更就能與 vndservicemanager 通訊;當供應商程序開啟 /dev/vndbinder 時,服務查詢會自動轉到 vndservicemanager

vndservicemanager 二進位檔已納入 Android 的預設裝置 makefile。

SELinux 政策

供應商程序如要使用 Binder 功能相互通訊,就需要以下項目:

  1. 有權存取 /dev/vndbinder
  2. Binder {transfer, call} 鉤入 vndservicemanager
  3. binder_call(A, B):任何供應商網域 A 如要透過供應商 Binder 介面呼叫供應商網域 B,
  4. vndservicemanager 中的 {add, find} 服務權限。

如要滿足第 1 和第 2 項規定,請使用 vndbinder_use() 巨集:

vndbinder_use(some_vendor_process_domain);

為了滿足第 3 項規定,需要透過繫結器通訊的供應商程序 A 和 B 的 binder_call(A, B) 可以保留原位,且不需要重新命名。

如要符合第 4 項規定,您必須變更服務名稱、服務標籤和規則的處理方式。

如要進一步瞭解 SELinux,請參閱「Android 中的安全增強式 Linux」。如要進一步瞭解 Android 8.0 中的 SELinux,請參閱「Android 8.0 專用 SELinux」。

服務名稱

先前,供應商程序會在 service_contexts 檔案中註冊服務名稱,並新增對應的存取該檔案的規則。device/google/marlin/sepolicyservice_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 8 中,vndservicemanager 會改為載入 vndservice_contexts 檔案。遷移至 vndservicemanager 的供應商服務 (已位於舊 service_contexts 檔案中) 應新增至新的 vndservice_contexts 檔案。

服務標籤

先前,u:object_r:atfwd_service:s0 等服務標籤是在 service.te 檔案中定義。例子:

type atfwd_service,      service_manager_type;

在 Android 8 中,您必須將類型變更為 vndservice_manager_type,並將規則移至 vndservice.te 檔案。例子:

type atfwd_service,      vndservice_manager_type;

服務管理員規則

先前,規則會授予網域存取權,以便從 servicemanager 新增或尋找服務。例子:

allow atfwd atfwd_service:service_manager find;
allow some_vendor_app atfwd_service:service_manager add;

在 Android 8 中,這些規則可以保留原樣,並使用相同的類別。範例如下:

allow atfwd atfwd_service:service_manager find;
allow some_vendor_app atfwd_service:service_manager add;