Fahrzeugkamera HAL

Android enthält eine Automotive-HIDL-Hardware-Abstraktionsschicht (HAL), die die Erfassung und Anzeige von Bildern sehr früh im Android-Startvorgang ermöglicht und während der gesamten Lebensdauer des Systems weiterhin funktioniert. Das HAL umfasst den Stack des Außensichtsystems (EVS) und wird typischerweise zur Unterstützung von Rückfahrkamera- und Rundumsichtanzeigen in Fahrzeugen mit Android-basierten In-Vehicle Infotainment (IVI)-Systemen verwendet. EVS ermöglicht auch die Implementierung erweiterter Funktionen in Benutzer-Apps.

Android enthält außerdem eine EVS-spezifische Erfassungs- und Anzeigetreiberschnittstelle (in /hardware/interfaces/automotive/evs/1.0 ). Es ist zwar möglich, eine Rückfahrkamera-App auf bestehenden Android-Kamera- und Anzeigediensten aufzubauen, eine solche App würde jedoch wahrscheinlich zu spät im Android-Startvorgang ausgeführt. Die Verwendung eines dedizierten HAL ermöglicht eine optimierte Schnittstelle 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, Videobilder vom EVS-Manager anzufordern und fertige Bilder zur Anzeige an den EVS-Manager zurückzusenden. Es wird erwartet, dass es von init gestartet wird, sobald EVS und Car Service verfügbar sind, und zwar innerhalb von zwei (2) Sekunden nach dem Einschalten. OEMs können die EVS-App nach Wunsch modifizieren oder ersetzen.

EVS-Manager

Der EVS Manager ( /packages/services/Car/evs/manager ) stellt die Bausteine ​​bereit, die eine EVS-App benötigt, um alles von einer einfachen Rückfahrkameraanzeige bis hin zu einem 6DOF-Multikamera-Rendering zu implementieren. Die Schnittstelle wird über HIDL bereitgestellt und ist so konzipiert, dass sie mehrere gleichzeitige Clients akzeptiert. Andere Apps und Dienste (insbesondere der Autoservice) können den EVS-Manager-Status abfragen, um herauszufinden, wann das EVS-System aktiv ist.

EVS HIDL-Schnittstelle

Das EVS-System, sowohl die Kamera als auch die Anzeigeelemente, ist im Paket android.hardware.automotive.evs definiert. Eine Beispielimplementierung, die die Schnittstelle ausübt (synthetische Testbilder generiert und überprüft, ob die Bilder den Roundtrip durchlaufen), wird in /hardware/interfaces/automotive/evs/1.0/default bereitgestellt.

Der OEM ist für die Implementierung der API verantwortlich, die durch die .hal-Dateien in /hardware/interfaces/automotive/evs ausgedrückt wird. Solche Implementierungen sind für die Konfiguration und Erfassung von Daten von physischen Kameras und deren Bereitstellung über von Gralloc erkennbare gemeinsam genutzte Speicherpuffer verantwortlich. Die Anzeigeseite der Implementierung ist dafür verantwortlich, einen gemeinsam genutzten Speicherpuffer bereitzustellen, der von der App gefüllt werden kann (normalerweise über EGL-Rendering) und die fertigen Frames vor allem anderen darzustellen, was möglicherweise auf der physischen Anzeige angezeigt werden soll. Anbieterimplementierungen der EVS-Schnittstelle können unter /vendor/… /device/… oder hardware/… gespeichert werden (z. B. /hardware/[vendor]/[platform]/evs ).

Kernel-Treiber

Ein Gerät, das den EVS-Stack unterstützt, erfordert Kernel-Treiber. Anstatt neue Treiber zu erstellen, haben OEMs die Möglichkeit, EVS-erforderliche Funktionen über vorhandene Kamera- und/oder Display-Hardwaretreiber zu unterstützen. Die Wiederverwendung von Treibern könnte von Vorteil sein, insbesondere für Anzeigetreiber, bei denen die Bildpräsentation möglicherweise eine Koordination mit anderen aktiven Threads erfordert. 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ängt.

Beschreibung der EVS-Hardwareschnittstelle

Der Abschnitt beschreibt den HAL. Von den Anbietern wird erwartet, dass sie an ihre Hardware angepasste Implementierungen dieser API bereitstellen.

IEvsEnumerator

Dieses Objekt ist für die Auflistung 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 zurück, der Beschreibungen für alle Kameras im System enthält. Es wird davon ausgegangen, dass der Satz Kameras beim Booten fest und erkennbar ist. Einzelheiten zu Kamerabeschreibungen finden Sie unter CameraDesc .

openCamera(string camera_id) generates (IEvsCamera camera);

Ruft ein Schnittstellenobjekt ab, das zur Interaktion mit einer bestimmten Kamera verwendet wird, die durch die eindeutige Zeichenfolge camera_id identifiziert wird. Gibt bei einem Fehler NULL zurück. Versuche, eine bereits geöffnete Kamera erneut zu öffnen, können nicht fehlschlagen. Um Race Conditions im Zusammenhang mit dem Starten und Herunterfahren einer App zu vermeiden, sollte beim erneuten Öffnen einer Kamera die vorherige Instanz heruntergefahren werden, damit die neue Anforderung erfüllt werden kann. Eine Kamerainstanz, die auf diese Weise vorbelegt wurde, muss in einen inaktiven Zustand versetzt werden, auf die endgültige Zerstörung warten und auf jede Anfrage, den Kamerastatus zu beeinflussen, mit dem Rückkehrcode OWNERSHIP_LOST antworten.

closeCamera(IEvsCamera camera);

Gibt die IEvsCamera-Schnittstelle frei (und ist das Gegenteil des openCamera() Aufrufs). Der Videostream der Kamera muss durch den Aufruf von stopVideoStream() vor dem Aufruf von closeCamera gestoppt werden.

openDisplay() generates (IEvsDisplay display);

Ruft ein Schnittstellenobjekt ab, das ausschließlich zur Interaktion mit der EVS-Anzeige des Systems verwendet wird. Es kann jeweils nur ein Client über eine funktionsfähige Instanz von IEvsDisplay verfügen. Ähnlich dem in openCamera beschriebenen aggressiven Öffnungsverhalten kann jederzeit ein neues IEvsDisplay-Objekt erstellt werden, das alle vorherigen Instanzen deaktiviert. Ungültig gemachte Instanzen bleiben bestehen und reagieren auf Funktionsaufrufe ihrer Besitzer, dürfen jedoch im toten Zustand keine Mutationsoperationen durchführen. Schließlich wird erwartet, dass die Client-App die OWNERSHIP_LOST Fehlerrückgabecodes bemerkt und die inaktive Schnittstelle schließt und freigibt.

closeDisplay(IEvsDisplay display);

Gibt die IEvsDisplay-Schnittstelle frei (und ist das Gegenteil des openDisplay() -Aufrufs). Ausstehende Puffer, die über getTargetBuffer() Aufrufe empfangen wurden, müssen an die Anzeige zurückgegeben werden, bevor die Anzeige geschlossen wird.

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 für die Änderung des Anzeigestatus verantwortliche Logik sollte oberhalb der Geräteschicht vorhanden sein, sodass es für die HAL-Implementierung unerwünscht ist, den Anzeigestatus spontan zu ändern. Wenn die Anzeige 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 . Eine Zeichenfolge, die 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 diese Zeichenfolge wird von der HAL-Implementierung ausgewählt und vom obigen Stapel undurchsichtig verwendet.
  • vendor_flags . Eine Methode zur undurchsichtigen Weitergabe spezieller Kamerainformationen vom Fahrer an eine benutzerdefinierte EVS-App. Es wird vom Fahrer uninterpretiert an die EVS-App weitergegeben, die es 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 CameraDesc dieser Kamera zurück.

setMaxFramesInFlight(int32 bufferCount) generates (EvsResult result);

Gibt die Tiefe der Pufferkette an, die die Kamera unterstützen soll. Bis dahin können viele Frames gleichzeitig vom Client von IEvsCamera gehalten werden. Wenn so viele Frames an den Empfänger übermittelt wurden, ohne von doneWithFrame zurückgegeben zu werden, überspringt der Stream Frames, bis ein Puffer zur Wiederverwendung zurückgegeben wird. Es ist zulässig, dass dieser Aufruf jederzeit erfolgt, auch während Streams bereits ausgeführt werden. In diesem Fall sollten der Kette entsprechend Puffer hinzugefügt oder daraus entfernt werden. Wenn dieser Einstiegspunkt nicht aufgerufen wird, unterstützt die IEvsCamera standardmäßig mindestens einen Frame. mit mehr akzeptable.

Wenn der angeforderte bufferCount nicht berücksichtigt werden kann, gibt die Funktion BUFFER_NOT_AVAILABLE oder einen anderen relevanten Fehlercode zurück. In diesem Fall arbeitet das System mit dem zuvor eingestellten Wert weiter.

startVideoStream(IEvsCameraStream receiver) generates (EvsResult result);

Fordert die Lieferung von EVS-Kamerarahmen von dieser Kamera an. Der IEvsCameraStream beginnt, regelmäßige Aufrufe mit neuen Bildframes zu empfangen, bis stopVideoStream() aufgerufen wird. Die Auslieferung der Frames muss innerhalb von 500 ms nach dem startVideoStream Aufruf beginnen und nach dem Start mit mindestens 10 FPS generiert werden. Die zum Starten des Videostreams erforderliche Zeit wird effektiv auf die erforderliche Startzeit der Rückfahrkamera angerechnet. 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 übermittelt wurde. Wenn die Verwendung eines an die IEvsCameraStream-Schnittstelle übermittelten Frames abgeschlossen ist, muss der Frame zur Wiederverwendung an die IEvsCamera zurückgegeben werden. Es steht eine kleine, endliche Anzahl von Puffern zur Verfügung (möglicherweise nur einer), und wenn der Vorrat erschöpft ist, werden keine weiteren Frames geliefert, bis ein Puffer zurückgegeben wird, was möglicherweise dazu führt, dass Frames übersprungen werden (ein Puffer mit einem Null-Handle zeigt das Ende an). eines Streams und muss nicht über diese Funktion zurückgegeben werden). Gibt bei Erfolg OK oder einen entsprechenden Fehlercode zurück, der möglicherweise INVALID_ARG oder BUFFER_NOT_AVAILABLE enthält.

stopVideoStream();

Stoppt die Lieferung von EVS-Kamerabildern. Da die Zustellung asynchron erfolgt, kann es nach der Rückkehr dieses Aufrufs noch einige Zeit dauern, bis 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 gestoppt oder nie gestartet wurde. In diesem Fall wird er ignoriert.

getExtendedInfo(int32 opaqueIdentifier) generates (int32 value);

Fordert treiberspezifische Informationen von der HAL-Implementierung an. Die für opaqueIdentifier zulässigen Werte sind treiberspezifisch, aber kein übergebener Wert kann den Treiber zum Absturz bringen. Der Treiber sollte für jeden nicht erkannten opaqueIdentifier 0 zurückgeben.

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

Sendet einen treiberspezifischen Wert an die HAL-Implementierung. Diese Erweiterung wird nur zur Erleichterung fahrzeugspezifischer Erweiterungen bereitgestellt und keine HAL-Implementierung sollte erfordern, dass dieser Aufruf in einem Standardzustand 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 zur Beschreibung des Bildpuffers auszufüllen, und der HAL-Client sollte diese Struktur als schreibgeschützt behandeln. Die Felder enthalten genügend Informationen, damit der Client ein ANativeWindowBuffer Objekt rekonstruieren kann, wie es möglicherweise erforderlich ist, um das Bild mit EGL über die Erweiterung eglCreateImageKHR() zu verwenden.

  • width . Die Breite des dargestellten Bildes in Pixel.
  • height . Die Höhe des dargestellten Bildes in Pixel.
  • stride . Anzahl der Pixel, die jede Zeile tatsächlich im Speicher belegt, unter Berücksichtigung etwaiger Auffüllungen für die Ausrichtung der Zeilen. Wird in Pixeln ausgedrückt, um der Konvention zu entsprechen, die gralloc für seine Pufferbeschreibungen verwendet.
  • pixelSize . Anzahl der von jedem einzelnen Pixel belegten Bytes, wodurch die Größe in Bytes berechnet werden kann, die erforderlich ist, um zwischen den Zeilen im Bild zu wechseln ( stride in Bytes = stride in Pixel * pixelSize ).
  • format . Das vom Bild verwendete Pixelformat. Das bereitgestellte Format muss mit der OpenGL-Implementierung der Plattform kompatibel sein. Um den Kompatibilitätstest zu bestehen, sollte HAL_PIXEL_FORMAT_YCRCB_420_SP für die Kameraverwendung und RGBA oder BGRA für die Anzeige bevorzugt werden.
  • usage . Von der HAL-Implementierung festgelegte Verwendungsflags. Von HAL-Clients wird erwartet, dass sie diese unverändert weitergeben (Einzelheiten finden Sie unter Gralloc.h bezogene Flags).
  • bufferId . Ein eindeutiger Wert, der von der HAL-Implementierung angegeben wird, um die Erkennung eines Puffers nach einem Roundtrip durch die HAL-APIs zu ermöglichen. Der in diesem Feld gespeicherte Wert kann von der HAL-Implementierung beliebig ausgewählt werden.
  • memHandle . Das Handle für den zugrunde liegenden Speicherpuffer, der die Bilddaten enthält. Die HAL-Implementierung könnte sich dafür entscheiden, hier ein Gralloc-Puffer-Handle zu speichern.

IEvsCameraStream

Der Client implementiert diese Schnittstelle, um asynchrone Videobildlieferungen zu empfangen.

deliverFrame(BufferDesc buffer);

Empfängt jedes Mal Anrufe vom HAL, wenn ein Videobild zur Inspektion bereit ist. Von dieser Methode empfangene Pufferhandles müssen über Aufrufe von IEvsCamera::doneWithFrame() zurückgegeben werden. Wenn der Videostream über einen Aufruf von IEvsCamera::stopVideoStream() gestoppt wird, wird dieser Rückruf möglicherweise fortgesetzt, während die Pipeline leer wird. Jeder Frame muss trotzdem zurückgegeben werden; Wenn der letzte Frame im Stream übermittelt wurde, wird ein NULL bufferHandle übermittelt, was das Ende des Streams anzeigt und keine weiteren Frame-Übermittlungen erfolgen. Das NULL bufferHandle selbst muss nicht über doneWithFrame() zurückgesendet werden, aber alle anderen Handles müssen zurückgegeben werden

Während proprietäre Pufferformate technisch möglich sind, erfordert die Kompatibilitätsprüfung, dass der Puffer in einem von vier unterstützten Formaten vorliegt: 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). Das ausgewählte Format muss eine gültige GL-Texturquelle in der GLES-Implementierung der Plattform sein.

Die App sollte sich nicht auf eine Korrespondenz zwischen dem bufferId Feld und dem memHandle in der BufferDesc Struktur verlassen. Die bufferId Werte sind im Wesentlichen privat für die HAL-Treiberimplementierung und können nach eigenem Ermessen verwendet (und wiederverwendet) werden.

IEvsDisplay

Dieses Objekt stellt die Evs-Anzeige dar, steuert den Status der Anzeige und kümmert sich um die eigentliche Darstellung von Bildern.

getDisplayInfo() generates (DisplayDesc info);

Gibt grundlegende Informationen über die vom System bereitgestellte EVS-Anzeige zurück (siehe DisplayDesc ).

setDisplayState(DisplayState state) generates (EvsResult result);

Legt den Anzeigestatus fest. Clients können den Anzeigestatus so festlegen, dass er den gewünschten Status ausdrückt, und die HAL-Implementierung muss eine Anfrage für jeden Status in jedem anderen Status ordnungsgemäß akzeptieren, obwohl die Antwort möglicherweise darin besteht, die Anfrage zu ignorieren.

Bei der Initialisierung wird die Anzeige so definiert, dass sie im Status NOT_VISIBLE startet. Anschließend wird erwartet, dass der Client den Status VISIBLE_ON_NEXT_FRAME anfordert und mit der Bereitstellung von Video beginnt. Wenn die Anzeige nicht mehr erforderlich ist, wird vom Client erwartet, dass er den Status NOT_VISIBLE anfordert, nachdem das letzte Videobild übergeben wurde.

Es gilt für jeden Staat und kann jederzeit angefordert werden. Wenn die Anzeige bereits sichtbar ist, sollte sie sichtbar bleiben, wenn sie auf VISIBLE_ON_NEXT_FRAME gesetzt ist. Gibt immer OK zurück, es sei denn, der angeforderte Status ist ein nicht erkannter Enumerationswert. 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 vom zuletzt angeforderten Status abweichen kann. Die für die Änderung des Anzeigestatus verantwortliche Logik sollte oberhalb der Geräteschicht vorhanden sein, sodass es für die HAL-Implementierung unerwünscht ist, den Anzeigestatus spontan zu ändern.

getTargetBuffer() generates (handle bufferHandle);

Gibt ein Handle für einen Frame-Puffer zurück, der der Anzeige zugeordnet ist. Dieser Puffer kann durch Software und/oder GL gesperrt und beschrieben werden. Dieser Puffer muss über einen Aufruf von returnTargetBufferForDisplay() zurückgegeben werden, auch wenn die Anzeige nicht mehr sichtbar ist.

Während proprietäre Pufferformate technisch möglich sind, erfordert die Kompatibilitätsprüfung, dass der Puffer in einem von vier unterstützten Formaten vorliegt: 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). Das ausgewählte Format muss ein gültiges GL-Renderziel 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);

Teilt der Anzeige mit, dass der Puffer zur Anzeige bereit ist. Für die Verwendung mit diesem Aufruf sind nur Puffer gültig, die durch einen Aufruf von getTargetBuffer() abgerufen wurden, und der Inhalt von BufferDesc darf von der Client-App nicht geändert werden. Nach diesem Aufruf ist der Puffer nicht mehr für die Verwendung durch den Client gültig. Gibt bei Erfolg OK oder einen entsprechenden 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 einer EVS-Anzeige und die für eine EVS-Implementierung erforderlich sind. Der HAL ist für das Ausfüllen dieser Struktur zur Beschreibung der EVS-Anzeige verantwortlich. Dabei kann es sich um eine physische Anzeige oder eine virtuelle Anzeige handeln, die mit einem anderen Präsentationsgerät überlagert oder mit diesem gemischt wird.

  • display_id . Eine Zeichenfolge, die die Anzeige 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 diese Zeichenfolge wird von der HAL-Implementierung ausgewählt und vom obigen Stapel undurchsichtig verwendet.
  • vendor_flags . Eine Methode zur undurchsichtigen Weitergabe spezieller Kamerainformationen vom Fahrer an eine benutzerdefinierte EVS-App. Es wird vom Fahrer uninterpretiert an die EVS-App weitergegeben, die es 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
}

Beschreibt den Status der EVS-Anzeige, die deaktiviert (für den Fahrer nicht sichtbar) oder aktiviert (dem Fahrer ein Bild angezeigt) sein kann. Enthält einen Übergangszustand, in dem die Anzeige noch nicht sichtbar ist, aber darauf vorbereitet ist, mit der Übermittlung des nächsten Bildbilds über den returnTargetBufferForDisplay() Aufruf sichtbar zu werden.

EVS-Manager

Der EVS Manager stellt die öffentliche Schnittstelle zum EVS-System zum Sammeln und Präsentieren externer Kameraansichten bereit. Wo Hardwaretreiber nur eine aktive Schnittstelle pro Ressource (Kamera oder Display) zulassen, erleichtert 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 Anzeigedaten schreiben darf (zusätzlichen Clients kann schreibgeschützter Zugriff auf Kamerabilder gewährt werden).

Der EVS-Manager implementiert dieselbe API wie die zugrunde liegenden HAL-Treiber und bietet einen erweiterten Service durch die Unterstützung mehrerer gleichzeitiger Clients (mehr als ein Client kann eine Kamera über den EVS-Manager öffnen und einen Videostream empfangen).

EVS-Manager und EVS-Hardware-API-Diagramm.

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

Apps sehen keine Unterschiede, wenn sie über die EVS-Hardware-HAL-Implementierung oder die EVS-Manager-API arbeiten, mit der Ausnahme, dass die EVS-Manager-API gleichzeitigen Kamera-Stream-Zugriff ermöglicht. Der EVS-Manager ist selbst der einzige zulässige Client der EVS-Hardware-HAL-Schicht und fungiert als Proxy für den EVS-Hardware-HAL.

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

IEvsEnumerator

openCamera(string camera_id) generates (IEvsCamera camera);

Ruft ein Schnittstellenobjekt ab, das zur Interaktion mit einer bestimmten Kamera verwendet wird, die durch die eindeutige Zeichenfolge camera_id identifiziert wird. Gibt bei einem Fehler NULL zurück. Auf der EVS-Manager-Ebene kann eine bereits geöffnete Kamera durch einen anderen Prozess erneut geöffnet werden, sofern ausreichend Systemressourcen verfügbar sind, sodass der Videostream an mehrere Verbraucher-Apps weitergeleitet werden kann. Die camera_id Zeichenfolgen auf der EVS-Manager-Ebene sind dieselben wie die, die an die EVS-Hardware-Ebene gemeldet 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 stoppen. Die zugrunde liegende Kamera startet, wenn der erste Client startet.

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

Gibt einen Frame zurück. Jeder Kunde muss seine Rahmen zurückgeben, wenn er fertig ist, darf ihn aber so lange behalten, wie er möchte. Wenn die von einem Client gehaltene Frame-Anzahl den konfigurierten Grenzwert erreicht, empfängt er keine weiteren Frames, bis er einen zurückgibt. Dieses Frame-Überspringen hat keine Auswirkungen auf andere Clients, die weiterhin alle Frames wie erwartet empfangen.

stopVideoStream();

Stoppt einen Videostream. Jeder Client kann seinen Videostream jederzeit stoppen, ohne dass dies Auswirkungen auf andere Clients hat. Der zugrunde liegende Kamerastream auf der Hardwareebene wird gestoppt, wenn der letzte Client einer bestimmten Kamera seinen Stream stoppt.

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

Sendet einen treiberspezifischen Wert, der es einem Client möglicherweise ermöglicht, einen anderen Client zu beeinflussen. Da der EVS-Manager die Auswirkungen herstellerdefinierter Steuerwörter nicht verstehen kann, werden diese nicht virtualisiert und etwaige Nebenwirkungen gelten für alle Clients einer bestimmten Kamera. Wenn beispielsweise ein Anbieter diesen Aufruf nutzt, um die Bildraten zu ändern, würden alle Clients der betroffenen Hardware-Layer-Kamera Bilder mit der neuen Rate empfangen.

IEvsDisplay

Es ist nur ein Besitzer des Displays zulässig, auch auf der Ebene des EVS-Managers. Der Manager fügt keine Funktionalität 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 dem Fahrzeug-HAL kommuniziert, um grundlegende Rückfahrkamerafunktionen bereitzustellen. Es wird erwartet, dass die App sehr früh beim Systemstart startet und abhängig von den verfügbaren Kameras und dem Zustand des Fahrzeugs (Gang- und Blinkerzustand) passende Videos anzeigt. OEMs können die EVS-App durch ihre eigene fahrzeugspezifische Logik und Darstellung modifizieren oder ersetzen.

Abbildung 3. Beispiellogik der EVS-App, Kameraliste abrufen.



Abbildung 4. EVS-App-Beispiellogik, Frame-Rückruf empfangen.

Da Bilddaten der App in einem Standard-Grafikpuffer präsentiert werden, ist die App dafür verantwortlich, das Bild vom Quellpuffer in den Ausgabepuffer zu verschieben. Dies erhöht zwar die Kosten für eine Datenkopie, bietet der App aber auch die Möglichkeit, das Bild auf jede gewünschte Weise in den Anzeigepuffer zu rendern.

Die App kann sich beispielsweise dafür entscheiden, die Pixeldaten selbst zu verschieben, möglicherweise mit einer Inline-Skalierungs- oder Rotationsoperation. Die App könnte sich auch dafür entscheiden, das Quellbild als OpenGL-Textur zu verwenden und eine komplexe Szene in den Ausgabepuffer zu rendern, einschließlich virtueller Elemente wie Symbole, Richtlinien und Animationen. Eine anspruchsvollere App kann auch mehrere gleichzeitige Eingabekameras auswählen und sie in einem einzigen Ausgabebild zusammenführen (z. B. für die Verwendung in einer virtuellen Ansicht der Fahrzeugumgebung von oben nach unten).

Verwenden Sie EGL/SurfaceFlinger im EVS Display HAL

In diesem Abschnitt wird erläutert, wie Sie EGL zum Rendern einer EVS Display HAL-Implementierung in Android 10 verwenden.

Eine EVS-HAL-Referenzimplementierung verwendet EGL zum Rendern der Kameravorschau auf dem Bildschirm und verwendet libgui zum Erstellen der Ziel-EGL-Renderoberfläche. In Android 8 (und höher) wird libgui als VNDK-private klassifiziert, was sich auf eine Gruppe von Bibliotheken bezieht, die VNDK-Bibliotheken zur Verfügung stehen und die von Anbieterprozessen nicht verwendet werden können. Da sich HAL-Implementierungen in der Anbieterpartition befinden müssen, ist es Anbietern untersagt, Surface in HAL-Implementierungen zu verwenden.

Erstellen einer Libgui für Anbieterprozesse

Die Verwendung von libgui ist die einzige Option zur Verwendung von EGL/SurfaceFlinger in EVS Display HAL-Implementierungen. Der einfachste Weg, libgui zu implementieren, ist über „frameworks/native/libs/gui“ direkt durch die Verwendung eines zusätzlichen Build-Ziels im Build-Skript. Dieses Ziel ist genau das gleiche wie das libgui Ziel, außer dass zwei Felder hinzugefügt wurden:

  • 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, das ein 32-Bit-Wort aus den Paketdaten entfernt. Da SurfaceFlinger erwartet, dass dieses Feld entfernt wurde, kann SurfaceFlinger das Paket nicht analysieren. Dies wird als fcntl Fehler beobachtet:

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

Um diesen Zustand zu beheben:

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

Nachfolgend finden Sie Beispielbauanweisungen . Erwarten Sie den Erhalt eines $(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

Verwenden Sie Binder in einer EVS-HAL-Implementierung

In Android 8 (und höher) wurde der Geräteknoten /dev/binder exklusiv für Framework-Prozesse und daher für Anbieterprozesse nicht zugänglich. Stattdessen sollten Anbieterprozesse /dev/hwbinder verwenden und alle AIDL-Schnittstellen in HIDL konvertieren. Für diejenigen, die weiterhin AIDL-Schnittstellen zwischen Anbieterprozessen verwenden möchten, verwenden Sie die Binder-Domäne /dev/vndbinder .

IPC-Domäne Beschreibung
/dev/binder IPC zwischen Framework-/App-Prozessen mit AIDL-Schnittstellen
/dev/hwbinder IPC zwischen Framework-/Anbieterprozessen mit HIDL-Schnittstellen
IPC zwischen Lieferantenprozessen mit HIDL-Schnittstellen
/dev/vndbinder IPC zwischen Vendor/Vendor-Prozessen mit AIDL-Schnittstellen

Während SurfaceFlinger AIDL-Schnittstellen definiert, können Anbieterprozesse nur HIDL-Schnittstellen verwenden, um mit Framework-Prozessen zu kommunizieren. Es ist ein nicht unerheblicher Arbeitsaufwand erforderlich, um vorhandene AIDL-Schnittstellen in HIDL zu konvertieren. Glücklicherweise bietet Android eine Methode zur Auswahl des Binder-Treibers für libbinder , mit dem die Prozesse der Userspace-Bibliothek 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 dies aufrufen , bevor sie Process oder IPCThreadState aufrufen oder bevor sie Binder-Aufrufe durchführen.

SELinux-Richtlinien

Wenn die Geräteimplementierung vollständig dreifach ist, verhindert SELinux, dass Anbieterprozesse /dev/binder verwenden. Beispielsweise ist eine EVS-HAL-Beispielimplementierung der Domäne hal_evs_driver zugewiesen und erfordert Lese-/Schreibberechtigungen für die Domäne 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 es gegen die folgenden Neverallow-Regeln verstößt, die in system/sepolicy/domain.te für ein Full-Treble-Gerät 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, das bereitgestellt wird, um einen Fehler zu erkennen und die Entwicklung zu steuern. Es kann auch zur Behebung des oben beschriebenen Verstoßes gegen Android 10 verwendet werden.

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)

Erstellen Sie eine EVS-HAL-Referenzimplementierung als Anbieterprozess

Als Referenz können Sie die folgenden Änderungen an packages/services/Car/evs/Android.mk vornehmen. Stellen Sie sicher, dass 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;