車載攝像頭 HAL

Android 包含一個汽車 HIDL 硬體抽象層 (HAL),可在 Android 啟動過程的早期提供影像擷取和顯示,並在系統生命週期內持續運作。 HAL 包括外視系統 (EVS) 堆疊,通常用於支援配備基於 Android 的車載資訊娛樂 (IVI) 系統的車輛中的後視攝影機和環景顯示器。 EVS 還支援在用戶應用程式中實現高級功能。

Android 還包括 EVS 特定的擷取和顯示驅動程式介面(位於/hardware/interfaces/automotive/evs/1.0 )。雖然可以在現有 Android 攝影機和顯示服務之上建立後視攝影機應用程序,但此類應用程式可能會在 Android 啟動過程中運行得太晚。使用專用 HAL 可實現簡化的接口,並明確 OEM 需要實作什麼來支援 EVS 堆疊。

系統組成

EVS 包含以下系統元件:

EVS系統組成圖

圖 1.EVS系統組件概覽。

電動車應用程式

範例 C++ EVS 應用程式 ( /packages/services/Car/evs/app ) 用作參考實作。此應用程式負責從 EVS 管理器請求視訊幀並將完成的幀發送回 EVS 管理器進行顯示。一旦 EVS 和汽車服務可用,它就會由 init 啟動,目標是在開機後兩 (2) 秒內。 OEM 可以根據需要修改或取代 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 負責實作由/hardware/interfaces/automotive/evs中的 .hal 檔案表示的 API。此類實作負責配置和收集來自實體相機的數據,並透過 Gralloc 可識別的共享記憶體緩衝區傳遞數據。實現的顯示端負責提供可由應用程式填充的共享記憶體緩衝區(通常透過 EGL 渲染),並優先於可能希望出現在實體顯示器上的任何其他內容呈現完成的幀。 EVS 介面的供應商實作可以儲存在/vendor/… /device/…hardware/…下(例如/hardware/[vendor]/[platform]/evs )。

核心驅動程式

支援 EVS 堆疊的裝置需要核心驅動程式。 OEM 無需建立新驅動程序,而是可以選擇透過現有攝影機和/或顯示硬體驅動程式來支援 EVS 所需的功能。重複使用驅動程式可能是有利的,特別是對於影像呈現可能需要與其他活動執行緒協調的顯示驅動程式。 Android 8.0 包含一個基於 v4l2 的範例驅動程式(位於packages/services/Car/evs/sampleDriver中),該驅動程式依賴核心來提供 v4l2 支持,並依賴 SurfaceFlinger 來呈現輸出映像。

EVS硬體介面說明

本節介紹 HAL。供應商應提供適合其硬體的 API 實作。

IEvs枚舉器

此物件負責枚舉系統中可用的 EVS 硬體(一個或多個攝影機和單一顯示設備)。

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

傳回一個向量,其中包含系統中所有攝影機的描述。假設相機組是固定的並且在啟動時是已知的。有關相機描述的詳細信息,請參閱CameraDesc

openCamera(string camera_id) generates (IEvsCamera camera);

取得用於與由唯一的camera_id字串識別的特定相機互動的介面物件。失敗時傳回 NULL。嘗試重新開啟已開啟的相機不會失敗。為了避免與應用程式啟動和關閉相關的競爭條件,重新開啟相機應關閉先前的實例,以便可以滿足新的請求。以這種方式被搶佔的相機實例必須置於非活動狀態,等待最終銷毀並以返回碼OWNERSHIP_LOST回應任何影響相機狀態的請求。

closeCamera(IEvsCamera camera);

釋放 IEvsCamera 介面(與openCamera()呼叫相反)。在呼叫closeCamera之前,必須透過呼叫stopVideoStream()來停止攝影機視訊串流。

openDisplay() generates (IEvsDisplay display);

取得用於專門與系統的 EVS 顯示互動的介面物件。同一時間只有一個客戶端可以持有 IEvsDisplay 的功能實例。與openCamera中所述的主動開啟行為類似,可以隨時建立新的 IEvsDisplay 對象,並將停用任何先前的實例。無效的實例繼續存在並回應其所有者的函數調用,但在死亡時不得執行任何變異操作。最終,客戶端應用程式預計會注意到OWNERSHIP_LOST錯誤返回代碼並關閉並釋放非活動介面。

closeDisplay(IEvsDisplay display);

釋放 IEvsDisplay 介面(與openDisplay()呼叫相反)。在關閉顯示之前,必須將透過getTargetBuffer()呼叫接收到的未完成緩衝區返回顯示。

getDisplayState() generates (DisplayState state);

取得目前顯示狀態。 HAL 實作應報告實際的當前狀態,該狀態可能與最近請求的狀態不同。負責改變顯示狀態的邏輯應該存在於設備層之上,這使得 HAL 實作不希望自發地改變顯示狀態。如果目前沒有任何用戶端持有該顯示(透過呼叫 openDisplay),則此函數傳回NOT_OPEN 。否則,它會報告 EVS Display 的目前狀態(請參閱IEvsDisplay API )。

struct CameraDesc {
    string      camera_id;
    int32       vendor_flags;       // Opaque value
}
  • camera_id 。唯一標識給定相機的字串。可以是設備的核心設備名稱或設備的名稱,例如後視鏡。該字串的值由 HAL 實作選擇,並由上面的堆疊不透明地使用。
  • vendor_flags 。一種將專用攝影機資訊從驅動程式不透明地傳遞到自訂 EVS 應用程式的方法。它未經解釋地從驅動程式傳遞到 EVS 應用程序,EVS 應用程式可以隨意忽略它。

電動車相機

此物件代表單一相機,是捕捉影像的主要介面。

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 FPS 產生。啟動視訊串流所需的時間有效地計入任何後視攝影機啟動時間要求。如果串流沒有啟動,必須傳回錯誤碼;否則返回OK。

oneway doneWithFrame(BufferDesc buffer);

返回傳送到 IEvsCameraStream 的訊框。當使用完傳遞到 IEvsCameraStream 介面的幀後,該幀必須返回到 IEvsCamera 以供重複使用。可用的緩衝區數量有限(可能只有一個),如果供應耗盡,則在返回緩衝區之前不會再傳送任何幀,這可能會導致幀跳過(帶有空句柄的緩衝區表示結束)流的並且不需要透過此函數返回)。成功時傳回 OK,或可能包含INVALID_ARGBUFFER_NOT_AVAILABLE的適當錯誤代碼。

stopVideoStream();

停止傳送 EVS 相機畫面。由於傳遞是異步的,因此在此呼叫返回後,幀可能會在一段時間內繼續到達。必須返回每個幀,直到向 IEvsCameraStream 發出串流關閉訊號。在已停止或從未啟動的流上呼叫stopVideoStream是合法的,在這種情況下,它將被忽略。

getExtendedInfo(int32 opaqueIdentifier) generates (int32 value);

從 HAL 實作請求特定於驅動程式的資訊。 opaqueIdentifier允許的值是特定於驅動程式的,但傳遞的任何值都可能導致驅動程式崩潰。對於任何無法識別的opaqueIdentifier ,驅動程式應傳回 0。

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對象,這可能需要透過eglCreateImageKHR()擴充功能將圖像與 EGL 一起使用。

  • 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()停止視訊串流時,此回呼可能會隨著管道耗盡而繼續。每一幀仍必須返回;當流中的最後一幀被傳送時,將傳送 NULL bufferHandle,表示流的結束並且不會發生進一步的幀傳送。 NULL 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欄位和BufferDesc結構中的memHandle之間的任何對應關係。 bufferId值本質上是 HAL 驅動程式實作私有的,它可以根據需要使用(和重複使用)它們。

電動車顯示

此物件代表 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)。所選格式必須是平台 GLES 實作上的有效 GL 渲染目標。

出錯時,會傳回有空句柄的緩衝區,但不需要將這樣的緩衝區傳回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 。唯一識別顯示的字串。這可以是設備的核心設備名稱,也可以是設備的名稱,例如後視鏡。該字串的值由 HAL 實作選擇,並由上面的堆疊不透明地使用。
  • vendor_flags 。一種將專用攝影機資訊從驅動程式不透明地傳遞到自訂 EVS 應用程式的方法。它未經解釋地從驅動程式傳遞到 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 顯示器的狀態,可以停用(駕駛者不可見)或啟用(向駕駛員顯示影像)。包括暫時狀態,其中顯示尚不可見,但準備透過returnTargetBufferForDisplay()呼叫傳遞下一幀圖像而變得可見。

電動車管理器

EVS 管理器為 EVS 系統提供公共介面,用於收集和呈現外部攝影機視圖。如果硬體驅動程式僅允許每個資源(攝影機或顯示器)有一個活動接口,則 EVS 管理器可促進對攝影機的共享存取。單一主 EVS 應用程式是 EVS 管理器的第一個客戶端,也是唯一允許寫入顯示資料的用戶端(可授予其他客戶端對攝影機影像的唯讀存取權)。

EVS 管理器實作與底層 HAL 驅動程式相同的 API,並透過支援多個並發用戶端(多個用戶端可以透過 EVS 管理員開啟攝影機並接收視訊串流)來提供擴充服務。

EVS 管理器和 EVS 硬體 API 圖。

圖 2.EVS Manager 鏡像底層 EVS 硬體 API。

透過 EVS 硬體 HAL 實作或 EVS 管理器 API 進行操作時,應用程式看不到任何差異,只是 EVS 管理器 API 允許並發攝影機流存取。 EVS 管理器本身就是 EVS 硬體 HAL 層允許的客戶端,並充當 EVS 硬體 HAL 的代理。

以下部分僅描述在 EVS Manager 實作中具有不同(擴展)行為的那些呼叫;其餘呼叫與 EVS HAL 描述相同。

IEvs枚舉器

openCamera(string camera_id) generates (IEvsCamera camera);

取得用於與由唯一的camera_id字串識別的特定相機互動的介面物件。失敗時傳回 NULL。在 EVS 管理層,只要有足夠的系統資源可用,已經打開的攝影機就可以由另一個進程再次打開,從而允許將視訊串流傳送到多個消費者應用程式。 EVS Manager層的camera_id字串與報告給EVS Hardware層的相同。

電動車相機

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 管理器無法理解供應商定義的控製字的含義,因此它們不會被虛擬化,並且任何副作用都會適用於給定攝影機的所有用戶端。例如,如果供應商使用此呼叫來更改幀速率,則受影響的硬體層相機的所有用戶端都將以新速率接收幀。

電動車顯示

即使在 EVS 管理員級別,也僅允許一名顯示器擁有者。 Manager 不會加入任何功能,只會將 IEvsDisplay 介面直接傳遞到底層 HAL 實作。

電動車應用程式

Android 包含 EVS 應用程式的本機 C++ 參考實現,該應用程式與 EVS 管理器和車輛 HAL 進行通信,以提供基本的後視攝影機功能。該應用程式預計將在系統啟動過程的早期啟動,並根據可用攝影機和汽車狀態(檔位和方向燈狀態)顯示合適的影片。 OEM 可以使用自己的車輛特定邏輯和演示來修改或取代 EVS 應用程式。

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



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

由於影像資料在標準圖形緩衝區中呈現給應用程序,因此應用程式負責將影像從來源緩衝區移動到輸出緩衝區。雖然這會帶來資料複製的成本,但它也為應用程式提供了以所需的任何方式將影像渲染到顯示緩衝區的機會。

例如,應用程式可能會選擇行動像素資料本身,可能會使用內聯縮放或旋轉操作。應用程式還可以選擇使用來源影像作為 OpenGL 紋理,並將複雜的場景渲染到輸出緩衝區,包括圖示、指南和動畫等虛擬元素。更複雜的應用程式還可以選擇多個並發輸入攝影機並將它們合併到單一輸出幀中(例如用於車輛周圍環境的自上而下的虛擬視圖)。

在 EVS 顯示 HAL 中使用 EGL/SurfaceFlinger

本部分介紹如何使用 EGL 在 Android 10 中渲染 EVS Display HAL 實作。

EVS HAL 參考實作使用 EGL 在螢幕上渲染相機預覽,並使用libgui建立目標 EGL 渲染表面。在 Android 8(及更高版本)中, libgui被歸類為VNDK-private ,它指的是一組可供 VNDK 函式庫使用但供應商程序無法使用的函式庫。由於 HAL 實作必須駐留在供應商分區中,因此供應商無法在 HAL 實作中使用 Surface。

為供應商流程建置 libgui

使用libgui是在 EVS Display HAL 實作中使用 EGL/SurfaceFlinger 的唯一選項。實作libgui最直接的方法是直接透過框架/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);

下面提供了範例建置說明。期望收到$(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介面與框架進程進行通訊。將現有 AIDL 介面轉換為 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 政策

如果裝置實作是 full tr​​ble,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中為 full-treble 裝置定義的以下 neverallow 規則。

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;