Fahrzeugkamera HAL

Android enthält eine Automotive-HIDL-Hardware-Abstraktionsschicht (HAL), die das Erfassen und Anzeigen von Bildern in sehr frühen Phasen des Android-Startvorgangs ermöglicht und so lange wie das System funktioniert. Der HAL enthält den EVS-Stack (Exterior View System) und wird in der Regel für Rückkameras und Surround-View-Displays in Fahrzeugen mit Android-basierten Infotainmentsystemen verwendet. Mit EVS können auch erweiterte Funktionen in Nutzer-Apps implementiert werden.

Android enthält auch eine EVS-spezifische Schnittstelle für Aufnahme- und Anzeigetreiber (in /hardware/interfaces/automotive/evs/1.0). Es ist zwar möglich, eine Rückkamera-App auf vorhandenen Android-Kamera- und Displaydiensten zu erstellen, aber eine solche App würde wahrscheinlich zu spät im Android-Startvorgang ausgeführt werden. Die Verwendung einer speziellen HAL ermöglicht eine optimierte Benutzeroberfläche und macht deutlich, was ein OEM implementieren muss, um den EVS-Stack zu unterstützen.

Systemkomponenten

EVS umfasst die folgenden Systemkomponenten:

Diagramm der EVS-Systemkomponenten

Abbildung 1. Übersicht über die EVS-Systemkomponenten

EVS-App

Eine Beispiel-C++-EVS-App (/packages/services/Car/evs/app) dient als Referenzimplementierung. Diese App ist dafür verantwortlich, Videoframes vom EVS-Manager anzufordern und fertige Frames zur Anzeige an den EVS-Manager zurückzusenden. Es wird erwartet, dass es von init gestartet wird, sobald EVS und der Autodienst verfügbar sind, d. h. innerhalb von zwei (2) Sekunden nach dem Einschalten. OEMs können die EVS-App nach Bedarf ändern oder ersetzen.

EVS-Manager

Der EVS Manager (/packages/services/Car/evs/manager) bietet die Bausteine, die für eine EVS-App erforderlich sind, um alles von einem einfachen Rückkameradisplay bis hin zu einem 6-DOF-Multikamera-Rendering zu implementieren. Die Benutzeroberfläche wird über HIDL präsentiert und ist für die gleichzeitige Annahme mehrerer Clients ausgelegt. Andere Apps und Dienste (insbesondere der Car-Dienst) können den Status des EVS-Managers abfragen, um herauszufinden, wann das EVS-System aktiv ist.

EVS HIDL-Schnittstelle

Das EVS-System, sowohl die Kamera als auch die Displayelemente, wird im Paket android.hardware.automotive.evs definiert. In /hardware/interfaces/automotive/evs/1.0/default finden Sie eine Beispielimplementierung, die die Schnittstelle testet (synthetische Testbilder generiert und prüft, ob die Bilder den gesamten Weg zurücklegen).

Der OEM ist für die Implementierung der API verantwortlich, die in /hardware/interfaces/automotive/evs durch die .hal-Dateien ausgedrückt wird. Solche Implementierungen sind für die Konfiguration und Erfassung von Daten von physischen Kameras und die Bereitstellung über Shared-Memory-Buffer verantwortlich, die von Gralloc erkannt werden. Die Displayseite der Implementierung ist für die Bereitstellung eines gemeinsamen Speicherpuffers verantwortlich, der von der App (in der Regel über EGL-Rendering) gefüllt werden kann. Die fertigen Frames werden vor allen anderen Elementen angezeigt, die auf dem physischen Display erscheinen sollen. Anbieterimplementierungen der EVS-Schnittstelle können unter /vendor/… /device/… oder hardware/… gespeichert werden (z.B. /hardware/[vendor]/[platform]/evs.

Kernel-Treiber

Für ein Gerät, das den EVS-Stack unterstützt, sind Kernel-Treiber erforderlich. Anstatt neue Treiber zu erstellen, haben OEMs die Möglichkeit, die für EVS erforderlichen Funktionen über vorhandene Kamera- und/oder Display-Hardwaretreiber zu unterstützen. Die Wiederverwendung von Treibern kann vorteilhaft sein, insbesondere bei Displaytreibern, bei denen die Bilddarstellung eine Abstimmung mit anderen aktiven Threads erfordern kann. Android 8.0 enthält einen V4l2-basierten Beispieltreiber (in packages/services/Car/evs/sampleDriver), der für die V4l2-Unterstützung vom Kernel und für die Darstellung des Ausgabebilds von SurfaceFlinger abhängig ist.

Beschreibung der EVS-Hardwareschnittstelle

Der Abschnitt beschreibt den HAL. Anbieter müssen Implementierungen dieser API bereitstellen, die für ihre Hardware angepasst sind.

IEvsEnumerator

Dieses Objekt ist für die Aufzählung der verfügbaren EVS-Hardware im System verantwortlich (eine oder mehrere Kameras und das einzelne Anzeigegerät).

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

Gibt einen Vektor mit Beschreibungen für alle Kameras im System zurück. Es wird davon ausgegangen, dass die Kameras festgelegt und beim Booten bekannt sind. Weitere Informationen zu Kamerabeschreibungen finden Sie unter CameraDesc.

openCamera(string camera_id) generates (IEvsCamera camera);

Ruft ein Interface-Objekt ab, das für die Interaktion mit einer bestimmten Kamera verwendet wird, die durch den eindeutigen String camera_id identifiziert wird. Bei einem Fehler wird NULL zurückgegeben. Versuche, eine bereits geöffnete Kamera noch einmal zu öffnen, können nicht fehlschlagen. Um Race-Bedingungen beim Starten und Beenden der App zu vermeiden, sollte beim erneuten Öffnen einer Kamera die vorherige Instanz heruntergefahren werden, damit die neue Anfrage erfüllt werden kann. Eine Kamerainstanz, die auf diese Weise präemptiv beendet wurde, muss in den Status „Inaktiv“ versetzt werden, auf das endgültige Löschen warten und auf jede Anfrage zum Ändern des Kamerastatus mit dem Rückgabecode OWNERSHIP_LOST antworten.

closeCamera(IEvsCamera camera);

Die IEvsCamera-Schnittstelle wird freigegeben. Dies ist das Gegenteil des openCamera()-Aufrufs. Der Kamera-Videostream muss durch Aufrufen von stopVideoStream() vor closeCamera beendet werden.

openDisplay() generates (IEvsDisplay display);

Ruft ein Steuerfeldobjekt ab, das ausschließlich zur Interaktion mit dem EVS-Display des Systems verwendet wird. Es kann jeweils nur ein Client eine funktionsfähige Instanz von IEvsDisplay haben. Ähnlich wie beim aggressiven Öffnen, das in openCamera beschrieben wird, kann jederzeit ein neues IEvsDisplay-Objekt erstellt werden, wodurch alle vorherigen Instanzen deaktiviert werden. Ungültige Instanzen existieren weiterhin und reagieren auf Funktionsaufrufe ihrer Eigentümer. Sie dürfen jedoch keine mutating-Vorgänge ausführen, wenn sie inaktiv sind. Die Client-App sollte die OWNERSHIP_LOST-Fehlerrückgabecodes irgendwann bemerken und die inaktive Benutzeroberfläche schließen und freigeben.

closeDisplay(IEvsDisplay display);

Die IEvsDisplay-Schnittstelle wird freigegeben. Dies ist das Gegenteil des openDisplay()-Aufrufs. Ausstehende Puffer, die über getTargetBuffer()-Aufrufe empfangen wurden, müssen vor dem Schließen der Anzeige an die Anzeige zurückgegeben werden.

getDisplayState() generates (DisplayState state);

Ruft den aktuellen Anzeigestatus ab. Die HAL-Implementierung sollte den tatsächlichen aktuellen Status melden, der vom zuletzt angeforderten Status abweichen kann. Die Logik, die für das Ändern der Displaystatus verantwortlich ist, sollte über der Geräteschicht liegen. Daher ist es nicht wünschenswert, dass die HAL-Implementierung spontan den Displaystatus ändert. Wenn das Display derzeit von keinem Client gehalten wird (durch einen Aufruf von „openDisplay“), gibt diese Funktion NOT_OPEN zurück. Andernfalls wird der aktuelle Status des EVS-Displays gemeldet (siehe IEvsDisplay API).

struct CameraDesc {
    string      camera_id;
    int32       vendor_flags;       // Opaque value
}
  • camera_id: Ein String, der eine bestimmte Kamera eindeutig identifiziert. Dies kann der Kernel-Gerätename des Geräts oder ein Name für das Gerät sein, z. B. rearview. Der Wert für diesen String wird von der HAL-Implementierung ausgewählt und vom Stack oben nicht transparent verwendet.
  • vendor_flags. Eine Methode zum undurchsichtigen Übergeben spezieller Kamerainformationen vom Treiber an eine benutzerdefinierte EVS-App. Die Informationen werden vom Treiber uninterpretiert an die EVS-App übergeben, die sie ignorieren kann.

IEvsCamera

Dieses Objekt stellt eine einzelne Kamera dar und ist die primäre Schnittstelle zum Aufnehmen von Bildern.

getCameraInfo() generates (CameraDesc info);

Gibt den CameraDesc dieser Kamera zurück.

setMaxFramesInFlight(int32 bufferCount) generates (EvsResult result);

Gibt die Tiefe der Pufferkette an, die die Kamera unterstützen soll. Bis zu diese Anzahl von Frames kann vom Client von IEvsCamera gleichzeitig gehalten werden. Wenn diese Anzahl von Frames an den Empfänger gesendet wurde, ohne von doneWithFrame zurückgegeben zu werden, überspringt der Stream Frames, bis ein Puffer zur Wiederverwendung zurückgegeben wird. Dieser Aufruf kann jederzeit erfolgen, auch wenn bereits Streams ausgeführt werden. In diesem Fall sollten der Kette nach Bedarf Buffers hinzugefügt oder daraus entfernt werden. Wenn kein Aufruf an diesen Einstiegspunkt erfolgt, unterstützt IEvsCamera standardmäßig mindestens einen Frame. Es kann mehr Frames geben.

Wenn der angeforderte Wert für „bufferCount“ nicht berücksichtigt werden kann, gibt die Funktion BUFFER_NOT_AVAILABLE oder einen anderen relevanten Fehlercode zurück. In diesem Fall wird das System weiterhin mit dem zuvor festgelegten Wert betrieben.

startVideoStream(IEvsCameraStream receiver) generates (EvsResult result);

Hiermit wird die Übermittlung von EVS-Kameraframes von dieser Kamera angefordert. Der IEvsCameraStream erhält dann regelmäßig Aufrufe mit neuen Bildframes, bis stopVideoStream() aufgerufen wird. Die Übermittlung von Frames muss innerhalb von 500 ms nach dem startVideoStream-Aufruf beginnen und nach dem Start mit mindestens 10 fps generiert werden. Die Zeit, die zum Starten des Videostreams erforderlich ist, wird auf die Zeit angerechnet, die die Rückkamera zum Starten benötigt. Wenn der Stream nicht gestartet wird, muss ein Fehlercode zurückgegeben werden. Andernfalls wird „OK“ zurückgegeben.

oneway doneWithFrame(BufferDesc buffer);

Gibt einen Frame zurück, der an den IEvsCameraStream gesendet wurde. Wenn ein Frame, der an die IEvsCameraStream-Schnittstelle gesendet wurde, nicht mehr benötigt wird, muss er zur Wiederverwendung an die IEvsCamera zurückgegeben werden. Es ist eine kleine, begrenzte Anzahl von Puffern verfügbar (möglicherweise so klein wie einer). Wenn das Angebot erschöpft ist, werden keine weiteren Frames geliefert, bis ein Puffer zurückgegeben wird. Dies kann zu übersprungenen Frames führen. Ein Zwischenspeicher mit einem Null-Handle gibt das Ende eines Streams an und muss nicht über diese Funktion zurückgegeben werden. Gibt bei Erfolg „OK“ oder einen geeigneten Fehlercode zurück, der möglicherweise INVALID_ARG oder BUFFER_NOT_AVAILABLE enthält.

stopVideoStream();

Die Auslieferung von EVS-Kameraframes wird beendet. Da die Übermittlung asynchron erfolgt, können noch einige Zeit nach dem Rückgabewert des Aufrufs Frames eintreffen. Jeder Frame muss zurückgegeben werden, bis das Schließen des Streams dem IEvsCameraStream signalisiert wird. Es ist zulässig, stopVideoStream für einen Stream aufzurufen, der bereits angehalten wurde oder nie gestartet wurde. In diesen Fällen wird er ignoriert.

getExtendedInfo(int32 opaqueIdentifier) generates (int32 value);

Fordert treiberspezifische Informationen aus der HAL-Implementierung an. Die für opaqueIdentifier zulässigen Werte sind treiberspezifisch. Wird kein Wert übergeben, kann es jedoch zum Absturz des Fahrers kommen. Der Treiber sollte für nicht erkannte opaqueIdentifier den Wert 0 zurückgeben.

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

Sendet einen treiberspezifischen Wert an die HAL-Implementierung. Diese Erweiterung wird nur zur Unterstützung fahrzeugspezifischer Erweiterungen bereitgestellt. Keine HAL-Implementierung sollte erfordern, dass dieser Aufruf im Standardstatus funktioniert. Wenn der Treiber die Werte erkennt und akzeptiert, sollte „OK“ zurückgegeben werden. Andernfalls sollte INVALID_ARG oder ein anderer repräsentativer Fehlercode zurückgegeben werden.

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
}

Beschreibt ein Bild, das über die API übergeben wird. Das HAL-Laufwerk ist dafür verantwortlich, diese Struktur auszufüllen, um den Bildpuffer zu beschreiben. Der HAL-Client sollte diese Struktur als schreibgeschützt behandeln. Die Felder enthalten genügend Informationen, damit der Client ein ANativeWindowBuffer-Objekt rekonstruieren kann, was erforderlich sein kann, um das Bild über die eglCreateImageKHR()-Erweiterung mit EGL zu verwenden.

  • width. Die Breite des angezeigten Bildes in Pixeln.
  • height: Die Höhe des angezeigten Bildes in Pixeln.
  • stride: Die Anzahl der Pixel, die jede Zeile tatsächlich im Arbeitsspeicher belegt, einschließlich eines möglichen Abstands zur Ausrichtung der Zeilen. Wird in Pixeln ausgedrückt, um der Konvention zu entsprechen, die von gralloc für die Zwischenspeicherbeschreibungen angewendet wird.
  • pixelSize: Die Anzahl der Bytes, die von jedem einzelnen Pixel belegt werden. Damit lässt sich die Größe in Byte berechnen, die zum Wechseln zwischen Zeilen im Bild erforderlich ist (stride in Byte = stride in Pixel × pixelSize).
  • format: Das Pixelformat, das für das Bild verwendet wird. Das angegebene Format muss mit der OpenGL-Implementierung der Plattform kompatibel sein. Damit die Kompatibilitätstests bestanden werden, sollte HAL_PIXEL_FORMAT_YCRCB_420_SP für die Kamera und RGBA oder BGRA für das Display verwendet werden.
  • usage. Nutzungsflags, die von der HAL-Implementierung festgelegt werden. HAL-Clients müssen diese Flags unverändert übergeben. Weitere Informationen finden Sie in den zugehörigen Flags für Gralloc.h.
  • bufferId: Ein eindeutiger Wert, der von der HAL-Implementierung angegeben wird, damit ein Zwischenspeicher nach einem Umlauf durch die HAL APIs erkannt werden kann. Der in diesem Feld gespeicherte Wert kann von der HAL-Implementierung beliebig ausgewählt werden.
  • memHandle: Der Handle für den zugrunde liegenden Arbeitsspeicherbuffer, der die Bilddaten enthält. Die HAL-Implementierung kann hier einen Gralloc-Puffer-Handle speichern.

IEvsCameraStream

Der Client implementiert diese Schnittstelle, um asynchrone Videoframe-Übermittlungen zu empfangen.

deliverFrame(BufferDesc buffer);

Erhält jedes Mal Aufrufe vom HAL, wenn ein Videoframe zur Prüfung bereit ist. Von dieser Methode empfangene Puffer-Handles müssen über Aufrufe von IEvsCamera::doneWithFrame() zurückgegeben werden. Wenn der Videostream über einen Aufruf von IEvsCamera::stopVideoStream() angehalten wird, wird dieser Rückruf möglicherweise fortgesetzt, während die Pipeline abläuft. Jeder Frame muss trotzdem zurückgegeben werden. Wenn der letzte Frame im Stream gesendet wurde, wird ein NULL-bufferHandle gesendet, was das Ende des Streams bedeutet und dass keine weiteren Frame-Übermittlungen erfolgen. Der NULL-bufferHandle selbst muss nicht über doneWithFrame() zurückgesendet werden, aber alle anderen Handles müssen zurückgegeben werden.

Obwohl proprietäre Pufferformate technisch möglich sind, muss für Kompatibilitätstests eines von vier unterstützten Formaten vorliegen: NV21 (YCrCb 4:2:0 Semi-Planar), YV12 (YCrCb 4:2:0 Planar), YUYV (YCrCb 4:2:2, verschachtelt), B:RGBA (32 Bit: Bit Rx). Das ausgewählte Format muss eine gültige GL-Texturquelle in der GLES-Implementierung der Plattform sein.

Die Anwendung sollte keine Übereinstimmung zwischen dem Feld bufferId und dem memHandle in der BufferDesc-Struktur benötigen. Die bufferId-Werte sind im Wesentlichen nur für die HAL-Treiberimplementierung bestimmt und können nach Bedarf verwendet und wiederverwendet werden.

IEvsDisplay

Dieses Objekt stellt das EVSE-Display dar, steuert den Status des Displays und verarbeitet die tatsächliche Darstellung von Bildern.

getDisplayInfo() generates (DisplayDesc info);

Gibt grundlegende Informationen zum EVS-Display zurück, die vom System bereitgestellt werden (siehe DisplayDesc).

setDisplayState(DisplayState state) generates (EvsResult result);

Legt den Anzeigestatus fest. Clients können den Anzeigestatus so festlegen, dass der gewünschte Status ausgedrückt wird. Die HAL-Implementierung muss eine Anfrage für einen beliebigen Status in einem anderen Status ordnungsgemäß akzeptieren, auch wenn die Antwort möglicherweise darin besteht, die Anfrage zu ignorieren.

Bei der Initialisierung wird festgelegt, dass das Display im Status NOT_VISIBLE gestartet wird. Danach soll der Client den Status VISIBLE_ON_NEXT_FRAME anfordern und mit der Bereitstellung von Video beginnen. Wenn die Anzeige nicht mehr erforderlich ist, sollte der Client den NOT_VISIBLE-Status anfordern, nachdem er den letzten Videoframe übergeben hat.

Sie kann jederzeit von jedem Bundesstaat angefordert werden. Wenn das Display bereits sichtbar ist, sollte es auch bei VISIBLE_ON_NEXT_FRAME sichtbar bleiben. Gibt immer „OK“ zurück, es sei denn, der angeforderte Status ist ein nicht erkannter Enum-Wert. In diesem Fall wird INVALID_ARG zurückgegeben.

getDisplayState() generates (DisplayState state);

Ruft den Anzeigestatus ab. Die HAL-Implementierung sollte den tatsächlichen aktuellen Status melden, der sich vom zuletzt angeforderten Status unterscheiden kann. Die Logik, die für das Ändern der Displaystatus verantwortlich ist, sollte über der Geräteschicht liegen. Daher ist es nicht wünschenswert, dass die HAL-Implementierung spontan den Displaystatus ändert.

getTargetBuffer() generates (handle bufferHandle);

Gibt einen Handle für einen Frame-Buffer zurück, der mit dem Display verknüpft ist. Dieser Puffer kann durch Software und/oder GL gesperrt und geschrieben werden. Dieser Puffer muss über einen Aufruf von returnTargetBufferForDisplay() zurückgegeben werden, auch wenn das Display nicht mehr sichtbar ist.

Benutzerdefinierte Zwischenspeicherformate sind zwar technisch möglich, für die Kompatibilitätstests muss der Zwischenspeicher jedoch in einem der vier unterstützten Formate vorliegen: 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) oder BGRA (32 Bit B:G:R:x). Das ausgewählte Format muss ein gültiges GL-Rendering-Ziel in der GLES-Implementierung der Plattform sein.

Bei einem Fehler wird ein Puffer mit einem Null-Handle zurückgegeben. Ein solcher Puffer muss jedoch nicht an returnTargetBufferForDisplay zurückgegeben werden.

returnTargetBufferForDisplay(handle bufferHandle) generates (EvsResult result);

Informiert das Display darüber, dass der Puffer zur Anzeige bereit ist. Nur Zwischenspeicher, die über einen Aufruf von getTargetBuffer() abgerufen wurden, können mit diesem Aufruf verwendet werden. Der Inhalt von BufferDesc darf von der Client-App nicht geändert werden. Nach diesem Aufruf kann der Zwischenspeicher nicht mehr vom Client verwendet werden. Gibt bei Erfolg „OK“ oder einen geeigneten Fehlercode zurück, der möglicherweise INVALID_ARG oder BUFFER_NOT_AVAILABLE enthält.

struct DisplayDesc {
     string  display_id;
     int32   vendor_flags;  // Opaque value
}

Beschreibt die grundlegenden Eigenschaften eines EVS-Displays, die für eine EVS-Implementierung erforderlich sind. Der HAL ist dafür verantwortlich, diese Struktur auszufüllen, um das EVS-Display zu beschreiben. Kann ein physisches oder virtuelles Display sein, das überlagert oder mit einem anderen Präsentationsgerät kombiniert wird.

  • display_id: Ein String, der das Display eindeutig identifiziert. Dies kann der Kernel-Gerätename des Geräts oder ein Name für das Gerät sein, z. B. rearview. Der Wert für diesen String wird von der HAL-Implementierung ausgewählt und vom Stack oben nicht transparent verwendet.
  • vendor_flags: Eine Methode, um spezielle Kamerainformationen vom Fahrer undurchsichtig an eine benutzerdefinierte EVS-App zu übergeben. Sie werden unverschlüsselt vom Fahrer an die EVS-App übergeben, die sie ignorieren kann.
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
}

Gibt den Status des EVS-Displays an, das deaktiviert (für den Fahrer nicht sichtbar) oder aktiviert (dem Fahrer wird ein Bild angezeigt) sein kann. Enthält einen vorübergehenden Zustand, bei dem der Bildschirm noch nicht sichtbar ist, aber mit der Bereitstellung des nächsten Bildframes über den returnTargetBufferForDisplay()-Aufruf sichtbar werden soll.

EVS Manager

Der EVS Manager bietet die öffentliche Schnittstelle zum EVS-System zum Erfassen und Präsentieren von externen Kameraansichten. Wenn Hardwaretreiber nur eine aktive Schnittstelle pro Ressource (Kamera oder Display) zulassen, ermöglicht der EVS-Manager den gemeinsamen Zugriff auf die Kameras. Eine einzelne primäre EVS-App ist der erste Client des EVS-Managers und der einzige Client, der Displaydaten schreiben darf. Zusätzlichen Clients kann Lesezugriff auf Kamerabilder gewährt werden.

Der EVS-Manager implementiert dieselbe API wie die zugrunde liegenden HAL-Treiber und bietet einen erweiterten Dienst, da mehrere Clients gleichzeitig unterstützt werden. Mehrere Clients können also über den EVS-Manager eine Kamera öffnen und einen Videostream empfangen.

EVS Manager und EVS Hardware API-Diagramm

Abbildung 2. EVS Manager spiegelt die zugrunde liegende EVS-Hardware-API.

Für Apps gibt es keine Unterschiede, wenn sie über die EVS-Hardware-HAL-Implementierung oder die EVS Manager API ausgeführt werden, mit Ausnahme der Tatsache, dass die EVS Manager API den gleichzeitigen Zugriff auf den Kamerastream ermöglicht. Der EVS-Manager ist der einzige zulässige Client der EVS-Hardware-HAL-Ebene und fungiert als Proxy für die EVS-Hardware-HAL.

In den folgenden Abschnitten werden nur die Aufrufe beschrieben, die in der EVS Manager-Implementierung ein anderes (erweitertes) Verhalten haben. Die verbleibenden Aufrufe sind mit den EVS HAL-Beschreibungen identisch.

IEvsEnumerator

openCamera(string camera_id) generates (IEvsCamera camera);

Ruft ein Interface-Objekt ab, das für die Interaktion mit einer bestimmten Kamera verwendet wird, die durch den eindeutigen String camera_id identifiziert wird. Bei einem Fehler wird NULL zurückgegeben. Sofern genügend Systemressourcen verfügbar sind, kann eine bereits geöffnete Kamera auf der EVS Manager-Ebene von einem anderen Prozess noch einmal geöffnet werden. So kann der Videostream an mehrere Verbraucher-Apps weitergeleitet werden. Die camera_id-Strings auf der EVS Manager-Ebene sind mit denen identisch, die an die EVS-Hardwareebene gesendet werden.

IEvsCamera

Die von EVS Manager bereitgestellte IEvsCamera-Implementierung ist intern virtualisiert, sodass Vorgänge an einer Kamera durch einen Client keine Auswirkungen auf andere Clients haben, die unabhängigen Zugriff auf ihre Kameras behalten.

startVideoStream(IEvsCameraStream receiver) generates (EvsResult result);

Startet Videostreams. Clients können Videostreams unabhängig voneinander auf derselben zugrunde liegenden Kamera starten und beenden. Die zugrunde liegende Kamera wird gestartet, wenn der erste Client gestartet wird.

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

Gibt einen Frame zurück. Jeder Client muss seine Frames zurückgeben, wenn er fertig ist, darf sie aber so lange wie gewünscht aufbewahren. Wenn die Anzahl der Frames, die ein Client hält, das konfigurierte Limit erreicht, erhält er erst dann wieder Frames, wenn er einen zurückgibt. Das Überspringen von Frames hat keine Auswirkungen auf andere Clients, die weiterhin alle Frames wie erwartet erhalten.

stopVideoStream();

Hält einen Videostream an. Jeder Client kann seinen Videostream jederzeit beenden, ohne andere Clients zu beeinträchtigen. Der zugrunde liegende Kamerastream auf der Hardwareebene wird beendet, wenn der letzte Client einer bestimmten Kamera seinen Stream beendet.

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

Es wird ein treiberspezifischer Wert gesendet, wodurch ein Client möglicherweise einen anderen Client beeinflussen kann. Da der EVS-Manager die Auswirkungen von vom Anbieter definierten Steuerwörtern nicht verstehen kann, werden sie nicht virtualisiert und alle Nebenwirkungen wirken sich auf alle Clients einer bestimmten Kamera aus. Wenn ein Anbieter diesen Aufruf beispielsweise verwendet, um die Framerate zu ändern, erhalten alle Clients der betroffenen Hardwareebenenkamera Frames mit der neuen Rate.

Internet Explorer Display

Es ist nur ein Eigentümer des Displays zulässig, auch auf EVS Manager-Ebene. Der Manager fügt keine Funktionen hinzu und leitet die IEvsDisplay-Schnittstelle einfach direkt an die zugrunde liegende HAL-Implementierung weiter.

EVS-App

Android enthält eine native C++-Referenzimplementierung einer EVS-App, die mit dem EVS-Manager und der Vehicle HAL kommuniziert, um grundlegende Rückkamerafunktionen bereitzustellen. Die App wird voraussichtlich sehr früh im Systemstartvorgang gestartet. Je nach verfügbaren Kameras und Zustand des Fahrzeugs (Getriebe- und Fahrtrichtungsschalterstatus) wird ein geeignetes Video angezeigt. OEMs können die EVS-App durch ihre eigene fahrzeugspezifische Logik und Präsentation ändern oder ersetzen.

Abbildung 3 Beispiellogik für EVS-App, Kameraliste abrufen



Abbildung 4: EVS-App-Beispiellogik, Frame-Callback empfangen.

Da Bilddaten der App in einem Standardgrafik-Puffer präsentiert werden, ist die App dafür verantwortlich, das Bild aus dem Quellpuffer in den Ausgabepuffer zu verschieben. Dies führt zwar zu den Kosten einer Datenkopie, bietet aber auch die Möglichkeit, dass die App das Bild auf beliebige Weise in den Displaybuffer rendert.

Die App kann beispielsweise die Pixeldaten selbst verschieben, möglicherweise mit einer Inline-Skalierung oder -Drehung. Die App kann das Quellbild auch als OpenGL-Textur verwenden und eine komplexe Szene in den Ausgabebuffer rendern, einschließlich virtueller Elemente wie Symbole, Richtlinien und Animationen. Eine ausgefeiltere App kann auch mehrere gleichzeitige Eingabekameras auswählen und in einem einzigen Ausgabeframe zusammenführen, z. B. für eine virtuelle Ansicht der Umgebung des Fahrzeugs von oben.

EGL/SurfaceFlinger im EVS-Display-HAL verwenden

In diesem Abschnitt wird erläutert, wie Sie mit EGL eine EVS-Display-HAL-Implementierung in Android 10 rendern.

Bei einer EVS HAL-Referenzimplementierung wird EGL verwendet, um die Kameravorschau auf dem Bildschirm zu rendern, und libgui, um die Ziel-EGL-Renderfläche zu erstellen. Ab Android 8 wird libgui als VNDK-private klassifiziert. Dies bezieht sich auf eine Gruppe von Bibliotheken, die VNDK-Bibliotheken zur Verfügung stehen und die von Anbieterprozessen nicht verwendet werden können. Da HAL-Implementierungen in der Anbieterpartition gespeichert sein müssen, können Anbieter Surface nicht in HAL-Implementierungen verwenden.

libgui für Anbieterprozesse erstellen

Die Verwendung von libgui ist die einzige Möglichkeit, EGL/SurfaceFlinger in EVS-Display-HAL-Implementierungen zu verwenden. Die einfachste Methode zur Implementierung von libgui ist über frameworks/native/libs/gui direkt mit einem zusätzlichen Build-Ziel im Build-Script. Dieses Ziel ist mit dem libgui-Ziel identisch, mit Ausnahme von zwei zusätzlichen Feldern:

  • 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", ], …

Hinweis: Anbieterziele werden mit dem Makro NO_INPUT erstellt, wodurch ein 32‑Bit-Wort aus den Paketdaten entfernt wird. Da SurfaceFlinger dieses entfernte Feld erwartet, kann es das Paket nicht parsen. Dies wird als fcntl-Fehler erfasst:

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

So beheben Sie dieses Problem:

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);

Unten finden Sie eine Beispielanleitung für den Build. Sie sollten eine $(ANDROID_PRODUCT_OUT)/system/lib64/libgui_vendor.so erhalten.

$ 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

Binder in einer EVS HAL-Implementierung verwenden

Unter Android 8 (und höher) ist der Geräteknoten /dev/binder nur noch für Framework-Prozesse verfügbar und daher für Anbieterprozesse nicht zugänglich. Stattdessen sollten Anbieterprozesse /dev/hwbinder verwenden und alle AIDL-Schnittstellen in HIDL konvertieren. Wenn Sie weiterhin AIDL-Schnittstellen zwischen Anbieterprozessen verwenden möchten, verwenden Sie die Binder-Domain /dev/vndbinder.

IPC-Domain Beschreibung
/dev/binder IPC zwischen Framework-/App-Prozessen mit AIDL-Schnittstellen
/dev/hwbinder IPC zwischen Framework-/Anbieterprozessen mit HIDL-Schnittstellen
IPC zwischen Anbieterprozessen mit HIDL-Schnittstellen
/dev/vndbinder IPC zwischen Anbieter-/Anbieterprozessen mit AIDL-Schnittstellen

SurfaceFlinger definiert AIDL-Schnittstellen, Anbieterprozesse können jedoch nur HIDL-Schnittstellen verwenden, um mit Framework-Prozessen zu kommunizieren. Die Konvertierung vorhandener AIDL-Schnittstellen in HIDL ist äußerst aufwendig. Glücklicherweise bietet Android eine Methode, mit der der Bindertreiber für libbinder ausgewählt werden kann, mit dem die Bibliotheksprozesse des Userspace verknüpft sind.

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));

Hinweis: Anbieterprozesse sollten diese Funktion vor dem Aufruf von Process oder IPCThreadState oder vor dem Ausführen von Binderaufrufen aufrufen.

SELinux-Richtlinien

Wenn die Geräteimplementierung vollständig treble ist, verhindert SELinux, dass Anbieterprozesse /dev/binder verwenden. Beispielsweise ist eine EVS HAL-Beispielimplementierung der Domain hal_evs_driver zugewiesen und erfordert r/w-Berechtigungen für die Domain 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

Das Hinzufügen dieser Berechtigungen führt jedoch zu einem Build-Fehler, da dies gegen die folgenden niezulassenden Regeln verstößt, die in system/sepolicy/domain.te für ein Gerät mit vollen Höhen definiert sind.

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 ist ein Attribut, mit dem ein Fehler erkannt und die Entwicklung gesteuert werden kann. Sie können sie auch verwenden, um den oben beschriebenen Verstoß gegen Android 10 zu beheben.

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)

EVS HAL-Referenzimplementierung als Anbieterprozess erstellen

Als Referenz können Sie die folgenden Änderungen auf packages/services/Car/evs/Android.mk anwenden. Prüfen Sie, ob alle beschriebenen Änderungen für Ihre Implementierung funktionieren.

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;