本頁介紹了 Android 8 中 binder 驅動程序的更改,提供了有關使用 binder IPC 的詳細信息,並列出了所需的 SELinux 策略。
對活頁夾驅動程序的更改
從 Android 8 開始,Android 框架和 HAL 現在使用 binder 相互通信。由於這種通信極大地增加了 binder 流量,Android 8 包含了幾項旨在保持 binder IPC 快速的改進。 SoC 供應商和 OEM 應直接從內核/通用項目的 android-4.4、android-4.9 和更高版本的相關分支合併。
多個綁定器域(上下文)
Common-4.4 及更高版本,包括上游為了在框架(獨立於設備)和供應商(特定於設備)代碼之間清晰地分割綁定器流量,Android 8 引入了綁定器上下文的概念。每個綁定器上下文都有自己的設備節點和自己的上下文(服務)管理器。您只能通過它所屬的設備節點訪問上下文管理器,並且當通過某個上下文傳遞綁定節點時,只能由另一個進程從同一上下文訪問它,從而將域彼此完全隔離。有關使用的詳細信息,請參閱vndbinder和vndservicemanager 。
分散-聚集
Common-4.4 及更高版本,包括上游在之前的 Android 版本中,Binder 調用中的每條數據都被複製了 3 次:
- 一次在調用過程中將其序列化為
Parcel
- 一旦在內核驅動程序中將
Parcel
複製到目標進程 - 一次在目標進程中對
Parcel
進行反序列化
Android 8 使用scatter-gather 優化將副本數量從 3 減少到 1。不是先序列化Parcel
中的數據,而是數據保持其原始結構和內存佈局,驅動程序立即將其複製到目標進程。數據進入目標進程後,結構和內存佈局相同,無需再次復制即可讀取數據。
細粒度鎖定
Common-4.4 及更高版本,包括上游在以前的 Android 版本中,binder 驅動程序使用全局鎖來防止對關鍵數據結構的並發訪問。雖然對鎖的爭用很少,但主要問題是如果低優先級線程獲得鎖然後被搶占,它可能會嚴重延遲需要獲得相同鎖的高優先級線程。這導致平台卡頓。
解決此問題的最初嘗試涉及在持有全局鎖時禁用搶占。然而,這更像是一種 hack 而不是真正的解決方案,最終被上游拒絕並丟棄。隨後的嘗試集中在使鎖定更加細粒度,其版本自 2017 年 1 月以來一直在 Pixel 設備上運行。雖然其中大部分更改已公開,但在後續版本中進行了實質性改進。
在確定了細粒度鎖定實現中的小問題後,我們設計了一個具有不同鎖定架構的改進解決方案,並在所有常見內核分支中提交了更改。我們繼續在大量不同的設備上測試這個實現;由於我們不知道有任何未解決的問題,因此建議對搭載 Android 8 的設備執行此操作。
實時優先級繼承
Common-4.4 和 common-4.9(即將推出上游)binder 驅動程序一直支持良好的優先級繼承。隨著 Android 中越來越多的進程以實時優先級運行,在某些情況下,如果實時線程進行綁定器調用,那麼處理該調用的進程中的線程也以實時優先級運行是有意義的.為了支持這些用例,Android 8 現在在 binder 驅動程序中實現了實時優先級繼承。
除了事務級優先級繼承之外,節點優先級繼承還允許節點(綁定服務對象)指定執行對該節點的調用的最低優先級。以前的 Android 版本已經支持具有不錯值的節點優先級繼承,但 Android 8 增加了對實時調度策略節點繼承的支持。
用戶空間更改
Android 8 包括在通用內核中使用當前 binder 驅動程序所需的所有用戶空間更改,但有一個例外:禁用/dev/binder
的實時優先級繼承的原始實現使用ioctl 。隨後的開發將優先級繼承的控制切換到了更細粒度的方法,即每個綁定器模式(而不是每個上下文)。因此,ioctl 不在 Android 公共分支中,而是在我們的公共內核中提交。
此更改的效果是默認情況下每個節點都禁用實時優先級繼承。 Android 性能團隊發現為hwbinder
域中的所有節點啟用實時優先級繼承是有益的。為了達到同樣的效果,在用戶空間中挑選這種變化。
通用內核的 SHA
要獲得對 binder 驅動程序的必要更改,請同步到適當的 SHA:
- Common-3.18
cc8b90c121de ANDROID:活頁夾:不要在恢復時檢查 prio 權限。 - Common-4.4
76b376eac7a2 ANDROID:活頁夾:不要檢查恢復時的優先權。 - Common-4.9
ecd972d4f9b5 ANDROID:活頁夾:不要檢查恢復時的prio權限。
使用活頁夾 IPC
歷史上,供應商進程使用綁定器進程間通信 (IPC) 進行通信。在 Android 8 中, /dev/binder
設備節點成為框架進程獨有的,這意味著供應商進程不再有權訪問它。供應商進程可以訪問/dev/hwbinder
,但必須將其 AIDL 接口轉換為使用 HIDL。對於希望在供應商進程之間繼續使用 AIDL 接口的供應商,Android 支持 binder IPC,如下所述。
vndbinder
Android 8 支持供供應商服務使用的新 binder 域,使用/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 驅動程序,而是鏈接到打開 binder 驅動程序的libbinder
用戶空間庫。為::android::ProcessState()
添加一個方法會選擇libbinder
的 binder 驅動程序。供應商進程應在調用ProcessState,
IPCThreadState
之前或在進行任何一般的綁定器調用之前調用此方法。要使用,請在供應商進程(客戶端和服務器)的main()
之後放置以下調用:
ProcessState::initWithDriver("/dev/vndbinder");
vnd服務管理器
以前,綁定服務在servicemanager
中註冊,其他進程可以在其中檢索它們。在 Android 8 中, servicemanager
現在僅供框架和應用進程使用,供應商進程無法再訪問它。
但是,供應商服務現在可以使用vndservicemanager
,這是一個新的servicemanager
實例,它使用/dev/vndbinder
而不是/dev/binder
並且與框架servicemanager
使用相同的源構建。供應商進程無需進行更改即可與vndservicemanager
;當供應商進程打開 / dev/vndbinder
時,服務查找會自動轉到vndservicemanager
。
vndservicemanager
二進製文件包含在 Android 的默認設備 makefile 中。
SELinux 政策
想要使用 binder 功能相互通信的供應商進程需要以下內容:
- 訪問
/dev/vndbinder
。 - Binder
{transfer, call}
掛鉤到vndservicemanager
。 -
binder_call(A, B)
用於希望通過供應商綁定接口調用供應商域 B 的任何供應商域 A。 - 在
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 的詳細信息,請參閱適用於 Android 8.0 的SELinux 。
服務名稱
以前,供應商在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 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;