Android contiene un HAL (Hardware Abstraction Layer) HIDL per auto e motori che consente di acquisire e visualizzare le immagini molto all'inizio del processo 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 in genere è utilizzato per supportare display con visione posteriore e surround nei veicoli con sistemi di infotainment per veicoli (IVI) basati su Android. EVS consente inoltre di implementare funzionalità avanzate nelle app utente.
Android include anche un'interfaccia del driver di acquisizione e visualizzazione specifica per EVS (in /hardware/interfaces/automotive/evs/1.0
). Sebbene sia possibile creare un'app per la videocamera di retromarcia in base ai servizi di visualizzazione e videocamera Android esistenti, è probabile che un'app del genere venga eseguita troppo tardi nella procedura di avvio di Android. L'uso di un HAL dedicato consente un'interfaccia semplificata
e chiarisce quali sono le attività che un OEM deve implementare per supportare lo stack EVS.
Componenti di sistema
EVS include i seguenti componenti di sistema:
Figura 1. Panoramica dei componenti del sistema EVS.
App EVS
Un'app C++ EVS di esempio
(/packages/services/Car/evs/app
) funge da implementazione
di riferimento. Questa app è responsabile della richiesta dei frame video dall'EVS Manager e dell'invio dei frame finali per la visualizzazione all'EVS Manager.
Dovrebbe essere avviato da init non appena EVS e Car Service sono disponibili,
in modo mirato entro due (2) secondi dall'accensione. Gli OEM possono modificare o sostituire l'app EVS come preferiscono.
Responsabile EVS
EVS Manager (/packages/services/Car/evs/manager
) fornisce gli elementi di base necessari a un'app EVS per implementare qualsiasi cosa, da un semplice display della videocamera di retrovisione 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 altri servizi (in particolare il servizio Car) possono eseguire query sullo stato di EVSManager per scoprire quando il sistema EVS è attivo.
Interfaccia HIDL EVS
Il sistema EVS, sia la videocamera sia gli elementi di visualizzazione, è definito nel
android.hardware.automotive.evs
pacchetto. In /hardware/interfaces/automotive/evs/1.0/default
è fornita un'implementazione di esempio che esegue l'interfaccia (genera immagini di test sintetiche e convalida il percorso di andata e ritorno delle immagini).
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 fotocamere fisiche e della loro elaborazione tramite buffer di memoria condivisa riconoscibili da Gralloc. Il lato del display
dell'implementazione è responsabile di fornire un buffer della memoria condivisa
che può essere riempito dall'app (in genere tramite il rendering EGL) e di presentare
i frame finali in preferenza a qualsiasi altro elemento che potrebbe essere visualizzato sul
display fisico. Le implementazioni dell'interfaccia di EVS da parte dei fornitori possono essere memorizzate
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
i driver hardware della fotocamera e/o del display esistenti. Il riutilizzo dei driver potrebbe essere vantaggioso, in particolare 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 della versione 4l2 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 proprio 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. È
supponiamo che l'insieme di videocamere sia fisso e conoscibile al momento dell'avvio. Per maggiori dettagli sulle descrizioni delle fotocamere, vedi CameraDesc
.
openCamera(string camera_id) generates (IEvsCamera camera);
Recupera un oggetto interfaccia utilizzato per interagire con una videocamera specifica identificata dalla stringa univoca camera_id. Restituisce un valore NULL in caso di errore.
I tentativi di riaprire una videocamera già aperta non possono non riuscire. Per evitare condizioni di gara
associate all'avvio e all'arresto dell'app, la riapertura di una videocamera
dovrebbe arrestare l'istanza precedente in modo da soddisfare la nuova richiesta. Un'istanza della videocamera che è stata anticipata in questo modo deve essere messa in uno stato inattivo, in attesa della distruzione finale e deve rispondere a qualsiasi richiesta di modifica dello stato della videocamera con un codice di ritorno OWNERSHIP_LOST
.
closeCamera(IEvsCamera camera);
Rilascia l'interfaccia IEvsCamera (ed è l'opposto della chiamata
openCamera()
). Lo stream video della videocamera deve essere stopped chiamando stopVideoStream()
prima di chiamare closeCamera
.
openDisplay() generates (IEvsDisplay display);
Ottieni un oggetto interfaccia utilizzato per interagire esclusivamente con il display Evs del sistema. Al momento, può esistere un solo client con un'istanza funzionante di IEvsDisplay. Analogamente al comportamento di apertura aggressivo descritto in openCamera
,
è possibile creare in qualsiasi momento un nuovo oggetto IEvsDisplay che disattiverà eventuali istanze precedenti. Le istanze non convalidate continuano a esistere e a rispondere alle chiamate di funzione
da parte dei relativi proprietari, ma non devono eseguire operazioni di mutazione quando non sono attive. Alla fine,
l'app client dovrebbe rilevare i codici di ritorno
dell'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 chiamate getTargetBuffer()
devono essere restituiti al display prima di chiuderlo.
getDisplayState() generates (DisplayState state);
Recupera lo stato di visualizzazione corrente. L'implementazione HAL deve segnalare lo stato corrente effettivo, che potrebbe essere diverso dallo stato richiesto più di recente.
La logica responsabile della modifica degli stati di visualizzazione deve esistere sopra il livello del dispositivo, rendendo indesiderabile che l'implementazione dell'HAL modifichi spontaneamente gli stati di visualizzazione. Se al momento il display non è in uso da nessun 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 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 in modo opaco informazioni specializzate della fotocamera dal conducente a un'app EVS personalizzata. Vengono trasmesse non interpretate dal conducente fino all'app EVS, che è libera di ignorarle.
IEvsCamera
Questo oggetto rappresenta una singola fotocamera ed è l'interfaccia principale per l'acquisizione di immagini.
getCameraInfo() generates (CameraDesc info);
Restituisce CameraDesc
di questa fotocamera.
setMaxFramesInFlight(int32 bufferCount) generates (EvsResult result);
Specifica la profondità della catena di buffer che la videocamera deve supportare. Il client di IEvsCamera può gestire contemporaneamente fino a questo numero di frame. Se questi
numerosi frame sono stati consegnati al ricevitore senza essere restituiti da
doneWithFrame
, il flusso salta i frame fino a quando non viene restituito un buffer
per il riutilizzo. È consentito che questa chiamata venga inviata in qualsiasi momento, anche quando gli stream sono già in esecuzione, nel qual caso i buffer devono essere aggiunti o rimossi dalla catena come appropriato. Se non viene effettuata alcuna chiamata a questo punto di ingresso, IEvsCamera supporta
per impostazione predefinita almeno un fotogramma, con un numero maggiore di immagini accettabile.
Se non è possibile soddisfare il valore bufferCount richiesto, la funzione restituisce BUFFER_NOT_AVAILABLE
o un altro codice di errore pertinente. In questo caso, il sistema continuerà a funzionare con il valore impostato in precedenza.
startVideoStream(IEvsCameraStream receiver) generates (EvsResult result);
Richiede l'invio di frame della videocamera EVS da questa videocamera. IEvsCameraStream
inizia a ricevere chiamate periodiche con nuovi frame immagine 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 una frequenza minima di 10 FPS. Il tempo necessario per avviare lo stream video viene conteggiato ai fini del rispetto di eventuali requisiti relativi al tempo di avvio della videocamera di retrovisione. Se il flusso non viene avviato, è necessario restituire un codice di errore, altrimenti viene restituito OK.
oneway doneWithFrame(BufferDesc buffer);
Restituisce un frame inviato a IEvsCameraStream. Al termine del consumo di un frame inviato all'interfaccia IEvsCameraStream, il frame deve essere restituito a IEvsCamera per il riutilizzo. È disponibile un numero limitato di buffer (possibilmente anche uno solo) e, se l'offerta è esaurita, non vengono inviati altri frame finché non viene restituito un buffer, con il rischio di saltare frame (un buffer con un handle nullo indica la fine di uno stream e non deve essere restituito tramite questa funzione). Restituisce "OK" se l'operazione è andata a buon fine o
un codice di errore appropriato che potrebbe includere INVALID_ARG
o
BUFFER_NOT_AVAILABLE
.
stopVideoStream();
Interrompe l'invio dei frame della videocamera EVS. Poiché l'invio è asincrono,
i frame potrebbero continuare ad arrivare per un po' di tempo dopo il ritorno di questa chiamata. Ogni frame deve essere restituito finché la chiusura dello stream non viene segnalata a IEvsCameraStream. È consentito chiamare stopVideoStream
su uno stream
che è già stato interrotto o non è mai stato avviato, in tal caso viene ignorato.
getExtendedInfo(int32 opaqueIdentifier) generates (int32 value);
Richiede informazioni specifiche sul driver dall'implementazione HAL. I valori consentiti per opaqueIdentifier
sono specifici del driver, ma se non viene passato alcun valore, il driver potrebbe arrestarsi in modo anomalo. Il conducente deve restituire 0 per ogni opaqueIdentifier
non riconosciuto.
setExtendedInfo(int32 opaqueIdentifier, int32 opaqueValue) generates (EvsResult result);
Invia un valore specifico per il driver all'implementazione HAL. Questa estensione viene fornita solo per facilitare estensioni specifiche per il veicolo e nessuna implementazione HAL deve richiedere che questa chiamata funzioni in uno stato predefinito. Se il driver riconosce e accetta i valori, deve essere restituito il valore 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. Il drive HAL è responsabile di compilare questa struttura per descrivere il buffer di immagini e il client HAL deve trattarla 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
. Il numero di pixel che ogni riga occupa effettivamente in memoria, che tiene conto dell'eventuale spaziatura interna per l'allineamento delle righe. Espresso in pixel per corrispondere alla convenzione adottata da gralloc per le descrizioni del buffer.pixelSize
. Numero di byte occupati da ogni singolo pixel, che consente di calcolare le dimensioni in byte necessarie per passare da una riga all'altra nell'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à, è preferibile utilizzareHAL_PIXEL_FORMAT_YCRCB_420_SP
per l'utilizzo della fotocamera eRGBA
oBGRA
per il display.usage
. Flag di utilizzo impostati dall'implementazione HAL. I client HAL devono trasmetterli invariati (per maggiori dettagli, consulta i flag correlatiGralloc.h
).bufferId
. Un valore univoco specificato dall'implementazione HAL per consentire il riconoscimento di un buffer dopo un viaggio di andata e ritorno tramite le API HAL. Il valore memorizzato in questo campo può essere scelto arbitrariamente dall'implementazione dell'HAL.memHandle
. L'handle per il buffer della 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 caricamenti asincroni dei frame video.
deliverFrame(BufferDesc buffer);
Riceve chiamate dall'HAL ogni volta che un frame video è pronto per l'ispezione.
Gli handle dei buffer ricevuti da questo metodo devono essere restituiti tramite chiamate a
IEvsCamera::doneWithFrame()
. Quando lo stream 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 viene inviato l'ultimo frame
nello stream, viene inviato un bufferHandle NULL,
che indica la fine dello stream e non vengono inviate ulteriori invii di frame. Il bufferHandle NULL non deve essere inviato tramite doneWithFrame()
, ma tutti gli altri handle devono essere restituiti
Sebbene i formati di buffer proprietari siano tecnicamente possibili, i test di compatibilità richiedono che il buffer sia in uno dei quattro formati supportati: NV21 (YCrCb 4:2:0 semiplanare), YV12 (YCrCb 4:2:0 planare), YUYV (YCrCb 4:2:2 interlacciato), RGBA (32 bit R:G:B:x), BGRA (32 bit B:G:R:x). Il formato selezionato deve essere un'origine texture GL valida nell'implementazione GLES della piattaforma.
L'app non deve fare affidamento 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 EV, 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 di visualizzazione. I client possono impostare lo stato di visualizzazione per esprimere lo stato preferito e l'implementazione HAL deve accettare in modo corretto una richiesta per qualsiasi stato mentre si trova in qualsiasi altro stato, anche se la risposta può essere l'ignorare della richiesta.
Dopo l'inizializzazione, la visualizzazione deve iniziare nello stato NOT_VISIBLE
, dopodiché il client dovrà richiedere lo stato VISIBLE_ON_NEXT_FRAME
e iniziare a fornire il video. Quando la visualizzazione non è più richiesta, il client deve richiedere lo stato NOT_VISIBLE
dopo aver trasmesso l'ultimo frame video.
È valido per qualsiasi stato da richiedere in qualsiasi momento. Se il display è già visibile, dovrebbe rimanere visibile se è impostato su VISIBLE_ON_NEXT_FRAME
. Restituisce sempre OK, a meno che lo stato richiesto
non sia un valore enumerato 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 corrente effettivo, che potrebbe essere diverso da quello richiesto più di recente. La logica responsabile della modifica degli stati di visualizzazione deve esistere sopra il livello del dispositivo, rendendo indesiderabile che l'implementazione dell'HAL modifichi spontaneamente gli stati di visualizzazione.
getTargetBuffer() generates (handle bufferHandle);
Restituisce un handle a un frame buffer associato al display. Questo buffer può essere bloccato e scritto dal software e/o dalla GL. Questo buffer deve essere restituito
tramite una chiamata a returnTargetBufferForDisplay()
anche se il display
non è più visibile.
Sebbene i formati di buffer proprietari siano tecnicamente possibili, i test di compatibilità richiedono che il buffer sia in uno dei quattro formati supportati: NV21 (YCrCb 4:2:0 semiplanar), YV12 (YCrCb 4:2:0 planare), YUYV (YCrCb 4:2:2 con interfoliazione), RGBA: BGRA: bit: BGRA: bit RGBA: 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 null, ma questo buffer non deve essere passato nuovamente 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 se l'operazione è andata a buon fine o codice di errore appropriato che potrebbe includere 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 della compilazione di questa struttura per descrivere il display EVS. Può essere un display fisico o virtuale sovrapposto o misto a un altro dispositivo per la presentazione.
display_id
. Una stringa che identifica in modo univoco la visualizzazione. Potrebbe trattarsi del nome del dispositivo del kernel o di un nome per il dispositivo, come 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 sulla videocamera specializzate in modo opaco dal driver a un'app EVS personalizzata. Viene trasmesso senza interpretazione dal driver all'app EVS, che è libera di ignorarlo.
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 transitorio in cui il display non è ancora visibile, ma è pronto
a diventare visibile con l'invio 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 delle videocamere esterne. Se i driver hardware consentono solo un'interfaccia attiva per risorsa (videocamera o display), EVS Manager facilita l'accesso condiviso alle videocamere. Un'unica app EVS principale è il primo client di EVS Manager ed è l'unico client autorizzato a scrivere i dati di visualizzazione (ad altri client può essere concesso l'accesso di sola lettura alle immagini della fotocamera).
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 uno stream video).
Le app non notano 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 è l'unico client consentito del livello HAL hardware EVS e agisce come proxy per l'HAL hardware EVS.
Le sezioni seguenti descrivono solo le chiamate con un comportamento diverso (esteso) nell'implementazione di EVS Manager. Le chiamate rimanenti sono identiche alle descrizioni HAL EVS.
IEvsEnumerator
openCamera(string camera_id) generates (IEvsCamera camera);
Restituisce un oggetto dell'interfaccia utilizzato per interagire con una fotocamera specifica
identificata dalla stringa univoca camera_id. Restituisce un valore NULL in caso di errore.
Al livello EVS Manager, purché siano disponibili risorse di sistema sufficienti,
una videocamera già aperta può essere aperta di nuovo da un altro processo, consentendo
il teaeing del video stream su più app consumer. Le
stringhe camera_id
nel livello EVS Manager sono le stesse
riportate al livello EVS Hardware.
VideocameraIEvs
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 di base. La videocamera di base si avvia all'avvio del primo client.
doneWithFrame(uint32 frameId, handle bufferHandle) generates (EvsResult result);
Restituisce un frame. Ogni cliente deve restituire i frame al termine, ma può trattenerli per tutto il tempo che vuole. Quando il conteggio frame di un client raggiunge il limite configurato, non riceverà altri frame finché non ne restituisce uno. Questo salto dei 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 di hardware viene interrotto quando l'ultimo client di una determinata videocamera interrompe il proprio stream.
setExtendedInfo(int32 opaqueIdentifier, int32 opaqueValue) generates (EvsResult result);
Invia un valore specifico per il driver, potenzialmente consentendo a un cliente di influire su un altro cliente. Poiché EVS Manager non è in grado di comprendere le implicazioni delle parole di controllo predefinite dal fornitore, queste non vengono virtualizzate e eventuali effetti collaterali si applicano a tutti i client di una determinata videocamera. Ad esempio, se un fornitore utilizzasse questa chiamata per modificare le frequenze dei fotogrammi, tutti i client della videocamera del livello hardware interessato riceverebbero i fotogrammi alla nuova frequenza.
IEvsDisplay
È consentito un solo proprietario della visualizzazione, anche a livello di gestore EVS. Manager non aggiunge alcuna funzionalità e trasmette 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 fotocamera posteriore. L'app dovrebbe avviarsi molto all'inizio del processo di avvio del sistema, mostrando un video adatto a seconda delle videocamere disponibili e dello stato dell'auto (cambio e indicatori di direzione). Gli OEM possono modificare o sostituire l'app EVS con la logica e la presentazione specifiche del veicolo.
Figura 3. Logica di esempio dell'app EVS, recupera l'elenco delle videocamere.
Figura 4. Logica di esempio dell'app EVS, ricevi callback frame.
Poiché i dati delle immagini vengono presentati all'app in un buffer di grafica standard, è compito dell'app spostare l'immagine dal buffer di origine al buffer di output. Sebbene questo introduca il costo di una copia dei dati, offre anche all'app l'opportunità di eseguire il rendering dell'immagine nel buffer di visualizzazione in qualsiasi modo desideri.
Ad esempio, l'app potrebbe scegliere di spostare i dati dei pixel stessi, eventualmente con un'operazione di scala o rotazione in linea. L'app potrebbe anche scegliere di utilizzare l'immagine di origine come texture OpenGL e di eseguire il rendering di 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 nel singolo fotogramma di output (ad esempio per l'utilizzo in una vista virtuale dall'alto verso il basso dell'ambiente circostante del veicolo).
Utilizza EGL/SurfaceFlinger nell'HAL del display EVS
Questa sezione spiega come utilizzare EGL per eseguire il rendering di un'implementazione HAL per display EVS in Android 10.
Un'implementazione di riferimento HAL EVS utilizza EGL per eseguire il rendering dell'anteprima della fotocamera sullo schermo e libgui
per creare la superficie di rendering EGL di destinazione. In Android 8 (e versioni successive), libgui
è classificato 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 dei fornitori
L'utilizzo di libgui
è l'unica opzione per utilizzare EGL/SurfaceFlinger nelle implementazioni HAL Display EVS. Il modo più semplice per implementare libgui
è tramite frameworks/native/libs/gui direttamente utilizzando una destinazione di build aggiuntiva nello script di build. Questo target è esattamente uguale al
target libgui
, tranne 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 dei fornitori vengono creati con la macro NO_INPUT
, che rimuove una parola di 32 bit dai dati del pacchetto. Poiché SurfaceFlinger prevede che questo campo sia stato rimosso, SurfaceFlinger non riesce ad analizzare il lotto. Viene rilevato 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 istruzioni di compilazione di esempio. Dovresti ricevere 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 il binder in un'implementazione HAL EVS
In Android 8 (e versioni successive), il nodo del dispositivo /dev/binder
è diventato esclusivo per i processi del framework e, di conseguenza, non è più accessibile ai processi del fornitore. Invece,
i processi del fornitore devono utilizzare /dev/hwbinder
e devono convertire eventuali interfacce AIDL
in HIDL. Per continuare a utilizzare le interfacce AIDL tra i processi del fornitore, utilizza il dominio binder /dev/vndbinder
.
Dominio IPC | Descrizione |
---|---|
/dev/binder |
IPC tra framework/processi dell'app con interfacce AIDL |
/dev/hwbinder |
IPC tra i processi del framework/del fornitore con interfacce HIDL IPC tra i processi del fornitore con interfacce HIDL |
/dev/vndbinder |
IPC tra processi del fornitore/del fornitore con interfacce AIDL |
Sebbene SurfaceFlinger definisce le interfacce AIDL, i processi del fornitore possono utilizzare solo le interfacce HIDL per comunicare con i processi del framework. È necessaria una quantità non trascurabile di lavoro per convertire le interfacce AIDL esistenti in HIDL. Fortunatamente, Android fornisce un metodo per selezionare il driver del binder per libbinder
, a cui sono collegati i processi della libreria dello spazio utente.
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: le procedure del fornitore devono chiamare questo metodo prima di chiamare Process
o IPCThreadState
o prima di effettuare chiamate al binder.
Criteri SELinux
Se l'implementazione del dispositivo è completa, SELinux impedisce ai processi del fornitore di utilizzare /dev/binder
. Ad esempio, un'implementazione di esempio dell'HAL EVS è 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 EVS HAL come processo del fornitore
Come riferimento, puoi applicare le seguenti modifiche a
packages/services/Car/evs/Android.mk
. Assicurati di verificare 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;