Telecamera per veicoli HAL

Android contiene un HAL (Hardware Abstraction Layer) HIDL automobilistico che consente l'acquisizione e la visualizzazione di immagini molto presto nel processo di avvio di Android e continua a funzionare per tutta la vita del sistema. L'HAL include lo stack del sistema di visione esterna (EVS) e viene generalmente utilizzato per supportare la telecamera per la retromarcia e i display per la visione circostante nei veicoli con sistemi IVI (In-Vehicle Infotainment) basati su Android. L'EVS consente inoltre di implementare funzionalità avanzate nelle applicazioni utente.

Android include anche un'interfaccia di acquisizione e driver di visualizzazione specifica per EVS (in /hardware/interfaces/automotive/evs/1.0 ). Sebbene sia possibile creare un'applicazione per telecamera per la retromarcia sopra la fotocamera Android esistente e i servizi di visualizzazione, tale applicazione probabilmente verrebbe eseguita troppo tardi nel processo 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 del sistema

L'EVS comprende i seguenti componenti del sistema:

Schema dei componenti del sistema SVE
Figura 1. Panoramica dei componenti del sistema SVE

Applicazione SVE

Un'applicazione EVS C++ di esempio ( /packages/services/Car/evs/app ) funge da implementazione di riferimento. Questa applicazione è responsabile della richiesta di fotogrammi video dall'EVS Manager e dell'invio di fotogrammi finiti per la visualizzazione all'EVS Manager. Si prevede che venga avviato da init non appena EVS e Car Service saranno disponibili, entro due (2) secondi dall'accensione. Gli OEM possono modificare o sostituire l'applicazione EVS come desiderato.

Responsabile SVE

Il Gestore EVS ( /packages/services/Car/evs/manager ) fornisce gli elementi costitutivi necessari a un'applicazione EVS per implementare qualsiasi cosa, da un semplice display di una telecamera per la retromarcia a un rendering multi-camera 6DOF. La sua interfaccia è presentata tramite HIDL ed è progettata per accettare più client simultanei. Altre applicazioni e servizi (nello specifico il Car Service) possono interrogare lo stato dell'EVS Manager per sapere quando il sistema EVS è attivo.

Interfaccia EVS HIDL

Il sistema EVS, sia la fotocamera 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 le immagini durante il viaggio di andata e ritorno) è 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 . Tali implementazioni sono responsabili della configurazione e della raccolta dei dati dalle telecamere fisiche e della loro distribuzione tramite buffer di memoria condivisa riconoscibili da Gralloc. Il lato display dell'implementazione è responsabile di fornire un buffer di memoria condivisa che può essere riempito dall'applicazione (solitamente tramite rendering EGL) e di presentare i fotogrammi finiti preferibilmente rispetto 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 esempio, /hardware/[vendor]/[platform]/evs ).

Driver del kernel

Un dispositivo che supporta lo stack EVS richiede i driver del kernel. Invece di creare nuovi driver, gli OEM hanno la possibilità di supportare le funzionalità richieste da EVS tramite driver hardware esistenti per fotocamere e/o display. Il riutilizzo dei driver potrebbe essere vantaggioso, soprattutto per i driver video 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 v4l2 e da SurfaceFlinger per presentare l'immagine di output.

Descrizione dell'interfaccia hardware EVS

La sezione descrive l'HAL. Ci si aspetta che i fornitori forniscano implementazioni di questa API adattate al loro hardware.

IEvsEnumerator

Questo oggetto ha il compito di enumerare l'hardware EVS disponibile nel sistema (una o più telecamere e il singolo dispositivo di visualizzazione).

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

Restituisce un vettore contenente le descrizioni per tutte le telecamere nel sistema. Si presuppone che il set di telecamere sia fisso e riconoscibile al momento dell'avvio. Per dettagli sulle descrizioni delle telecamere, vedere CameraDesc .

openCamera(string camera_id) generates (IEvsCamera camera);

Ottiene un oggetto interfaccia utilizzato per interagire con una telecamera specifica identificata dalla stringa univoca camera_id . Restituisce un NULL in caso di fallimento. I tentativi di riaprire una fotocamera già aperta non possono fallire. Per evitare condizioni di competizione associate all'avvio e all'arresto dell'applicazione, la riapertura di una fotocamera dovrebbe arrestare l'istanza precedente in modo che la nuova richiesta possa essere soddisfatta. Un'istanza della telecamera che è stata sottoposta a prelazione in questo modo deve essere messa in uno stato inattivo, in attesa della distruzione finale e rispondendo a qualsiasi richiesta di influenzare lo stato della telecamera con un codice di ritorno OWNERSHIP_LOST .

closeCamera(IEvsCamera camera);

Rilascia l'interfaccia IEvsCamera (ed è l'opposto della chiamata openCamera() ). Il flusso video della telecamera deve essere interrotto chiamando stopVideoStream() prima di chiamare closeCamera .

openDisplay() generates (IEvsDisplay display);

Ottiene un oggetto interfaccia utilizzato per interagire esclusivamente con il display EVS del sistema. Solo un client alla volta può contenere un'istanza funzionale di IEvsDisplay. Similmente al comportamento di apertura aggressivo descritto in openCamera , un nuovo oggetto IEvsDisplay può essere creato in qualsiasi momento e disabiliterà qualsiasi istanza precedente. Le istanze invalidate continuano ad esistere e rispondono alle chiamate di funzione dei loro proprietari, ma non devono eseguire operazioni di mutazione quando sono morte. Alla fine, è previsto che l'applicazione client noti i codici restituiti di errore OWNERSHIP_LOST e chiuda e rilasci 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 chiudere il display.

getDisplayState() generates (DisplayState state);

Ottiene lo stato di visualizzazione corrente. L'implementazione dell'HAL dovrebbe riportare lo stato corrente effettivo, che potrebbe differire dallo stato richiesto più recentemente. La logica responsabile della modifica degli stati di visualizzazione dovrebbe esistere al di sopra del livello del dispositivo, rendendo indesiderabile che l'implementazione HAL modifichi spontaneamente gli stati di visualizzazione. Se il display non è attualmente mantenuto da alcun client (tramite una chiamata a openDisplay), questa funzione restituisce NOT_OPEN . Altrimenti riporta lo stato attuale dell'EVS Display (vedi IEvsDisplay API ).

struct CameraDesc {
    string      camera_id;
    int32       vendor_flags;       // Opaque value
}
  • camera_id . Una stringa che identifica in modo univoco una determinata fotocamera. Può essere il nome del kernel del dispositivo o un nome per il dispositivo, ad esempio Rearview . Il valore per questa stringa viene scelto dall'implementazione HAL e utilizzato in modo opaco dallo stack precedente.
  • vendor_flags . Un metodo per trasmettere in modo opaco le informazioni specializzate della telecamera dal conducente a un'applicazione EVS personalizzata. Viene passato senza essere interpretato dal conducente fino all'applicazione EVS, che è libera di ignorarlo.

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 telecamera deve supportare. Fino a questo numero di fotogrammi possono essere conservati contemporaneamente dal client di IEvsCamera. Se questo numero di frame è stato consegnato al ricevitore senza essere restituito da doneWithFrame , il flusso salta i frame finché non viene restituito un buffer per il riutilizzo. È legale che questa chiamata arrivi in ​​qualsiasi momento, anche mentre i flussi sono già in esecuzione, nel qual caso i buffer dovrebbero essere aggiunti o rimossi dalla catena a seconda dei casi. Se non viene effettuata alcuna chiamata a questo punto di ingresso, IEvsCamera supporta almeno un fotogramma per impostazione predefinita; con più accettabile.

Se il bufferCount richiesto non può essere soddisfatto, la funzione restituisce BUFFER_NOT_AVAILABLE o un altro codice di errore rilevante. In questo caso il sistema continua a funzionare con il valore precedentemente impostato.

startVideoStream(IEvsCameraStream receiver) generates (EvsResult result);

Richiede la consegna dei fotogrammi della fotocamera EVS da questa fotocamera. IEvsCameraStream inizia a ricevere chiamate periodiche con nuovi fotogrammi di immagine finché non viene chiamato stopVideoStream() . I frame devono iniziare a essere consegnati entro 500 ms dalla chiamata startVideoStream e dopo l'avvio devono essere generati a un minimo di 10 FPS. Il tempo necessario per avviare il flusso video viene effettivamente conteggiato rispetto al tempo richiesto per l'avvio della telecamera per la retromarcia. Se lo stream non viene avviato deve essere restituito un codice di errore; altrimenti viene restituito OK.

oneway doneWithFrame(BufferDesc buffer);

Restituisce un frame che è stato consegnato a IEvsCameraStream. Una volta terminato di utilizzare un frame consegnato all'interfaccia IEvsCameraStream, il frame deve essere restituito a IEvsCamera per essere riutilizzato. È disponibile un numero piccolo e finito di buffer (possibilmente piccolo come uno) e, se la fornitura è esaurita, non vengono consegnati ulteriori frame finché non viene restituito un buffer, con il risultato potenziale di frame saltati (un buffer con un handle nullo indica la fine di un flusso e non è necessario restituirlo tramite questa funzione). Restituisce OK in caso di successo o il codice di errore appropriato che potrebbe includere INVALID_ARG o BUFFER_NOT_AVAILABLE .

stopVideoStream();

Interrompe la consegna dei fotogrammi della fotocamera SVE. Poiché la consegna è asincrona, i frame potrebbero continuare ad arrivare per qualche tempo dopo il ritorno della chiamata. Ogni frame deve essere restituito finché non viene segnalata la chiusura dello stream alla IEvsCameraStream. È legale chiamare stopVideoStream su uno streaming che è già stato interrotto o mai avviato, nel qual 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 nessun valore passato potrebbe causare l'arresto anomalo del driver. Il driver dovrebbe restituire 0 per qualsiasi opaqueIdentifier non riconosciuto.

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

Invia un valore specifico del driver all'implementazione HAL. Questa estensione viene fornita solo per facilitare le estensioni specifiche del veicolo e nessuna implementazione HAL dovrebbe richiedere che questa chiamata funzioni in uno stato predefinito. Se il driver riconosce e accetta i valori, dovrebbe 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 della compilazione di questa struttura per descrivere il buffer dell'immagine e il client HAL dovrebbe considerare 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 che ciascuna riga occupa effettivamente in memoria, tenendo conto dell'eventuale riempimento 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 il calcolo della dimensione in byte necessaria per spostarsi tra le righe dell'immagine ( stride in bytes = stride in pixels * pixelSize ).
  • format . Il formato pixel utilizzato dall'immagine. Il formato fornito deve essere compatibile con l'implementazione OpenGL della piattaforma. Per superare il test di compatibilità, è opportuno preferire HAL_PIXEL_FORMAT_YCRCB_420_SP per l'utilizzo della fotocamera e RGBA o BGRA per la visualizzazione.
  • usage . Flag di utilizzo impostati dall'implementazione HAL. Si prevede che i client HAL li superino senza modifiche (per i dettagli, fare riferimento ai flag correlati Gralloc.h ).
  • bufferId . Un valore univoco specificato dall'implementazione HAL per consentire il riconoscimento di un buffer dopo un viaggio di andata e ritorno attraverso le API HAL. Il valore memorizzato in questo campo può essere scelto arbitrariamente dall'implementazione HAL.
  • memHandle . 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 di frame video asincrone.

deliverFrame(BufferDesc buffer);

Riceve chiamate dall'HAL ogni volta che un fotogramma video è pronto per l'ispezione. Gli handle di buffer 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 mentre la pipeline si esaurisce. Ogni fotogramma deve essere comunque restituito; quando l'ultimo frame nel flusso è stato consegnato, verrà consegnato un bufferHandle NULL, a indicare la fine del flusso e non si verificano ulteriori consegne di frame. Non è necessario che lo stesso bufferHandle NULL venga rispedito tramite doneWithFrame() , ma tutti gli altri handle devono essere restituiti

Sebbene i formati 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 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'origine texture GL valida sull'implementazione GLES della piattaforma.

L'applicazione 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 può utilizzarli (e riutilizzarli) come ritiene opportuno.

IEvsDisplay

Questo oggetto rappresenta il display Evs, controlla lo stato del display e gestisce la presentazione effettiva delle immagini.

getDisplayInfo() generates (DisplayDesc info);

Restituisce le informazioni di base sulla visualizzazione EVS fornite dal sistema (vedere DisplayDesc ).

setDisplayState(DisplayState state) generates (EvsResult result);

Imposta lo stato di visualizzazione. I client possono impostare lo stato di visualizzazione per esprimere lo stato desiderato e l'implementazione dell'HAL deve accettare normalmente una richiesta per qualsiasi stato mentre si trova in qualsiasi altro stato, sebbene la risposta possa essere quella di ignorare la richiesta.

Al momento dell'inizializzazione, la visualizzazione viene definita per iniziare nello stato NOT_VISIBLE , dopodiché è previsto che il client richieda lo stato VISIBLE_ON_NEXT_FRAME e inizi a fornire il video. Quando la visualizzazione non è più richiesta, è previsto che il client richieda lo stato NOT_VISIBLE dopo aver superato l'ultimo fotogramma video.

È valido per qualsiasi stato e può essere richiesto 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 viene restituito INVALID_ARG .

getDisplayState() generates (DisplayState state);

Ottiene lo stato di visualizzazione. L'implementazione dell'HAL dovrebbe riportare lo stato corrente effettivo, che potrebbe differire dallo stato richiesto più recentemente. La logica responsabile della modifica degli stati di visualizzazione dovrebbe esistere al di sopra del livello del dispositivo, rendendo indesiderabile che l'implementazione 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 GL. Questo buffer deve essere restituito tramite una chiamata a returnTargetBufferForDisplay() anche se il display non è più visibile.

Sebbene i formati 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 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 destinazione di rendering GL valida sull'implementazione GLES della piattaforma.

In caso di errore, viene restituito un buffer con un handle null, ma non è necessario restituire tale buffer 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 il contenuto di BufferDesc non può essere modificato dall'applicazione client. Dopo questa chiamata, il buffer non è più valido per l'utilizzo da parte del client. Restituisce OK in caso di successo o il 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 la visualizzazione EVS. Può essere uno schermo fisico o uno schermo 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 kernel del dispositivo o di un nome per il dispositivo, ad esempio Rearview . Il valore per questa stringa viene scelto dall'implementazione HAL e utilizzato in modo opaco dallo stack precedente.
  • vendor_flags . Un metodo per trasmettere in modo opaco le informazioni specializzate della telecamera dal conducente a un'applicazione EVS personalizzata. Viene passato senza essere interpretato dal conducente fino all'applicazione 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 disabilitato (non visibile al conducente) o abilitato (mostrando un'immagine al conducente). Include uno stato transitorio in cui il display non è ancora visibile ma è pronto a diventarlo con la consegna del successivo fotogramma di immagini tramite la chiamata returnTargetBufferForDisplay() .

Responsabile SVE

Il gestore EVS fornisce l'interfaccia pubblica al sistema EVS per raccogliere e presentare le visualizzazioni delle telecamere esterne. Laddove i driver hardware consentono solo un'interfaccia attiva per risorsa (fotocamera o display), EVS Manager facilita l'accesso condiviso alle telecamere. Una singola applicazione EVS primaria è il primo client di EVS Manager ed è l'unico client autorizzato a scrivere i dati di visualizzazione (ai client aggiuntivi può essere concesso l'accesso di sola lettura alle immagini della telecamera).

Il gestore EVS implementa la stessa API dei driver HAL sottostanti e fornisce un servizio esteso supportando più client simultanei (più di un client può aprire una telecamera tramite il gestore EVS e ricevere un flusso video).

Diagramma API EVS Manager e hardware EVS.
Figura 2. EVS Manager rispecchia l'API hardware EVS sottostante

Le applicazioni non riscontrano 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 al flusso della telecamera. L'EVS Manager è di per sé l'unico client autorizzato del livello EVS Hardware HAL e funge da proxy per EVS Hardware HAL.

Le sezioni seguenti descrivono solo le chiamate che hanno un comportamento diverso (esteso) nell'implementazione dell'EVS Manager; le restanti chiamate sono identiche alle descrizioni dell'HAL EVS.

IEvsEnumerator

openCamera(string camera_id) generates (IEvsCamera camera);

Ottiene un oggetto interfaccia utilizzato per interagire con una telecamera specifica identificata dalla stringa univoca camera_id . Restituisce un NULL in caso di fallimento. A livello di EVS Manager, finché sono disponibili risorse di sistema sufficienti, una telecamera già aperta può essere riaperta da un altro processo, consentendo il collegamento del flusso video a più applicazioni consumer. Le stringhe camera_id al livello EVS Manager sono le stesse riportate al livello EVS Hardware.

IEvsCamera

Il gestore EVS ha fornito l'implementazione di IEvsCamera virtualizzata internamente in modo che le operazioni su una telecamera da parte di un client non influenzino gli altri client, che mantengono un accesso indipendente alle proprie telecamere.

startVideoStream(IEvsCameraStream receiver) generates (EvsResult result);

Avvia i flussi video. I client possono avviare e interrompere in modo indipendente i flussi video sulla stessa telecamera sottostante. La fotocamera sottostante si avvia all'avvio del primo client.

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

Restituisce un frame. Ogni cliente deve restituire i propri telai una volta terminato, ma è autorizzato a trattenerli per tutto il tempo che desidera. Quando il numero di frame trattenuti da un client raggiunge il limite configurato, non riceverà più frame finché non ne restituirà uno. Questo frame saltato non influisce sugli altri client che continuano a ricevere tutti i frame come previsto.

stopVideoStream();

Interrompe un flusso video. Ogni client può interrompere il proprio flusso video in qualsiasi momento senza influenzare gli altri client. Il flusso della telecamera sottostante a livello hardware viene interrotto quando l'ultimo client di una determinata telecamera interrompe il flusso.

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

Invia un valore specifico del driver, consentendo potenzialmente a un client di influenzare un altro client. Poiché EVS Manager non è in grado di comprendere le implicazioni delle parole di controllo definite dal fornitore, queste non vengono virtualizzate e gli eventuali effetti collaterali si applicano a tutti i client di una determinata telecamera. Ad esempio, se un fornitore utilizzasse questa chiamata per modificare la frequenza dei fotogrammi, tutti i client della telecamera del livello hardware interessato riceverebbero i fotogrammi alla nuova velocità.

IEvsDisplay

È consentito un solo proprietario del display, anche a livello di Responsabile EVS. Il Manager non aggiunge alcuna funzionalità e passa semplicemente l'interfaccia IEvsDisplay direttamente all'implementazione HAL sottostante.

Applicazione SVE

Android include un'implementazione di riferimento C++ nativa di un'applicazione EVS che comunica con EVS Manager e Vehicle HAL per fornire funzioni di base della telecamera retrovisiva. Si prevede che l'applicazione venga avviata molto presto nel processo di avvio del sistema, con un video mostrato in base alle telecamere disponibili e allo stato dell'auto (stato della marcia e degli indicatori di direzione). Gli OEM possono modificare o sostituire l'applicazione EVS con la logica e la presentazione specifiche del veicolo.

Figura 3. Logica di esempio dell'applicazione SVE, ottieni l'elenco delle telecamere.


Figura 4. Logica di esempio dell'applicazione EVS, callback del frame di ricezione.

Poiché i dati dell'immagine vengono presentati all'applicazione in un buffer grafico standard, l'applicazione è responsabile dello spostamento dell'immagine dal buffer di origine al buffer di output. Sebbene ciò introduca il costo di una copia dei dati, offre anche l'opportunità all'applicazione di eseguire il rendering dell'immagine nel buffer di visualizzazione nel modo desiderato.

Ad esempio, l'applicazione può scegliere di spostare i dati pixel stessi, potenzialmente con un'operazione di ridimensionamento o rotazione in linea. L'applicazione può anche scegliere di utilizzare l'immagine sorgente come texture OpenGL ed eseguire il rendering di una scena complessa nel buffer di output, inclusi elementi virtuali come icone, linee guida e animazioni. Un'applicazione più sofisticata può anche selezionare più telecamere di input simultanee e unirle nel singolo fotogramma di output (ad esempio per l'uso in una vista virtuale dall'alto verso il basso dell'ambiente circostante il veicolo).

Utilizzare EGL/SurfaceFlinger nell'HAL del display EVS

Questa sezione spiega come utilizzare EGL per eseguire il rendering di un'implementazione HAL di visualizzazione EVS in Android 10.

Un'implementazione di riferimento HAL EVS utilizza EGL per eseguire il rendering dell'anteprima della fotocamera sullo schermo e utilizza 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, ai fornitori non è consentito utilizzare Surface nelle implementazioni HAL.

Creazione di libgui per i processi del fornitore

L'uso 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 lo stesso del target libgui ad eccezione dell'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: le destinazioni fornitore vengono create con la macro NO_INPUT , che rimuove una parola a 32 bit dai dati del pacco. Poiché SurfaceFlinger prevede che questo campo sia stato rimosso, SurfaceFlinger 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 vengono fornite istruzioni di compilazione di esempio. Aspettatevi di 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

Utilizzo del raccoglitore in un'implementazione HAL EVS

In Android 8 (e versioni successive), il nodo del dispositivo /dev/binder è diventato esclusivo dei processi del framework e, pertanto, inaccessibile ai processi del fornitore. Invece, i processi del fornitore dovrebbero utilizzare /dev/hwbinder e convertire qualsiasi interfaccia AIDL in HIDL. Per coloro che desiderano continuare a utilizzare le interfacce AIDL tra i processi del fornitore, utilizzare il dominio del raccoglitore, /dev/vndbinder .

Dominio IPC Descrizione
/dev/binder IPC tra processi framework/app con interfacce AIDL
/dev/hwbinder IPC tra processi framework/fornitore con interfacce HIDL
IPC tra processi del fornitore con interfacce HIDL
/dev/vndbinder IPC tra processi fornitore/venditore 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. È necessaria una quantità di lavoro non banale per convertire le interfacce AIDL esistenti in HIDL. Fortunatamente, Android fornisce un metodo con cui selezionare il driver del raccoglitore 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 dovrebbero chiamarlo prima di chiamare Process o IPCThreadState o prima di effettuare qualsiasi chiamata al raccoglitore.

Politiche SELinux

Se l'implementazione del dispositivo è completamente tripla, SELinux impedisce ai processi del fornitore di utilizzare /dev/binder . Ad esempio, un'implementazione di esempio HAL EVS viene assegnata al dominio hal_evs_driver e richiede autorizzazioni r/w 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, provoca 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 individuare un bug e guidare lo sviluppo. Può anche essere utilizzato per risolvere la violazione di Android 10 sopra descritta.

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)

Creazione dell'implementazione di riferimento dell'HAL EVS come processo 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;