Videocamera per veicoli HAL

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'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:

Diagramma dei componenti
di sistema EVS
Figura 1. Panoramica dei componenti del sistema EVS.

App EVS

Un'app EVS C++ di esempio (/packages/services/Car/evs/app) funge da implementazione di riferimento. Questa app è responsabile della richiesta dei frame video dall'EVS Manager e dell'invio dei frame finali per la visualizzazione all'EVS Manager. Prevede di essere avviato tramite init non appena EVS e Car Service saranno disponibili, preso di mira entro due (2) secondi dall'accensione. Gli OEM possono modificare o sostituire l'app EVS come preferiscono.

EVS Manager

EVS Manager (/packages/services/Car/evs/manager) fornisce 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 servizi (nello specifico il Servizio auto) possono inviare query sullo stato di EVS Manager per sapere 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 è disponibile un'implementazione di esempio che esercita l'interfaccia (genera immagini di test sintetiche e convalida le immagini che consentono il round trip).

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 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 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 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 fallire. 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 prerilasciata in questo modo deve essere impostata in stato non attivo, in attesa di distruzione definitiva e nel rispondere a eventuali richieste di modifica dello stato della videocamera con un codice di ritorno pari a OWNERSHIP_LOST.

closeCamera(IEvsCamera camera);

Rilascia l'interfaccia IEvsCamera (ed è l'opposto della chiamata openCamera()). Lo stream video della videocamera deve essere interrotto chiamando il numero stopVideoStream() prima di chiamare il numero closeCamera.

openDisplay() generates (IEvsDisplay display);

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 un nuovo oggetto IEvsDisplay in qualsiasi momento e disattivare tutte le 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 (e è 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 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 (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 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.

VideocameraIEvs

Questo oggetto rappresenta una singola fotocamera ed è l'interfaccia principale per acquisire le immagini.

getCameraInfo() generates (CameraDesc info);

Restituisce CameraDesc di questa videocamera.

setMaxFramesInFlight(int32 bufferCount) generates (EvsResult result);

Specifica la profondità della catena di buffer che la videocamera deve supportare. Il client di IEvsCamera può gestire contemporaneamente fino a questo numero di frame. Se questo numero di frame è stato inviato al ricevente senza essere restituito da doneWithFrame, lo stream salta i frame finché 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);

Richiede l'invio di frame della videocamera EVS da 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 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 la pubblicazione di fotogrammi delle videocamere 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 fino a quando la chiusura del flusso 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 driver deve restituire 0 per qualsiasi 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 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. L'unità HAL è responsabile della compilazione di questa struttura per descrivere il buffer di immagini e il client HAL deve 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 con 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 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 utilizzare HAL_PIXEL_FORMAT_YCRCB_420_SP per l'uso della fotocamera, mentre RGBA o BGRA per la visualizzazione.
  • usage. Flag di utilizzo impostati dall'implementazione dell'HAL. I client HAL devono trasmetterli invariati (per maggiori dettagli, consulta i 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 tramite le API HAL. Il valore memorizzato in questo campo può essere scelto arbitrariamente dall'implementazione 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 le pubblicazioni asincrone dei fotogrammi 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 NULL bufferHandle non deve essere restituito con 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.

Display IEvs

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

getDisplayInfo() generates (DisplayDesc info);

Restituisce le 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.

All'inizializzazione, il display è definito per iniziare nello stato NOT_VISIBLE, dopodiché il client dovrebbe 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 dell'HAL deve riportare lo stato attuale 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.

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 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 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 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 quelle 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 il display. 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 solo un'interfaccia attiva per risorsa (videocamera o display), EVS Manager facilita l'accesso condiviso alle videocamere. Una singola app EVS principale è il primo client di EVS Manager ed è l'unico client autorizzato a scrivere 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).

Diagramma dell&#39;API hardware EVS e di EVS Manager.
Figura 2. EVS Manager esegue il mirroring dell'API hardware EVS di base.

Le app non notano differenze quando operano tramite l'implementazione di EVS Hardware HAL 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.

IEvsEnumeratore

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 sottostante 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 logica e la presentazione specifiche del veicolo.

Figura 3. Logica di esempio dell'app EVS, recupera il 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 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 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 un target di build aggiuntivo 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 binder nell'implementazione dell'HAL di 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 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 i processi di fornitore/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. Per convertire le interfacce AIDL esistenti in HIDL, è richiesta una quantità di lavoro non banale. 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: i processi del fornitore devono richiamare questo nome prima di chiamare Process o IPCThreadState, oppure prima di effettuare qualsiasi chiamata a 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 utilizzata 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;