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 visualizzazione esterna (EVS) e viene in genere utilizzato per supportare la videocamera di retromarcia e i display della visualizzazione panoramica nei veicoli con sistemi In-Vehicle Infotainment (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'utilizzo di un HAL dedicato consente un'interfaccia semplificata
e chiarisce cosa deve implementare un OEM per supportare lo stack EVS.
Componenti di sistema
EVS include i seguenti componenti di sistema:
App EVS
Un'app EVS C++ 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 viene 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 nei file .hal in /hardware/interfaces/automotive/evs
. Queste implementazioni sono responsabili della configurazione e della raccolta dei dati dalle fotocamere fisiche e del loro invio 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 (di solito con 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 EVS dei fornitori 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 dall'EVS tramite i driver hardware esistenti della videocamera o del display. 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 di v4l2 e da SurfaceFlinger per la presentazione dell'immagine di output.
Descrizione dell'interfaccia hardware EVS
La sezione descrive l'HAL. I fornitori dovrebbero 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. È
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 che la nuova richiesta possa essere soddisfatta. 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);
Recupera 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 disabilita 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 con 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 corrente effettivo, che potrebbe essere diverso dallo stato 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 sconsigliato l'implementazione dell'HAL modificare spontaneamente gli stati di visualizzazione. Se al momento la visualizzazione non è trattenuta da alcun client (da una chiamata a openDisplay), questa funzione restituisce NOT_OPEN
. In caso contrario, segnala lo stato corrente 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. Fino a questo numero di frame possono essere conservati contemporaneamente dal client di IEvsCamera. 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 contatto, IEvsCamera supporta almeno un frame per impostazione predefinita; con più 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 continua a funzionare con il valore impostato in precedenza.
startVideoStream(IEvsCameraStream receiver) generates (EvsResult result);
Richiedi la consegna dei fotogrammi delle fotocamere EVS di questa videocamera. IEvsCameraStream inizia a ricevere chiamate periodiche con nuovi frame di immagini fino a quando non viene chiamata 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 lo stream non è stato avviato, deve essere restituito un codice di errore; in caso contrario, viene restituito il valore OK.
oneway doneWithFrame(BufferDesc buffer);
Restituisce un frame che è stato pubblicato da 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);
Consente di richiedere informazioni specifiche sul conducente dall'implementazione dell'HAL. I valori consentiti per opaqueIdentifier
sono specifici del driver, ma nessun valore superato potrebbe causare l'arresto anomalo del driver. Il conducente deve restituire 0 per ogni opaqueIdentifier
non riconosciuto.
setExtendedInfo(int32 opaqueIdentifier, int32 opaqueValue) generates (EvsResult result);
Invia un valore specifico del driver all'implementazione dell'HAL. Questa estensione viene fornita solo per facilitare le estensioni specifiche del veicolo e nessuna implementazione HAL dovrebbe richiedere il funzionamento di questa chiamata 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 con 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 in memoria da ogni riga, tenendo conto di eventuali spaziature interne per l'allineamento delle righe. Espresso in pixel in modo da corrispondere alla convenzione adottata da gralloc per le descrizioni dei 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 dell'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 dell'HAL potrebbe scegliere di archiviare 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 con 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 dello stream, viene inviato un bufferHandle
NULL, che indica la fine dello stream e l'interruzione dell'invio di frame. Il valore NULLbufferHandle
non deve essere inviato condoneWithFrame()
, 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 ritiene opportuno.
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 del display. 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ù necessaria, il client dovrebbe richiedere lo
stato NOT_VISIBLE
dopo aver passato l'ultimo frame video.
È valida 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 enum non riconosciuto, nel qual caso INVALID_ARG
viene
restituito.
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 buffer di frame associato al display. Questo buffer può essere bloccato e scritto dal software e/o dalla GL. Questo buffer deve essere restituito con 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 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 della compilazione 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 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 la visualizzazione non è ancora visibile, ma è preparata per diventare visibile con la pubblicazione del frame di immagini successivo con 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 una sola interfaccia attiva per risorsa (videocamera o display), il gestore EVS facilita l'accesso condiviso alle videocamere. Una singola app EVS principale è il primo client di EVS Manager ed è l'unico client autorizzato a scrivere i dati di visualizzazione (a client aggiuntivi può essere concesso l'accesso di sola lettura alle immagini della videocamera).
EVS Manager implementa la stessa API dei driver HAL sottostanti e fornisce un servizio ampliato 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 è di per sé l'unico client consentito del livello EVS Hardware HAL e agisce da proxy per l'EVS Hardware HAL.
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);
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.
A livello di EVS Manager, se sono disponibili risorse di sistema sufficienti,
una videocamera già aperta può essere riaperta da un altro processo, consentendo
di suddividere lo stream video in più app per i consumatori. Le stringhe camera_id
a livello di EVS Manager sono le stesse riportate al livello di hardware EVS.
IEvsCamera
EVS Manager forniva che l'implementazione di IEvsCamera fosse virtualizzata internamente, in modo che le operazioni su una videocamera da un client non influiscano su altri client, che mantengono l'accesso indipendente alle loro 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. La possibilità di ignorare i 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. Il gestore non aggiunge alcuna funzionalità e passa semplicemente l'interfaccia IEvsDisplay all'implementazione HAL sottostante.
App EVS
Android include un'implementazione di riferimento nativa in C++ di un'app EVS che comunica con il gestore EVS e l'HAL del veicolo per fornire funzioni di base della videocamera di retrovisione. 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 propria logica e presentazione specifica per il veicolo.
Poiché i dati 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 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 può scegliere di spostare i dati dei pixel, potenzialmente 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 contemporaneamente e unificarle nell'unico frame di output (ad esempio per l'utilizzo in una visualizzazione 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,
ovvero 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 si aspetta questo campo che è stato rimosso, non riesce ad analizzare il pacchetto. 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 nell'implementazione dell'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 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 i processi del framework/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 l'implementazione di riferimento HAL EVS 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;