車用相機 HAL

Android 內含汽車 HIDL 硬體抽象層 (HAL), 提供圖像擷取及顯示功能。 在整個系統生命週期中都會持續運作HAL 包含 外部檢視系統 (EVS) 堆疊,通常用於支援後視圖 配備 Android 的車內相機和環場檢視畫面 資訊娛樂 (IVI) 系統。EVS 也能用來導入進階功能 使用者應用程式

Android 還另外提供 EVS 專用的擷取與顯示驅動程式 介面 (在 /hardware/interfaces/automotive/evs/1.0 中)。這件事 在現有 Android 上建構後置鏡頭應用程式 相機和螢幕服務,例如應用程式可能太晚執行 Android 開機程序使用專屬 HAL 可實現簡化的介面 也會清楚說明原始設備製造商 (OEM) 必須實作哪些項目來支援 EVS 堆疊。

系統元件

EVS 包含下列系統元件:

EVS 系統
元件圖表
圖 1.EVS 系統元件總覽。

EVS 應用程式

C++ EVS 應用程式範例 (/packages/services/Car/evs/app) 做為參考 。這個應用程式會負責向 然後將已播放完畢的影格傳回給 EVS Manager。 通常會在 EVS 和 Car Service 可供使用時啟動。 並在啟動後的兩 (2) 秒內設定目標原始設備製造商 (OEM) 可以修改或替換 EVS 應用程式。

EVS 經理

EVS 管理工具 (/packages/services/Car/evs/manager) 提供 EVS 應用程式需要的建構模塊 具備簡易的後置鏡頭畫面,支援 6DOF 多鏡頭轉譯功能。其介面 會透過 HIDL 存取,並建置可接受多個並行用戶端。 其他應用程式和服務 (尤其是「汽車服務」) 可以查詢 EVS 管理員狀態,用來瞭解 EVS 系統何時處於啟用狀態。

EVS HIDL 介面

EVS 系統 (相機和顯示元素) 定義於 android.hardware.automotive.evs 套件。實作範例 會練習介面 (產生合成測試圖片並驗證 所需的圖片) /hardware/interfaces/automotive/evs/1.0/default

原始設備製造商 (OEM) 負責導入 .hal 檔案表示的 API 在「/hardware/interfaces/automotive/evs」中。這類實作方式 負責設定及收集來自實體攝影機的資料 透過 Gralloc 可識別的共用記憶體緩衝區來傳送該資料。螢幕 實作端負責提供共用記憶體緩衝區 可由應用程式填入內容 (通常使用 EGL 算繪) 並分享螢幕畫面 優先顯示已完成的影格 實際螢幕可儲存 EVS 介面的供應商實作項目 低於 /vendor/… /device/…hardware/… (例如 /hardware/[vendor]/[platform]/evs)。

核心驅動程式

支援 EVS 堆疊的裝置需要核心驅動程式。而不是 生產新驅動程式後,原始設備製造商 (OEM) 可選擇透過 或顯示硬體驅動程式重複使用駕駛座可以帶來以下好處 尤其是在可能提供圖像展示畫面的顯示驅動程式方面,更是如此 需要與其他使用中的執行緒協調。Android 8.0 提供 v4l2 版本 樣本驅動程式 (位於 packages/services/Car/evs/sampleDriver) 取決於核心 v4l2 支援核心 以及透過 SurfaceFlinger 進行 輸出圖片

EVS 硬體介面說明

本節說明 HAL。供應商應提供 專為其硬體而調整的 API 實作。

IEvsEnumerator

這個物件負責列舉 系統 (一或多個鏡頭和單一顯示裝置) 的虛擬機器。

getCameraList() generates (vec<CameraDesc> cameras);

傳回包含系統中所有相機說明的向量。是 。如要進一步瞭解 攝影機描述,請參閱 CameraDesc

openCamera(string camera_id) generates (IEvsCamera camera);

取得介面物件,用來與特定相機互動 以不重複 camera_id 字串識別。失敗時會傳回 NULL。 無法重新開啟已開啟的攝影機。避開競速 應用程式啟動與關機、重新開啟相機的相關條件 關閉上一個執行個體,以便執行新的要求。A 罩杯 以這種方式先佔的相機執行個體,必須是停用的 並等待最終刪除並回應任何影響 相機狀態傳回 OWNERSHIP_LOST

closeCamera(IEvsCamera camera);

推出 IEvsCamera 介面 (與 openCamera() 呼叫)。攝影機視訊串流必須 已先呼叫 stopVideoStream() 再呼叫 closeCamera

openDisplay() generates (IEvsDisplay display);

取得介面物件,用來與系統的 EVS 螢幕。只有一個用戶端可以在 IEvsDisplay 設定一個實際運作的 IEvsDisplay 項目。 讓應用程式從可以最快做出回應的位置 回應使用者要求與 openCamera 中描述的積極開啟行為類似, 隨時建立新的 IEvsDisplay 物件,並停用先前使用的所有 IEvsDisplay 物件 執行個體。失效的執行個體會繼續存在,並回應函式呼叫 但不得以變更作業。最後 用戶端應用程式應會看到 OWNERSHIP_LOST 錯誤 傳回代碼,然後關閉並釋放已停用的介面。

closeDisplay(IEvsDisplay display);

推出 IEvsDisplay 介面 (與 openDisplay() 呼叫)。接收的未結緩衝區, getTargetBuffer() 呼叫都必須傳回顯示畫面, 關上螢幕。

getDisplayState() generates (DisplayState state);

取得目前的顯示狀態。HAL 實作應回報 這可能會與最近要求的狀態不同。 裝置上方應有負責變更顯示狀態的邏輯 因此不適合 HAL 實作能突然變更 顯示狀態如果螢幕目前未由任何用戶端 (通話 openDisplay),這個函式會傳回 NOT_OPEN。否則, 回報 EVS 顯示器的目前狀態 (詳情請參閱 IEvsDisplay API)。

struct CameraDesc {
    string      camera_id;
    int32       vendor_flags;       // Opaque value
}
  • camera_id。專門用於識別指定鏡頭的字串。 這可以是裝置的核心裝置名稱,也可以是裝置名稱,例如 rearview。這個字串的值是由 HAL 實作選擇 而且是在上圖的堆疊中不透明使用
  • vendor_flags。傳遞特殊相機的方法 不透明的駕駛資訊給自訂 EVS 應用程式通過測試 從駕駛人解讀為前到 EVS 應用程式,該應用程式可以自由忽略 基礎架構

IEvs 相機

這個物件代表單一相機,是 像是 Google 的開放原始碼模型

getCameraInfo() generates (CameraDesc info);

傳回這部攝影機的 CameraDesc

setMaxFramesInFlight(int32 bufferCount) generates (EvsResult result);

指定相機支援的緩衝區鏈深度。最多 這個許多影格可能會由 IEvsCamera 用戶端同時保留。如果這是 多個影格已送達接收器,但並未傳回 doneWithFrame,串流會略過影格,直到緩衝區傳回為止 以便重複使用即使在直播中 已在執行中,在這種情況下,建議您從鏈結中新增或移除緩衝區 。如果未呼叫此進入點,IEvsCamera 就會支援 至少會有一個畫格更加可接受

如果無法滿足要求的 bufferCount,函式會傳回 BUFFER_NOT_AVAILABLE 或其他相關錯誤代碼。在本例中 讓系統繼續執行先前設定的值。

startVideoStream(IEvsCameraStream receiver) generates (EvsResult result);

要求傳送這部攝影機的 EVS 相機畫面。IEvsCameraStream 開始定期接收新的圖像影格呼叫,直到 系統會呼叫 stopVideoStream()。影格必須開始提交 在 startVideoStream 呼叫的 500 毫秒內,且開始後,必須 最低每秒 10 個影格速率開始影片串流所需的時間 有效計入後置鏡頭啟動時間要求。如果 尚未開始串流,傳回錯誤代碼;否則會傳回 OK。

oneway doneWithFrame(BufferDesc buffer);

傳回由 IEvsCameraStream 傳送的影格。完成後 使用傳送至 IEvsCameraStream 介面的影格,影格必須 返回 IEvsCamera 以便重複使用。會有數量有限的小型緩衝區 供應時只要小幅更新,如果供應量已用盡 直到傳回緩衝區為止 略過的影格 (具有空值控點的緩衝區表示串流的結束, 不需要透過此函式傳回)。成功時傳回確定,或 可能包含 INVALID_ARGBUFFER_NOT_AVAILABLE

stopVideoStream();

停止提供 EVS 相機影格。由於放送是非同步的 影格傳回後,影格可能會繼續送達一段時間。每個畫面 直到您收到串流關閉訊號, IEvsCameraStream。透過直播撥打 stopVideoStream 是合法的 已停止或從未啟動 (在這種情況下,系統會忽略)。

getExtendedInfo(int32 opaqueIdentifier) generates (int32 value);

向 HAL 實作要求取得驅動程式專屬資訊。值 「opaqueIdentifier」只能為驅動程式專屬,但沒有任何值 否則驅動程式可能會當機如果有任何無法辨識的行程,駕駛應傳回 0 opaqueIdentifier

setExtendedInfo(int32 opaqueIdentifier, int32 opaqueValue) generates (EvsResult result);

將驅動程式專屬值傳送至 HAL 實作。這項額外資訊為 僅用於輔助車輛專用擴充功能,且無 HAL 實作時,此呼叫必須在預設狀態下運作。如果 驅動程式會辨識並接受值,則應傳回 OK ;否則 應傳回 INVALID_ARG 或其他代表性的錯誤代碼。

struct BufferDesc {
    uint32  width;      // Units of pixels
    uint32  height;     // Units of pixels
    uint32  stride;     // Units of pixels
    uint32  pixelSize;  // Size of single pixel in bytes
    uint32  format;     // May contain values from android_pixel_format_t
    uint32  usage;      // May contain values from Gralloc.h
    uint32  bufferId;   // Opaque value
    handle  memHandle;  // gralloc memory buffer handle
}

說明透過 API 傳遞的圖片。HAL 磁碟機負責 請填寫這個結構來描述圖片緩衝區和 HAL 用戶端 應將此結構視為唯讀。欄位包含足夠的資訊 能讓用戶端重建 ANativeWindowBuffer 物件, 搭配 EGL 使用圖片時 eglCreateImageKHR() 副檔名。

  • width。圖片的寬度 (以像素為單位)。
  • height。圖片的高度 (以像素為單位)。
  • stride。每個資料列實際佔用記憶體的像素數量 計算資料列對齊方式的任何邊框間距。以像素表示來進行比對 以及 gralloc 為緩衝區說明採用的慣例。
  • pixelSize。每個像素佔用的位元組數、 能讓您計算不同資料列之間資料列之間的必要大小 (以位元組為單位) 圖片 (以位元組為單位 stride = 以像素為單位 stride *) pixelSize)。
  • format。圖片使用的像素格式。提供的格式 必須與平台的 OpenGL 實作相容。票證 相容性測試,應將 HAL_PIXEL_FORMAT_YCRCB_420_SP 建議用於相機,RGBABGRA 則應 都放在多媒體廣告聯播網中
  • usage。HAL 實作設定的使用旗標。HAL 用戶端 則應傳遞這些未經修改的資訊 (詳情請參閱 Gralloc.h 個相關旗標)。
  • bufferId。HAL 實作所指定的不重複值,目的是 允許系統在透過 HAL API 往返後辨識緩衝區。 這個欄位中儲存的值可能會由 HAL 實作任意選擇。
  • memHandle。基準記憶體緩衝區的控點 包含圖片資料。HAL 實作可能會選擇儲存 Gralloc 緩衝區控制代碼

IEvsCameraStream

用戶端實作此介面以接收非同步影片影格 提交內容。

deliverFrame(BufferDesc buffer);

會在影片影格可供檢查時接收 HAL 來電。 此方法接收到的緩衝區控制代碼必須透過呼叫 IEvsCamera::doneWithFrame()。當使用者看到 呼叫 IEvsCamera::stopVideoStream(),這個回呼可能會繼續 以持續排除節點您必須傳回每個影格;最後一個影格 串流傳送,但系統傳回了空值 bufferHandle。 表示串流的結尾,不會再提交影格。空值 bufferHandle 本身不需要與 doneWithFrame(),但必須傳回所有其他帳號代碼

雖然技術上可以運用專屬緩衝區格式,但相容性 測試時需要採用以下四種支援的格式:NV21 (YCrCb) 4:2:0 半平面圖、YV12 (YCrCb 4:2:0 平面)、YUYV (YCrCb 4:2:2 交錯式)、 RGBA (32 位元 R:G:B:x)、BGRA (32 位元 B:G:R:x)。所選格式必須是有效格式 平台 GLES 實作上的 GL 紋理來源。

應用程式「不應」仰賴任何通訊內容 介於 bufferId 欄位和 memHandle 之間的 BufferDesc 結構。bufferId 的值如下: 基本上適用於 HAL 驅動程式實作,且可使用 (和重複使用) 然後視需要調整

IEvsDisplay

此物件代表 Evs 顯示、控制顯示狀態 並處理圖片的實際呈現方式

getDisplayInfo() generates (DisplayDesc info);

傳回系統提供的 EVS 顯示基本資訊 (請參閱 DisplayDesc)。

setDisplayState(DisplayState state) generates (EvsResult result);

設定顯示狀態。用戶端可以設定顯示狀態,表示 HAL 實作必須妥善接受 任何狀態仍可運作,不過回應可能會忽略 請求。

初始化時,畫面會定義從 NOT_VISIBLE 狀態,之後用戶端應會要求 VISIBLE_ON_NEXT_FRAME 狀態並開始提供影片。當 顯示不再需要顯示,用戶端必須 傳遞最後一個影片影格後出現 NOT_VISIBLE 狀態。

這個值對隨時要求任何州而言有效。如果螢幕 如果設為 VISIBLE_ON_NEXT_FRAME。除非所要求的狀態,否則一律傳回 OK 是無法辨識的列舉值,這時 INVALID_ARG

getDisplayState() generates (DisplayState state);

取得顯示狀態。HAL 實作應回報 則可能與最近要求的狀態不同。 裝置上方應有負責變更顯示狀態的邏輯 因此不適合 HAL 實作能突然變更 顯示狀態

getTargetBuffer() generates (handle bufferHandle);

傳回與螢幕相關聯的影格緩衝區控制代碼。這個緩衝區 由軟體和/或 GL 鎖定並寫入。必須傳回這個緩衝區 呼叫 returnTargetBufferForDisplay(),即使螢幕 不顯示。

雖然技術上可行的專屬緩衝區格式,但相容性測試 緩衝區必須採用以下四種支援的格式之一:NV21 (YCrCb 4:2:0 半平面)、YV12 (YCrCb 4:2:0 平面)、YUYV (YCrCb 4:2:2 交錯式)、RGBA (32 位元 R:G:B:x)、BGRA (32 位元 B:G:R:x)。所選格式必須是有效的 GL 平台的 GLES 實作上的算繪目標。

發生錯誤時,會傳回具有空值控制代碼的緩衝區,但這類緩衝區並未傳回 需要傳回 returnTargetBufferForDisplay

returnTargetBufferForDisplay(handle bufferHandle) generates (EvsResult result);

告知顯示緩衝區已準備好顯示。只能擷取緩衝區 透過呼叫 getTargetBuffer() 可以與這個物件搭配使用 呼叫,且 BufferDesc 的內容不得由 用戶端應用程式。在此呼叫之後,緩衝區不再適用於 用戶端。成功時會傳回 OK 或可能適當的錯誤代碼 包括 INVALID_ARGBUFFER_NOT_AVAILABLE

struct DisplayDesc {
    string  display_id;
    int32   vendor_flags;  // Opaque value
}

說明 EVS 顯示器的基本性質和 EVS 所需的屬性 。HAL 負責填寫這個結構 介紹 EVS 螢幕可以是實體螢幕,也可以是 覆蓋或混合其他呈現裝置。

  • display_id。專門用於識別顯示畫面的字串。 這可以是裝置的核心裝置名稱,也可以是裝置名稱 例如 rearview這個字串的值是由 HAL 選擇 實作和使用。
  • vendor_flags。傳遞特殊相機的方法 駕駛人無法清楚掌握資訊,進而提供自訂 EVS 應用程式。通過測試 從駕駛人解讀為前到 EVS 應用程式,該應用程式可以自由忽略 基礎架構
enum DisplayState : uint32 {
    NOT_OPEN,               // Display has not been “opened” yet
    NOT_VISIBLE,            // Display is inhibited
    VISIBLE_ON_NEXT_FRAME,  // Will become visible with next frame
    VISIBLE,                // Display is currently active
    DEAD,                   // Display is not available. Interface should be closed
}

說明 EVS 顯示器的狀態,可停用 (而非 或 enabled (會向駕駛人顯示圖片)。 包含尚未顯示,但已準備的暫時狀態 隨著圖像的下一個影格放送 returnTargetBufferForDisplay() 呼叫。

EVS 經理

EVS Manager 為 EVS 系統提供公用介面 收集和展示外部攝影機視圖。硬體驅動程式允許事項 每個資源 (攝影機或螢幕) 只有一個使用中的介面;EVS 管理工具 監視攝影機的共用存取權。只有一個主要的 EVS 應用程式 也是唯一可以寫入 EVS 管理員的用戶端 螢幕資料 (可向其他用戶端授予相機唯讀存取權 圖片)。

EVS Manager 實作的 API 與基礎 HAL 驅動程式相同 提供多種並行用戶端 (超過 哪個客戶可以透過 EVS Manager 開啟相機 串流)。

EVS 經理和
EVS 硬體 API 圖表。
圖 2. EVS 經理反映出基礎 EVS Hardware API。

透過 EVS 硬體 HAL 操作應用程式時,應用程式沒有任何差異 或 EVS Manager API (除非 EVS Manager API 允許) 並行相機串流存取。EVS 管理工具本身 是 EVS Hardware HAL 層的用戶端,用來代理 EVS Hardware HAL。

以下各節僅說明使用不同的呼叫 (延伸) 在導入 EVS Manager 時的行為;剩餘呼叫為 與 EVS HAL 說明相同

IEvsEnumerator

openCamera(string camera_id) generates (IEvsCamera camera);

取得介面物件,用來與特定相機互動 以不重複 camera_id 字串識別。失敗時會傳回 NULL。 只要系統資源充足,在 EVS Manager 層中 已開啟的相機可能會由另一個程序再次開啟 並將影片串流傳送到多個消費者應用程式。 EVS Manager 層中的 camera_id 字串與這些字串相同 回報給 EVS 硬體層

IEvs 相機

EVS Manager 提供 IEvsCamera 實作項目為內部虛擬化 因此,某個用戶端對相機執行的作業不會影響其他用戶端, 保有攝影機獨立存取權。

startVideoStream(IEvsCameraStream receiver) generates (EvsResult result);

開始影片串流。用戶端可以單獨開始及停止影片串流 使用相同的基礎鏡頭基礎相機會在

doneWithFrame(uint32 frameId, handle bufferHandle) generates (EvsResult result);

傳回影格。每個用戶端都必須在完成後傳回影格, 都可保留邊框。當 用戶端保留的畫面數目達到設定的上限時, 直到它傳回一個為止這個影格略過不會影響其他 這些用戶端仍可繼續正常接收所有影格。

stopVideoStream();

停止影片串流。各個用戶端可隨時停止自己的影片串流,不必另外建立 影響其他客戶硬體層的基礎相機串流 特定攝影機的最後一個用戶端停止串流時便會停止。

setExtendedInfo(int32 opaqueIdentifier, int32 opaqueValue) generates (EvsResult result);

傳送特定司機的專屬值,可能使單一用戶端受到影響 另一個用戶端。因為 EVS Manager 無法瞭解 服務供應商定義的控製字詞,並未虛擬化,因此任何副作用 會套用至指定相機的所有用戶端舉例來說,如果廠商使用這項呼叫, 受影響的硬體層相機的所有用戶端 以新的速率接收影格

IEvsDisplay

即使在 EVS 管理員層級,系統只允許顯示一位螢幕擁有者。 Manager 不會新增任何功能,只會傳送 IEvsDisplay 介面 直接導向基礎 HAL 實作

EVS 應用程式

Android 提供 EVS 的原生 C++ 參照實作 應用程式,藉此與 EVS Manager 和車輛 HAL 通訊 提供基本的後置鏡頭功能應用程式應會啟動 系統開機後,會根據 可用的攝影機和車輛的狀態 (齒輪和轉動訊號狀態)。 原始設備製造商 (OEM) 可將 EVS 應用程式修改或替換為自己的車輛專用 邏輯和呈現方式

圖 3.EVS 應用程式範例邏輯,取得相機 清單。


圖 4.EVS 應用程式範例邏輯,接收 影格回呼。

因為圖片資料會以標準圖形提供給應用程式 緩衝區時,應用程式負責將圖片從來源中移出 緩衝區插入輸出緩衝區雖然這會產生資料複製費用 也能讓應用程式將圖片算繪到 顯示緩衝區。

例如,應用程式可能會選擇移動像素資料本身 可能會搭配內嵌縮放或旋轉作業應用程式可以 還選擇使用來源圖片做為 OpenGL 紋理,並呈現複雜的 進入輸出緩衝區,包括圖示等虛擬元素 指南和動畫或者,應用程式也較複雜 並合併至單一輸出影格中 (例如在由上而下,以虛擬方式呈現車輛環境)。

在 EVS 螢幕 HAL 中使用 EGL/SurfaceFlinger

本節說明如何使用 EGL 算繪 EVS 螢幕 HAL 實作項目 。

EVS HAL 參考實作使用 EGL 算繪相機預覽畫面 並使用 libgui 來建立目標 EGL 算繪介面。在 Android 8 (及以上版本) 中,libgui 會歸類為 VNDK-private 代表廠商程序無法使用的一組 VNDK 程式庫。 HAL 實作項目必須位於供應商分區,因此無法使用 HAL 實作中的 Surface。

為廠商程序建立 libgui

使用 libgui 是唯一使用 EGL/SurfaceFlinger 的選項 。實作 libgui 最直接的方法就是 到 frameworks/native/libs/gui ,直接在建構指令碼中使用額外的建構目標。這個目標與 libgui 目標,但新增兩個欄位除外:

  • name
  • vendor_available
cc_library_shared {
    name: "libgui_vendor",
    vendor_available: true,
    vndk: {
        enabled: false,
    },
    double_loadable: true,

defaults: ["libgui_bufferqueue-defaults"],
srcs: [ … // bufferhub is not used when building libgui for vendors target: { vendor: { cflags: [ "-DNO_BUFFERHUB", "-DNO_INPUT", ], …

注意:供應商目標是使用 NO_INPUT 巨集建構,此巨集會從地塊資料中移除一個 32 位元的字詞。因為 SurfaceFlinger 預期這個欄位已遭移除,因此 SurfaceFlinger 無法剖析地塊。系統會將這視為 fcntl 失敗:

W Parcel  : Attempt to read object from Parcel 0x78d9cffad8 at offset 428 that is not in the object list
E Parcel  : fcntl(F_DUPFD_CLOEXEC) failed in Parcel::read, i is 0, fds[i] is 0, fd_count is 20, error: Unknown error 2147483647
W Parcel  : Attempt to read object from Parcel 0x78d9cffad8 at offset 544 that is not in the object list

如何解決這個問題:

diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 6066421fa..25cf5f0ce 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -54,6 +54,9 @@ status_t layer_state_t::write(Parcel& output) const
    output.writeFloat(color.b);
#ifndef NO_INPUT
    inputInfo.write(output);
+#else
+    // Write a dummy 32-bit word.
+    output.writeInt32(0);
#endif
    output.write(transparentRegion);
    output.writeUint32(transform);

build 範例 操作說明。預計會收到 $(ANDROID_PRODUCT_OUT)/system/lib64/libgui_vendor.so

$ cd <your_android_source_tree_top>
$ . ./build/envsetup.
$ lunch <product_name>-<build_variant>
============================================
PLATFORM_VERSION_CODENAME=REL
PLATFORM_VERSION=10
TARGET_PRODUCT=<product_name>
TARGET_BUILD_VARIANT=<build_variant>
TARGET_BUILD_TYPE=release
TARGET_ARCH=arm64
TARGET_ARCH_VARIANT=armv8-a
TARGET_CPU_VARIANT=generic
TARGET_2ND_ARCH=arm
TARGET_2ND_ARCH_VARIANT=armv7-a-neon
TARGET_2ND_CPU_VARIANT=cortex-a9
HOST_ARCH=x86_64
HOST_2ND_ARCH=x86
HOST_OS=linux
HOST_OS_EXTRA=<host_linux_version>
HOST_CROSS_OS=windows
HOST_CROSS_ARCH=x86
HOST_CROSS_2ND_ARCH=x86_64
HOST_BUILD_TYPE=release
BUILD_ID=QT
OUT_DIR=out
============================================

$ m -j libgui_vendor … $ find $ANDROID_PRODUCT_OUT/system -name "libgui_vendor*" .../out/target/product/hawk/system/lib64/libgui_vendor.so .../out/target/product/hawk/system/lib/libgui_vendor.so

在 EVS HAL 實作中使用繫結器

在 Android 8 (及以上版本) 中,/dev/binder 裝置節點專屬於 因此導致供應商流程無法存取。 供應商的程序應使用 /dev/hwbinder,且必須轉換任何 AIDL 介面 至 HIDL。如要在供應商程序之間繼續使用 AIDL 介面, 使用繫結器網域 /dev/vndbinder

IPC 網域 說明
/dev/binder 使用 AIDL 介面時,在架構/應用程式程序之間建立處理序間通訊 (IPC)
/dev/hwbinder 採用 HIDL 介面的架構/供應商程序之間的處理序間通訊 (IPC)
與 HIDL 介面的供應商程序之間的處理序間通訊 (IPC)
/dev/vndbinder 供應商/供應商使用 AIDL 介面之間的處理序間通訊 (IPC)

雖然 SurfaceFlinger 定義了 AIDL 介面,但供應商程序只能使用 HIDL 介面來 與架構程序通訊轉換現有成本 傳入 HIDL。幸好,Android 提供一種選取繫結器的方法 libbinder 驅動程式,使用者空間程式庫程序會連結至該驅動程式。

diff --git a/evs/sampleDriver/service.cpp b/evs/sampleDriver/service.cpp
index d8fb3166..5fd02935 100644
--- a/evs/sampleDriver/service.cpp
+++ b/evs/sampleDriver/service.cpp
@@ -21,6 +21,7 @@
#include <utils/Errors.h>
#include <utils/StrongPointer.h>
#include <utils/Log.h>
+#include <binder/ProcessState.h>

#include "ServiceNames.h"
#include "EvsEnumerator.h"
@@ -43,6 +44,9 @@ using namespace android;
int main() {
    ALOGI("EVS Hardware Enumerator service is starting");


+    // Use /dev/binder for SurfaceFlinger
+    ProcessState::initWithDriver("/dev/binder");
+


    // Start a thread to listen to video device addition events.
    std::atomic<bool> running { true };
    std::thread ueventHandler(EvsEnumerator::EvsUeventThread, std::ref(running));

注意:供應商流程應在呼叫之前呼叫 ProcessIPCThreadState,或是發出任何繫結器呼叫之前。

SELinux 政策

如果裝置實作完整,SELinux 會阻止廠商 程序,不再使用 /dev/binder例如 EVS HAL 樣本 實作項目已指派給 hal_evs_driver 網域,需要 寫入 binder_device 網域的權限。

W ProcessState: Opening '/dev/binder' failed: Permission denied
F ProcessState: Binder driver could not be opened. Terminating.
F libc    : Fatal signal 6 (SIGABRT), code -1 (SI_QUEUE) in tid 9145 (android.hardwar), pid 9145 (android.hardwar)
W android.hardwar: type=1400 audit(0.0:974): avc: denied { read write } for name="binder" dev="tmpfs" ino=2208 scontext=u:r:hal_evs_driver:s0 tcontext=u:object_r:binder_device:s0 tclass=chr_file permissive=0

不過,新增這些權限會導致建構失敗,因為它違反下列規範 一律不允許在 system/sepolicy/domain.te 中定義的規則。

libsepol.report_failure: neverallow on line 631 of system/sepolicy/public/domain.te (or line 12436 of policy.conf) violated by allow hal_evs_driver binder_device:chr_file { read write };
libsepol.check_assertions: 1 neverallow failures occurred
full_treble_only(`
neverallow {
    domain
    -coredomain
    -appdomain
    -binder_in_vendor_violators
} binder_device:chr_file rw_file_perms;
')

binder_in_vendor_violators 提供的屬性可擷取錯誤並引導開發指南。這項工具也能用來 解決上述 Android 10 違規問題。

diff --git a/evs/sepolicy/evs_driver.te b/evs/sepolicy/evs_driver.te
index f1f31e9fc..6ee67d88e 100644
--- a/evs/sepolicy/evs_driver.te
+++ b/evs/sepolicy/evs_driver.te
@@ -3,6 +3,9 @@ type hal_evs_driver, domain, coredomain;
hal_server_domain(hal_evs_driver, hal_evs)
hal_client_domain(hal_evs_driver, hal_evs)

+# Allow to use /dev/binder
+typeattribute hal_evs_driver binder_in_vendor_violators;
+
# allow init to launch processes in this context
type hal_evs_driver_exec, exec_type, file_type, system_file_type;
init_daemon_domain(hal_evs_driver)

建立 EVS HAL 參考實作做為廠商程序

建議您將下列變更套用至 packages/services/Car/evs/Android.mk。請務必確認 所有所述的變更項目都會在您的實作項目中運作。

diff --git a/evs/sampleDriver/Android.mk b/evs/sampleDriver/Android.mk
index 734feea7d..0d257214d 100644
--- a/evs/sampleDriver/Android.mk
+++ b/evs/sampleDriver/Android.mk
@@ -16,7 +16,7 @@ LOCAL_SRC_FILES := \
LOCAL_SHARED_LIBRARIES := \
    android.hardware.automotive.evs@1.0 \
    libui \
-    libgui \
+    libgui_vendor \
    libEGL \
    libGLESv2 \
    libbase \
@@ -33,6 +33,7 @@ LOCAL_SHARED_LIBRARIES := \
LOCAL_INIT_RC := android.hardware.automotive.evs@1.0-sample.rc

LOCAL_MODULE := android.hardware.automotive.evs@1.0-sample
+LOCAL_PROPRIETARY_MODULE := true

LOCAL_MODULE_TAGS := optional
LOCAL_STRIP_MODULE := keep_symbols
@@ -40,6 +41,7 @@ LOCAL_STRIP_MODULE := keep_symbols
LOCAL_CFLAGS += -DLOG_TAG=\"EvsSampleDriver\"
LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
+LOCAL_CFLAGS += -Iframeworks/native/include

#NOTE:  It can be helpful, while debugging, to disable optimizations
#LOCAL_CFLAGS += -O0 -g
diff --git a/evs/sampleDriver/service.cpp b/evs/sampleDriver/service.cpp
index d8fb31669..5fd029358 100644
--- a/evs/sampleDriver/service.cpp
+++ b/evs/sampleDriver/service.cpp
@@ -21,6 +21,7 @@
#include <utils/Errors.h>
#include <utils/StrongPointer.h>
#include <utils/Log.h>
+#include <binder/ProcessState.h>

#include "ServiceNames.h"
#include "EvsEnumerator.h"
@@ -43,6 +44,9 @@ using namespace android;
int main() {
    ALOGI("EVS Hardware Enumerator service is starting");
+    // Use /dev/binder for SurfaceFlinger
+    ProcessState::initWithDriver("/dev/binder");
+
     // Start a thread to listen video device addition events.
    std::atomic<bool> running { true };
    std::thread ueventHandler(EvsEnumerator::EvsUeventThread, std::ref(running));
diff --git a/evs/sepolicy/evs_driver.te b/evs/sepolicy/evs_driver.te
index f1f31e9fc..632fc7337 100644
--- a/evs/sepolicy/evs_driver.te
+++ b/evs/sepolicy/evs_driver.te
@@ -3,6 +3,9 @@ type hal_evs_driver, domain, coredomain;
hal_server_domain(hal_evs_driver, hal_evs)
hal_client_domain(hal_evs_driver, hal_evs)

+# allow to use /dev/binder
+typeattribute hal_evs_driver binder_in_vendor_violators;
+
# allow init to launch processes in this context
type hal_evs_driver_exec, exec_type, file_type, system_file_type;
init_daemon_domain(hal_evs_driver)
@@ -22,3 +25,7 @@ allow hal_evs_driver ion_device:chr_file r_file_perms;

# Allow the driver to access kobject uevents
allow hal_evs_driver self:netlink_kobject_uevent_socket create_socket_perms_no_ioctl;
+
+# Allow the driver to use the binder device
+allow hal_evs_driver binder_device:chr_file rw_file_perms;