Android contiene un livello di astrazione hardware (HAL) HIDL per il settore automobilistico che consente l'acquisizione e la visualizzazione di immagini nelle prime fasi della procedura di avvio di Android e continua a funzionare per tutta la durata del sistema. L'HAL include lo stack del sistema di visione esterna (EVS) e viene in genere utilizzato per supportare la telecamera posteriore e i display con visuale a 360° nei veicoli con sistemi di infotainment (IVI) basati su Android. EVS consente inoltre di implementare funzionalità avanzate nelle app utente.
Android include anche un'interfaccia driver di acquisizione e visualizzazione specifica per EVS (in /hardware/interfaces/automotive/evs/1.0
). Sebbene sia possibile creare un'app per la videocamera posteriore basata sui servizi di visualizzazione e fotocamera Android esistenti, è probabile che un'app di questo tipo venga eseguita troppo tardi nel processo di avvio di Android. L'utilizzo di un HAL dedicato consente di semplificare l'interfaccia
e rende chiaro ciò che un OEM deve implementare per supportare lo stack EVS.
Componenti di sistema
EVS include i seguenti componenti del sistema:

Figura 1. Panoramica dei componenti del sistema EVS.
App EVS
Un'app EVS C++ di esempio
(/packages/services/Car/evs/app
) funge da implementazione
di riferimento. Questa app è responsabile della richiesta di frame video
a EVS Manager e dell'invio dei frame completati per la visualizzazione a EVS Manager.
Prevede di essere avviato da init non appena sono disponibili EVS e Car Service,
entro due (2) secondi dall'accensione. Gli OEM possono modificare o sostituire l'app EVS
come preferiscono.
EVS Manager
EVS Manager (/packages/services/Car/evs/manager
) fornisce
i blocchi di base necessari a un'app EVS per implementare qualsiasi cosa, da una
semplice visualizzazione della videocamera posteriore a un rendering multicamera a 6 gradi di libertà. La sua interfaccia
è presentata tramite HIDL ed è progettata per accettare più client simultanei.
Altre app e servizi (in particolare il servizio auto) possono eseguire query sullo stato di EVS Manager per scoprire quando il sistema EVS è attivo.
Interfaccia HIDL EVS
Il sistema EVS, sia la videocamera che gli elementi del display, è definito nel pacchetto
android.hardware.automotive.evs
. Un'implementazione di esempio
che esercita l'interfaccia (genera immagini di test sintetiche e convalida il
viaggio di andata e ritorno delle immagini) è fornita in
/hardware/interfaces/automotive/evs/1.0/default
.
L'OEM è responsabile dell'implementazione dell'API espressa dai file .hal
in /hardware/interfaces/automotive/evs
. Queste implementazioni sono
responsabili della configurazione e della raccolta dei dati dalle videocamere fisiche e
della loro distribuzione tramite buffer di memoria condivisa riconoscibili da Gralloc. La parte
dell'implementazione del display è responsabile della fornitura di un buffer di memoria condiviso
che può essere riempito dall'app (di solito tramite il rendering EGL) e della presentazione
dei frame completati in preferenza a qualsiasi altra cosa che potrebbe voler apparire sul
display fisico. Le implementazioni del fornitore dell'interfaccia EVS possono essere archiviate
in /vendor/… /device/…
o hardware/…
(ad es.
/hardware/[vendor]/[platform]/evs
).
Driver del kernel
Un dispositivo che supporta lo stack EVS richiede driver del kernel. Anziché
creare nuovi driver, gli OEM hanno la possibilità di supportare le funzionalità richieste da EVS tramite
driver hardware di videocamere e/o display esistenti. Il riutilizzo dei driver potrebbe essere
vantaggioso, soprattutto per i driver di visualizzazione in cui la presentazione delle immagini potrebbe
richiedere il coordinamento con altri thread attivi. Android 8.0 include un driver di esempio basato su v4l2 (in packages/services/Car/evs/sampleDriver
) che dipende dal kernel per il supporto di v4l2 e da SurfaceFlinger per la presentazione dell'immagine di output.
Descrizione dell'interfaccia hardware EVS
La sezione descrive l'HAL. I fornitori devono fornire implementazioni di questa API adattate al loro hardware.
IEvsEnumerator
Questo oggetto è responsabile dell'enumerazione dell'hardware EVS disponibile nel sistema (una o più videocamere e il singolo dispositivo di visualizzazione).
getCameraList() generates (vec<CameraDesc> cameras);
Restituisce un vettore contenente le descrizioni di tutte le videocamere del sistema. Si presume che l'insieme di videocamere sia fisso e conoscibile al momento dell'avvio. Per informazioni dettagliate
sulle descrizioni delle telecamere, vedi CameraDesc
.
openCamera(string camera_id) generates (IEvsCamera camera);
Ottiene un oggetto interfaccia utilizzato per interagire con una videocamera specifica
identificata dalla stringa univoca camera_id. Restituisce NULL in caso di errore.
I tentativi di riapertura di una videocamera già aperta non possono non riuscire. Per evitare condizioni di competizione
associate all'avvio e all'arresto dell'app, la riapertura di una videocamera
deve arrestare l'istanza precedente in modo che la nuova richiesta possa essere soddisfatta. Un'istanza della videocamera che è stata interrotta in questo modo deve essere messa in stato inattivo, in attesa della distruzione finale e rispondere a qualsiasi richiesta di modifica dello stato della videocamera con un codice restituito OWNERSHIP_LOST
.
closeCamera(IEvsCamera camera);
Rilascia l'interfaccia IEvsCamera (ed è l'opposto della chiamata
openCamera()
). Lo stream video della videocamera deve essere
interrotto chiamando il numero stopVideoStream()
prima di chiamare il numero closeCamera
.
openDisplay() generates (IEvsDisplay display);
Ottiene un oggetto interfaccia utilizzato per interagire esclusivamente con il display
EVS del sistema. Solo un client può contenere un'istanza funzionale di IEvsDisplay alla volta. Analogamente al comportamento di apertura aggressivo descritto in openCamera
,
un nuovo oggetto IEvsDisplay può essere creato in qualsiasi momento e disattiverà qualsiasi istanza precedente. Le istanze invalidate continuano a esistere e a rispondere alle chiamate di funzioni
dei proprietari, ma non devono eseguire operazioni di mutazione quando sono inattive. Alla fine,
l'app client dovrebbe notare i codici di errore OWNERSHIP_LOST
,
chiudere e rilasciare l'interfaccia inattiva.
closeDisplay(IEvsDisplay display);
Rilascia l'interfaccia IEvsDisplay (ed è l'opposto della chiamata
openDisplay()
). I buffer in sospeso ricevuti tramite
le chiamate getTargetBuffer()
devono essere restituiti al display prima di
chiuderlo.
getDisplayState() generates (DisplayState state);
Recupera lo stato attuale del display. L'implementazione HAL deve segnalare lo stato attuale effettivo, che potrebbe differire da quello richiesto più di recente.
La logica responsabile della modifica degli stati di visualizzazione deve esistere al di sopra del livello del dispositivo, il che rende indesiderabile che l'implementazione HAL modifichi spontaneamente gli stati di visualizzazione. Se la visualizzazione non è attualmente detenuta da alcun client (tramite una chiamata a
openDisplay), questa funzione restituisce NOT_OPEN
. In caso contrario, segnala lo stato attuale del display EVS (vedi
API IEvsDisplay).
struct CameraDesc { string camera_id; int32 vendor_flags; // Opaque value }
camera_id
. Una stringa che identifica in modo univoco una determinata videocamera. Può essere il nome del dispositivo del kernel o un nome per il dispositivo, ad esempio rearview. Il valore di questa stringa viene scelto dall'implementazione HAL e utilizzato in modo opaco dallo stack precedente.vendor_flags
. Un metodo per trasmettere informazioni specializzate sulla videocamera in modo opaco dal driver a un'app EVS personalizzata. Le informazioni vengono trasmesse dal driver all'app EVS senza interpretazione, che è libera di ignorarle.
IEvsCamera
Questo oggetto rappresenta una singola videocamera ed è l'interfaccia principale per acquisire immagini.
getCameraInfo() generates (CameraDesc info);
Restituisce CameraDesc
di questa videocamera.
setMaxFramesInFlight(int32 bufferCount) generates (EvsResult result);
Specifica la profondità della catena di buffer che la videocamera deve supportare. Il client di IEvsCamera può contenere contemporaneamente fino a questo numero di frame. Se
questi fotogrammi sono stati inviati al destinatario senza essere restituiti da
doneWithFrame
, lo stream salta i fotogrammi finché non viene restituito un buffer
per il riutilizzo. È legale che questa chiamata venga effettuata in qualsiasi momento, anche durante la riproduzione degli stream, nel qual caso i buffer devono essere aggiunti o rimossi dalla catena in modo appropriato. Se non viene effettuata alcuna chiamata a questo punto di ingresso, IEvsCamera supporta
almeno un frame per impostazione predefinita, ma ne sono accettati di più.
Se il valore bufferCount richiesto non può essere soddisfatto, la funzione restituisce
BUFFER_NOT_AVAILABLE
o un altro codice di errore pertinente. In questo caso,
il sistema continua a funzionare con il valore impostato in precedenza.
startVideoStream(IEvsCameraStream receiver) generates (EvsResult result);
Richiede la consegna dei frame della videocamera EVS da questa videocamera. IEvsCameraStream
inizia a ricevere chiamate periodiche con nuovi frame di immagini finché
non viene chiamato stopVideoStream()
. I frame devono iniziare a essere inviati
entro 500 ms dalla chiamata startVideoStream
e, dopo l'avvio, devono
essere generati a un minimo di 10 FPS. Il tempo necessario per avviare lo stream video
viene effettivamente conteggiato ai fini di qualsiasi requisito di tempo di avvio della telecamera posteriore. Se lo stream non viene avviato, deve essere restituito un codice di errore; in caso contrario, viene restituito OK.
oneway doneWithFrame(BufferDesc buffer);
Restituisce un frame fornito a IEvsCameraStream. Una volta terminato
l'utilizzo di un frame fornito all'interfaccia IEvsCameraStream, il frame deve essere
restituito a IEvsCamera per il riutilizzo. È disponibile un numero ridotto e finito di buffer (forse anche solo uno) e, se la scorta si esaurisce, non vengono inviati altri frame finché non viene restituito un buffer, il che potrebbe comportare la perdita di frame (un buffer con un handle nullo indica la fine di uno stream e non deve essere restituito tramite questa funzione). Restituisce OK in caso di esito positivo o
il codice di errore appropriato, che potrebbe includere INVALID_ARG
o
BUFFER_NOT_AVAILABLE
.
stopVideoStream();
Interrompe la pubblicazione dei frame della videocamera EVS. Poiché la consegna è asincrona,
i frame potrebbero continuare ad arrivare per un po' di tempo dopo la restituzione di questa chiamata. Ogni frame
deve essere restituito finché la chiusura dello stream non viene segnalata a
IEvsCameraStream. È legale chiamare stopVideoStream
su uno stream
che è già stato interrotto o mai avviato, nel qual caso viene ignorato.
getExtendedInfo(int32 opaqueIdentifier) generates (int32 value);
Richiede informazioni specifiche del conducente dall'implementazione HAL. I valori
consentiti per opaqueIdentifier
sono specifici del driver, ma nessun valore
passato potrebbe causare l'arresto anomalo del driver. Il driver deve restituire 0 per qualsiasi opaqueIdentifier
non riconosciuto.
setExtendedInfo(int32 opaqueIdentifier, int32 opaqueValue) generates (EvsResult result);
Invia un valore specifico per il conducente all'implementazione HAL. Questa estensione viene fornita solo per facilitare le estensioni specifiche per il veicolo e nessuna implementazione HAL dovrebbe richiedere questa chiamata per funzionare in uno stato predefinito. Se il
driver riconosce e accetta i valori, deve essere restituito OK; in caso contrario,
deve essere restituito INVALID_ARG
o un altro codice di errore rappresentativo.
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 }
Descrive un'immagine passata tramite l'API. L'unità HAL è responsabile
del completamento di questa struttura per descrivere il buffer dell'immagine e il client HAL
deve trattare questa struttura come di sola lettura. I campi contengono informazioni sufficienti
per consentire al client di ricostruire un oggetto ANativeWindowBuffer
,
come potrebbe essere necessario per utilizzare l'immagine con EGL tramite
l'estensione eglCreateImageKHR()
.
width
. La larghezza in pixel dell'immagine presentata.height
. L'altezza in pixel dell'immagine presentata.stride
. Numero di pixel effettivamente occupati da ogni riga in memoria, tenendo conto di eventuali spazi interni per l'allineamento delle righe. Espressa in pixel per corrispondere alla convenzione adottata da gralloc per le descrizioni dei buffer.pixelSize
. Numero di byte occupati da ogni singolo pixel, consentendo il calcolo delle dimensioni in byte necessarie per passare da una riga all'altra dell'immagine (stride
in byte =stride
in pixel *pixelSize
).format
. Il formato pixel utilizzato dall'immagine. Il formato fornito deve essere compatibile con l'implementazione OpenGL della piattaforma. Per superare i test di compatibilità,HAL_PIXEL_FORMAT_YCRCB_420_SP
deve essere preferito per l'utilizzo della videocamera eRGBA
oBGRA
deve essere preferito per il display.usage
. Flag di utilizzo impostati dall'implementazione HAL. I client HAL devono superare questi test senza modifiche (per i dettagli, consulta i flag correlatiGralloc.h
).bufferId
. Un valore univoco specificato dall'implementazione HAL per consentire il riconoscimento di un buffer dopo un round trip tramite le API HAL. Il valore memorizzato in questo campo può essere scelto arbitrariamente dall'implementazione HAL.memHandle
. L'handle per il buffer di memoria sottostante che contiene i dati dell'immagine. L'implementazione HAL potrebbe scegliere di memorizzare qui un handle del buffer Gralloc.
IEvsCameraStream
Il client implementa questa interfaccia per ricevere consegne asincrone di frame video.
deliverFrame(BufferDesc buffer);
Riceve chiamate dall'HAL ogni volta che un frame video è pronto per l'ispezione.
I buffer gestiti ricevuti da questo metodo devono essere restituiti tramite chiamate a
IEvsCamera::doneWithFrame()
. Quando il flusso video viene interrotto tramite una
chiamata a IEvsCamera::stopVideoStream()
, questo callback potrebbe continuare
man mano che la pipeline si svuota. Ogni frame deve comunque essere restituito; quando l'ultimo frame
nel flusso è stato consegnato, verrà consegnato un bufferHandle NULL,
che indica la fine del flusso e non si verificano ulteriori consegne di frame. Il bufferHandle NULL
non deve essere restituito tramite
doneWithFrame()
, ma tutti gli altri handle devono essere restituiti
Sebbene siano tecnicamente possibili formati buffer proprietari, i test di compatibilità richiedono che il buffer sia in uno dei quattro formati supportati: NV21 (YCrCb 4:2:0 Semi-Planar), YV12 (YCrCb 4:2:0 Planar), YUYV (YCrCb 4:2:2 Interleaved), RGBA (32 bit R:G:B:x), BGRA (32 bit B:G:R:x). Il formato selezionato deve essere una sorgente di texture GL valida nell'implementazione GLES della piattaforma.
L'app non deve basarsi su alcuna corrispondenza
tra il campo bufferId
e memHandle
nella
struttura BufferDesc
. I valori bufferId
sono
essenzialmente privati per l'implementazione del driver HAL e possono essere utilizzati (e riutilizzati)
come meglio crede.
IEvsDisplay
Questo oggetto rappresenta il display Evs, controlla lo stato del display e gestisce la presentazione effettiva delle immagini.
getDisplayInfo() generates (DisplayDesc info);
Restituisce informazioni di base sul display EVS fornito dal sistema (vedi DisplayDesc).
setDisplayState(DisplayState state) generates (EvsResult result);
Imposta lo stato del display. I client possono impostare lo stato di visualizzazione per esprimere lo stato desiderato e l'implementazione HAL deve accettare normalmente una richiesta per qualsiasi stato mentre si trova in un altro stato, anche se la risposta potrebbe essere quella di ignorare la richiesta.
All'inizializzazione, il display è definito per l'avvio nello stato
NOT_VISIBLE
, dopodiché il client deve richiedere
lo stato VISIBLE_ON_NEXT_FRAME
e iniziare a fornire video. Quando la
visualizzazione non è più necessaria, il client deve richiedere lo stato
NOT_VISIBLE
dopo aver superato l'ultimo frame del video.
Può essere richiesto in qualsiasi momento per qualsiasi stato. Se la visualizzazione è
già visibile, deve rimanere visibile se impostata su
VISIBLE_ON_NEXT_FRAME
. Restituisce sempre OK, a meno che lo stato richiesto
non sia un valore enum non riconosciuto, nel qual caso viene restituito INVALID_ARG
.
getDisplayState() generates (DisplayState state);
Recupera lo stato del display. L'implementazione HAL deve segnalare lo stato attuale effettivo, che potrebbe differire da quello richiesto più di recente. La logica responsabile della modifica degli stati del display deve esistere al di sopra del livello del dispositivo, il che rende indesiderabile che l'implementazione HAL modifichi spontaneamente gli stati del display.
getTargetBuffer() generates (handle bufferHandle);
Restituisce un handle a un frame buffer associato al display. Questo buffer
potrebbe essere bloccato e scritto da software e/o GL. Questo buffer deve essere restituito
tramite una chiamata a returnTargetBufferForDisplay()
anche se il display
non è più visibile.
Sebbene siano tecnicamente possibili formati buffer proprietari, i test di compatibilità richiedono che il buffer sia in uno dei quattro formati supportati: NV21 (YCrCb 4:2:0 Semi-Planar), YV12 (YCrCb 4:2:0 Planar), YUYV (YCrCb 4:2:2 Interleaved), RGBA (32 bit R:G:B:x), BGRA (32 bit B:G:R:x). Il formato selezionato deve essere un target di rendering GL valido nell'implementazione GLES della piattaforma.
In caso di errore, viene restituito un buffer con un handle nullo, ma tale buffer non
deve essere restituito a returnTargetBufferForDisplay
.
returnTargetBufferForDisplay(handle bufferHandle) generates (EvsResult result);
Indica al display che il buffer è pronto per la visualizzazione. Solo i buffer recuperati
tramite una chiamata a getTargetBuffer()
sono validi per l'utilizzo con questa
chiamata e i contenuti di BufferDesc
non possono essere modificati dall'app client. Dopo questa chiamata, il buffer non è più valido per l'utilizzo da parte del client. Restituisce OK in caso di esito positivo o il codice di errore appropriato, potenzialmente
incluso INVALID_ARG
o BUFFER_NOT_AVAILABLE
.
struct DisplayDesc { string display_id; int32 vendor_flags; // Opaque value }
Descrive le proprietà di base di un display EVS e richieste da un'implementazione EVS. L'HAL è responsabile del completamento di questa struttura per descrivere il display EVS. Può essere un display fisico o virtuale sovrapposto o combinato con un altro dispositivo di presentazione.
display_id
. Una stringa che identifica in modo univoco il display. Potrebbe trattarsi del nome del dispositivo del kernel o di un nome per il dispositivo, ad esempio specchietto retrovisore. Il valore di questa stringa viene scelto dall'implementazione HAL e utilizzato in modo opaco dallo stack precedente.vendor_flags
. Un metodo per trasmettere informazioni specializzate sulla videocamera in modo opaco dal driver a un'app EVS personalizzata. Le informazioni vengono trasmesse dal driver all'app EVS senza interpretazione, che è libera di ignorarle.
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 }
Descrive lo stato del display EVS, che può essere disattivato (non
visibile al conducente) o attivato (mostra un'immagine al conducente).
Include uno stato temporaneo in cui la visualizzazione non è ancora visibile, ma è pronta
a diventare visibile con la distribuzione del fotogramma successivo delle immagini tramite la
chiamata returnTargetBufferForDisplay()
.
EVS Manager
EVS Manager fornisce l'interfaccia pubblica al sistema EVS per raccogliere e presentare le visualizzazioni della videocamera esterna. Se i driver hardware consentono una sola interfaccia attiva per risorsa (videocamera o display), EVS Manager facilita l'accesso condiviso alle videocamere. Una singola app EVS principale è il primo client di EVS Manager ed è l'unico client autorizzato a scrivere dati di visualizzazione (ad altri client può essere concesso l'accesso in sola lettura alle immagini della videocamera).
EVS Manager implementa la stessa API dei driver HAL sottostanti e fornisce un servizio esteso supportando più client simultanei (più di un client può aprire una videocamera tramite EVS Manager e ricevere un flusso video).

Figura 2. EVS Manager rispecchia l'API hardware EVS sottostante.
Le app non vedono differenze quando operano tramite l'implementazione dell'HAL hardware EVS o l'API EVS Manager, tranne per il fatto che l'API EVS Manager consente l'accesso simultaneo allo stream della videocamera. EVS Manager è il client consentito del livello HAL hardware EVS e funge da proxy per l'HAL hardware EVS.
Le seguenti sezioni descrivono solo le chiamate che hanno un comportamento diverso (esteso) nell'implementazione di EVS Manager; le chiamate rimanenti sono identiche alle descrizioni HAL di EVS.
IEvsEnumerator
openCamera(string camera_id) generates (IEvsCamera camera);
Ottiene un oggetto interfaccia utilizzato per interagire con una videocamera specifica
identificata dalla stringa univoca camera_id. Restituisce NULL in caso di errore.
A livello di EVS Manager, se sono disponibili risorse di sistema sufficienti,
una videocamera già aperta può essere aperta di nuovo da un altro processo, consentendo
la coda dello stream video a più app consumer. Le stringhe
camera_id
nel livello EVS Manager sono le stesse
riportate nel livello EVS Hardware.
IEvsCamera
L'implementazione di IEvsCamera fornita da EVS Manager è virtualizzata internamente in modo che le operazioni su una videocamera da parte di un client non influiscano sugli altri client, che mantengono l'accesso indipendente alle proprie videocamere.
startVideoStream(IEvsCameraStream receiver) generates (EvsResult result);
Avvia i flussi video. I client possono avviare e interrompere in modo indipendente gli stream video sulla stessa videocamera sottostante. La videocamera sottostante si avvia quando si avvia il primo client.
doneWithFrame(uint32 frameId, handle bufferHandle) generates (EvsResult result);
Restituisce un frame. Ogni cliente deve restituire i propri occhiali quando ha finito, ma può tenerli per tutto il tempo che desidera. Quando il conteggio dei frame mantenuti da un client raggiunge il limite configurato, non riceverà altri frame finché non ne restituisce uno. Questo salto di frame non influisce sugli altri client, che continuano a ricevere tutti i frame come previsto.
stopVideoStream();
Interrompe uno stream video. Ogni client può interrompere il proprio stream video in qualsiasi momento senza influire sugli altri client. Lo stream della videocamera sottostante a livello hardware viene interrotto quando l'ultimo client di una determinata videocamera interrompe lo stream.
setExtendedInfo(int32 opaqueIdentifier, int32 opaqueValue) generates (EvsResult result);
Invia un valore specifico del driver, consentendo potenzialmente a un client di influire su un altro. Poiché EVS Manager non può comprendere le implicazioni delle parole di controllo definite dal fornitore, queste non vengono virtualizzate e tutti gli effetti collaterali si applicano a tutti i client di una determinata videocamera. Ad esempio, se un fornitore ha utilizzato questa chiamata per modificare i frame rate, tutti i client della videocamera del livello hardware interessato riceveranno i frame alla nuova velocità.
IEvsDisplay
È consentito un solo proprietario della visualizzazione, anche a livello di gestore EVS. Il gestore non aggiunge alcuna funzionalità e passa semplicemente l'interfaccia IEvsDisplay direttamente all'implementazione HAL sottostante.
App EVS
Android include un'implementazione di riferimento C++ nativa di un'app EVS che comunica con EVS Manager e l'HAL del veicolo per fornire le funzioni di base della telecamera posteriore. L'app dovrebbe avviarsi all'inizio della procedura di avvio del sistema, con un video adatto mostrato a seconda delle videocamere disponibili e dello stato dell'auto (marcia e stato degli indicatori di direzione). Gli OEM possono modificare o sostituire l'app EVS con la propria logica e presentazione specifiche per il veicolo.

Figura 3. Logica di esempio dell'app EVS, recupera l'elenco delle videocamere.

Figura 4. Logica di esempio dell'app EVS, ricevi callback del frame.
Poiché i dati dell'immagine vengono presentati all'app in un buffer grafico standard, l'app è responsabile dello spostamento dell'immagine dal buffer di origine al buffer di output. Sebbene ciò comporti il costo di una copia dei dati, offre anche l'opportunità all'app di eseguire il rendering dell'immagine nel buffer di visualizzazione nel modo che preferisce.
Ad esempio, l'app potrebbe scegliere di spostare i dati dei pixel stessi, potenzialmente con un'operazione di scalatura o rotazione in linea. L'app potrebbe anche scegliere di utilizzare l'immagine di origine come texture OpenGL e renderizzare una scena complessa nel buffer di output, inclusi elementi virtuali come icone, linee guida e animazioni. Un'app più sofisticata può anche selezionare più videocamere di input simultanee e unirle in un unico frame di output (ad esempio per l'utilizzo in una visualizzazione virtuale dall'alto dell'ambiente circostante il veicolo).
Utilizzare EGL/SurfaceFlinger nell'HAL di visualizzazione EVS
Questa sezione spiega come utilizzare EGL per eseguire il rendering di un'implementazione HAL di EVS Display in Android 10.
Un'implementazione di riferimento
dell'HAL EVS utilizza EGL per eseguire il rendering dell'anteprima della videocamera sullo schermo e utilizza libgui
per creare la superficie di rendering EGL di destinazione. In Android 8 (e versioni successive), libgui
è classificata come VNDK-private,
che si riferisce a un gruppo di librerie disponibili per le librerie VNDK che i processi del fornitore non possono utilizzare.
Poiché le implementazioni HAL devono risiedere nella partizione del fornitore, i fornitori non possono utilizzare
Surface nelle implementazioni HAL.
Creazione di libgui per i processi del fornitore
L'utilizzo di libgui
funge da unica opzione per utilizzare EGL/SurfaceFlinger
nelle implementazioni HAL di EVS Display. Il modo più semplice per implementare libgui
è
tramite
frameworks/native/libs/gui
direttamente utilizzando un target di build aggiuntivo nello script di build. Questo target è esattamente uguale al target libgui
, tranne che per l'aggiunta di due campi:
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", ], …
Nota: i target fornitore vengono creati con la macro NO_INPUT
, che rimuove una parola a 32 bit dai dati del pacco. Poiché SurfaceFlinger si aspetta questo campo che è stato rimosso, non riesce ad analizzare il pacco. Questo viene osservato come un errore 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
Per risolvere questa condizione:
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);
Di seguito sono riportate le istruzioni
di compilazione di esempio. Riceverai un
$(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
Utilizzare Binder in un'implementazione EVS HAL
In Android 8 (e versioni successive), il nodo del dispositivo /dev/binder
è diventato esclusivo per i processi del framework e, pertanto, inaccessibile ai processi del fornitore. Invece, i processi del fornitore devono utilizzare /dev/hwbinder
e devono convertire qualsiasi interfaccia AIDL in HIDL. Per chi vuole continuare a utilizzare le interfacce AIDL tra i processi del fornitore,
utilizza il dominio binder, /dev/vndbinder
.
Dominio IPC | Descrizione |
---|---|
/dev/binder |
IPC tra processi di framework/app con interfacce AIDL |
/dev/hwbinder |
IPC tra processi framework/fornitore con interfacce HIDL IPC tra processi fornitore con interfacce HIDL |
/dev/vndbinder |
IPC tra processi del fornitore con interfacce AIDL |
Mentre SurfaceFlinger definisce le interfacce AIDL, i processi del fornitore possono utilizzare solo le interfacce HIDL per
comunicare con i processi del framework. Per convertire le interfacce AIDL esistenti in HIDL è necessario un lavoro non banale. Fortunatamente, Android fornisce un metodo per selezionare il driver binder per libbinder
, a cui sono collegati i processi della libreria userspace.
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));
Nota: i processi del fornitore devono chiamare questo numero prima di chiamare
Process
o IPCThreadState
o prima di effettuare chiamate di binder.
Policy SELinux
Se l'implementazione del dispositivo è full treble, SELinux impedisce ai processi del fornitore di utilizzare /dev/binder
. Ad esempio, un'implementazione di esempio di EVS HAL
è assegnata al dominio hal_evs_driver
e richiede
autorizzazioni di lettura/scrittura per il dominio 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
L'aggiunta di queste autorizzazioni, tuttavia, causa un errore di compilazione perché viola le seguenti
regole neverallow definite in system/sepolicy/domain.te
per un dispositivo full-treble.
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
è un attributo fornito per rilevare un bug e guidare lo sviluppo. Può essere utilizzato anche per
risolvere la violazione di Android 10 descritta sopra.
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)
Creare un'implementazione di riferimento HAL EVS come procedura del fornitore
Come riferimento, puoi applicare le seguenti modifiche a
packages/services/Car/evs/Android.mk
. Assicurati di confermare che
tutte le modifiche descritte funzionino per la tua implementazione.
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;