Fahrzeugkamera HAL

Android enthält eine Automotive-HIDL-Hardwareabstraktionsschicht (HAL), die das Erfassen und Anzeigen von Bildern sehr früh im Android-Bootprozess ermöglicht und während der gesamten Lebensdauer des Systems funktioniert. Das HAL umfasst den EVS-Stack (Exterior View System) und wird in der Regel zur Unterstützung von Rückfahrkameras und Surround-View-Displays in Fahrzeugen mit Android-basierten Infotainmentsystemen (In-Vehicle Infotainment, IVI) verwendet. EVS ermöglicht auch die Implementierung erweiterter Funktionen in Nutzer-Apps.

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ückfahrkamera-App auf Grundlage der vorhandenen Android-Kamera- und ‑Anzeigedienste zu entwickeln, eine solche App würde jedoch wahrscheinlich zu spät im Android-Bootvorgang ausgeführt. Durch die Verwendung eines dedizierten HAL wird eine optimierte Schnittstelle ermöglicht und es wird klar, 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-EVS-App in C++ (/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 Car Service verfügbar sind, und zwar innerhalb von zwei (2) Sekunden nach dem Einschalten. OEMs können die EVS-App nach Belieben ändern oder ersetzen.

EVS Manager

Der EVS-Manager (/packages/services/Car/evs/manager) bietet die Bausteine, die eine EVS-App benötigt, um alles von einer einfachen Rückfahrkameraanzeige bis hin zu einem 6DOF-Rendering mit mehreren Kameras zu implementieren. Die Schnittstelle wird über HIDL präsentiert 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 Displayelemente, ist im Paket android.hardware.automotive.evs definiert. Eine Beispielimplementierung, die die Schnittstelle nutzt (synthetische Testbilder generiert und prüft, ob die Bilder den Roundtrip durchlaufen), finden Sie unter /hardware/interfaces/automotive/evs/1.0/default.

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 die Bereitstellung über von Gralloc erkennbare Shared Memory-Puffer verantwortlich. Die Displayseite der Implementierung ist dafür verantwortlich, einen gemeinsamen Speicherpuffer bereitzustellen, der von der App gefüllt werden kann (in der Regel mit EGL-Rendering), und die fertigen Frames vor allen anderen Elementen zu präsentieren, die auf dem physischen Display angezeigt werden 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 Kerneltreiber erforderlich. Anstatt neue Treiber zu erstellen, können OEMs die für EVS erforderlichen Funktionen über vorhandene Kamera- oder Display-Hardwaretreiber unterstützen. Die Wiederverwendung von Treibern kann von Vorteil sein, insbesondere bei Displaytreibern, bei denen die Bilddarstellung eine Koordination mit anderen aktiven Threads erfordern kann. Android 8.0 enthält einen auf v4l2 basierenden 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

In diesem Abschnitt wird die HAL beschrieben. Von Anbietern wird erwartet, dass sie Implementierungen dieser API für ihre Hardware 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 mit Beschreibungen für alle Kameras im System zurück. Es wird davon ausgegangen, dass die Anzahl der Kameras beim Booten festgelegt und bekannt ist. Weitere Informationen zu Kamerabeschreibungen finden Sie unter CameraDesc.

openCamera(string camera_id) generates (IEvsCamera camera);

Ruft ein Schnittstellenobjekt ab, das für die Interaktion mit einer bestimmten Kamera verwendet wird, die durch den eindeutigen String camera_id identifiziert wird. Gibt bei einem Fehler NULL zurück. Versuche, eine bereits geöffnete Kamera noch einmal zu öffnen, können nicht fehlschlagen. Um Race-Bedingungen zu vermeiden, die mit dem Starten und Herunterfahren von Apps zusammenhängen, sollte beim erneuten Öffnen einer Kamera die vorherige Instanz heruntergefahren werden, damit die neue Anfrage erfüllt werden kann. Eine Kamera-Instanz, die auf diese Weise unterbrochen wurde, muss in einen inaktiven Status versetzt werden. Sie wartet auf die endgültige Zerstörung und reagiert auf jede Anfrage, die den Kamerastatus mit dem Rückgabecode OWNERSHIP_LOST beeinflusst.

closeCamera(IEvsCamera camera);

Gibt die IEvsCamera-Schnittstelle frei (das Gegenteil des openCamera()-Aufrufs). Der Videostream der Kamera muss durch Aufrufen von stopVideoStream() beendet werden, bevor closeCamera aufgerufen wird.

openDisplay() generates (IEvsDisplay display);

Ruft ein Schnittstellenobjekt ab, das ausschließlich für die Interaktion mit dem EVS-Display des Systems verwendet wird. Nur ein Client darf jeweils eine funktionale Instanz von IEvsDisplay enthalten. Ähnlich wie beim aggressiven Öffnungsverhalten, das in openCamera beschrieben wird, kann jederzeit ein neues IEvsDisplay-Objekt erstellt werden, wodurch alle vorherigen Instanzen deaktiviert werden. Ungültige Instanzen sind weiterhin vorhanden und reagieren auf Funktionsaufrufe ihrer Eigentümer, dürfen aber keine mutierenden Vorgänge ausführen, wenn sie inaktiv sind. Die Client-App sollte schließlich die OWNERSHIP_LOST-Fehler-Rückgabecodes erkennen und die inaktive Schnittstelle schließen und freigeben.

closeDisplay(IEvsDisplay display);

Gibt die IEvsDisplay-Schnittstelle frei (das Gegenteil des openDisplay()-Aufrufs). Ausstehende Puffer, die mit getTargetBuffer()-Aufrufen empfangen wurden, müssen an das Display zurückgegeben werden, bevor das Display geschlossen wird.

getDisplayState() generates (DisplayState state);

Ruft den aktuellen Displaystatus ab. Die HAL-Implementierung sollte den tatsächlichen aktuellen Status melden, der sich vom zuletzt angeforderten Status unterscheiden kann. Die Logik zum Ändern von Displaystatus sollte sich über der Geräteebene befinden. Daher ist es unerwünscht, dass die HAL-Implementierung den Displaystatus spontan ändert. Wenn die Anzeige derzeit nicht von einem 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. 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 darüber undurchsichtig verwendet.
  • vendor_flags. Eine Methode zum undurchsichtigen Übergeben spezieller Kamerainformationen vom Treiber an eine benutzerdefinierte EVS-App. Die Informationen werden vom Treiber an die EVS-App weitergeleitet, 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 die CameraDesc dieser Kamera zurück.

setMaxFramesInFlight(int32 bufferCount) generates (EvsResult result);

Gibt die Tiefe der Pufferkette an, die die Kamera unterstützen soll. So viele Frames können gleichzeitig vom Client von IEvsCamera gehalten werden. Wenn so viele Frames an den Empfänger gesendet wurden, ohne dass sie von doneWithFrame zurückgegeben wurden, werden im Stream Frames übersprungen, bis ein Puffer zur Wiederverwendung zurückgegeben wird. Dieser Aufruf kann jederzeit erfolgen, auch wenn Streams bereits laufen. In diesem Fall sollten Puffer nach Bedarf hinzugefügt oder aus der Kette entfernt werden. Wenn kein Aufruf dieses Einstiegspunkts erfolgt, unterstützt IEvsCamera standardmäßig mindestens einen Frame. Mehr sind jedoch akzeptabel.

Wenn die angeforderte 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 mit dem zuvor festgelegten Wert weiter betrieben.

startVideoStream(IEvsCameraStream receiver) generates (EvsResult result);

Fordert die Übermittlung von EVS-Kamerabildern von dieser Kamera an. IEvsCameraStream empfängt regelmäßig Aufrufe mit neuen Bildframes, bis stopVideoStream() aufgerufen wird. Die Frames müssen innerhalb von 500 ms nach dem startVideoStream-Aufruf ausgeliefert werden. Nach dem Start müssen sie mit mindestens 10 FPS generiert werden. Die Zeit, die zum Starten des Videostreams benötigt wird, wird auf die 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 gesendet wurde. Wenn ein Frame, der über die IEvsCameraStream-Schnittstelle bereitgestellt wurde, verarbeitet wurde, muss er zur Wiederverwendung an die IEvsCamera zurückgegeben werden. Es ist eine kleine, endliche Anzahl von Puffern verfügbar (möglicherweise nur einer). Wenn der Vorrat erschöpft ist, werden keine weiteren Frames geliefert, bis ein Puffer zurückgegeben wird. Dies kann dazu führen, dass Frames übersprungen werden. Ein Puffer mit einem Null-Handle kennzeichnet das Ende eines Streams und muss nicht über diese Funktion zurückgegeben werden. Gibt bei Erfolg „OK“ zurück oder einen entsprechenden Fehlercode, der möglicherweise INVALID_ARG oder BUFFER_NOT_AVAILABLE enthält.

stopVideoStream();

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

getExtendedInfo(int32 opaqueIdentifier) generates (int32 value);

Fordert treiberspezifische Informationen von der HAL-Implementierung an. Die für opaqueIdentifier zulässigen Werte sind treiberspezifisch. Wenn kein Wert übergeben wird, kann dies zum Absturz des Treibers führen. Der Treiber sollte für alle nicht erkannten 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 Erleichterung fahrzeugspezifischer Erweiterungen bereitgestellt. Für die Funktion einer HAL-Implementierung im Standardzustand ist dieser Aufruf nicht erforderlich. 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 wurde. Der HAL-Treiber 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 möglicherweise erforderlich ist, um das Bild mit EGL mit der Erweiterung eglCreateImageKHR() zu verwenden.

  • width: Die Breite des präsentierten Bildes in Pixeln.
  • height: Die Höhe des präsentierten Bildes in Pixeln.
  • stride: Anzahl der Pixel, die jede Zeile tatsächlich im Arbeitsspeicher belegt, unter Berücksichtigung von Auffüllungen zur Ausrichtung von Zeilen. Wird in Pixeln angegeben, um der von gralloc für die Pufferbeschreibungen verwendeten Konvention zu entsprechen.
  • pixelSize: Anzahl der Byte, die von jedem einzelnen Pixel belegt werden. Damit kann die Größe in Byte berechnet werden, die für den Schritt zwischen den Zeilen im Bild erforderlich ist (stride in Byte = 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 Kameranutzung und RGBA oder BGRA für die Anzeige bevorzugt werden.
  • usage: Von der HAL-Implementierung festgelegte Nutzungs-Flags. HAL-Clients müssen diese unverändert übergeben (weitere Informationen finden Sie unter Gralloc.h-bezogene Flags).
  • bufferId: Ein eindeutiger Wert, der von der HAL-Implementierung angegeben wird, damit ein Puffer nach einem Roundtrip durch die HAL-APIs erkannt werden kann. Der in diesem Feld gespeicherte Wert kann von der HAL-Implementierung beliebig gewählt werden.
  • memHandle: Das Handle für den zugrunde liegenden Speicherpuffer, der die Bilddaten enthält. In der HAL-Implementierung kann ein Gralloc-Puffer-Handle gespeichert werden.

IEvsCameraStream

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

deliverFrame(BufferDesc buffer);

Empfängt Aufrufe von der HAL jedes Mal, wenn ein Videobild für die Prüfung bereit ist. Puffer-Handles, die von dieser Methode empfangen werden, müssen durch Aufrufe von IEvsCamera::doneWithFrame() zurückgegeben werden. Wenn der Videostream mit einem Aufruf von IEvsCamera::stopVideoStream() beendet wird, kann dieser Callback fortgesetzt werden, während die Pipeline geleert wird. Jeder Frame muss weiterhin zurückgegeben werden. Wenn der letzte Frame im Stream gesendet wurde, wird ein NULL-bufferHandle gesendet, das das Ende des Streams signalisiert. Es werden keine weiteren Frames gesendet. Der NULL-bufferHandle selbst muss nicht mit doneWithFrame() zurückgesendet werden, aber alle anderen Handles müssen zurückgegeben werden.

Proprietäre Pufferformate sind zwar technisch möglich, für Kompatibilitätstests muss der Puffer jedoch eines der vier unterstützten Formate haben: 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 nicht auf einer Korrespondenz zwischen dem Feld bufferId und dem Feld memHandle in der Struktur BufferDesc basieren. Die bufferId-Werte sind im Wesentlichen privat für die HAL-Treiberimplementierung und können nach Belieben verwendet und wiederverwendet werden.

IEvsDisplay

Dieses Objekt stellt die EVS-Anzeige dar, steuert den Status der Anzeige und übernimmt die eigentliche Darstellung von Bildern.

getDisplayInfo() generates (DisplayDesc info);

Gibt grundlegende Informationen zum vom System bereitgestellten EVS-Display zurück (siehe DisplayDesc).

setDisplayState(DisplayState state) generates (EvsResult result);

Legt den Anzeigestatus fest. Clients können den Anzeigestatus auf den gewünschten Status festlegen. Die HAL-Implementierung muss eine Anfrage für einen beliebigen Status in einem beliebigen anderen Status problemlos akzeptieren, auch wenn die Antwort darin besteht, die Anfrage zu ignorieren.

Bei der Initialisierung wird das Display so definiert, dass es im Status NOT_VISIBLE startet. Danach wird vom Client erwartet, dass er den Status VISIBLE_ON_NEXT_FRAME anfordert und mit der Bereitstellung von Video beginnt. Wenn die Anzeige nicht mehr erforderlich ist, sollte der Client nach dem letzten Videobild den Status NOT_VISIBLE anfordern.

Es ist gültig, wenn jederzeit ein beliebiger Status angefordert wird. Wenn die Anzeige bereits sichtbar ist, sollte sie auch bei der Einstellung VISIBLE_ON_NEXT_FRAME sichtbar bleiben. Gibt immer „OK“ zurück, es sei denn, der angeforderte Status ist ein unbekannter 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 zum Ändern von Anzeigestatus sollte sich über der Geräteebene befinden. Daher ist es unerwünscht, dass die HAL-Implementierung den Anzeigestatus spontan ändert.

getTargetBuffer() generates (handle bufferHandle);

Gibt ein Handle für einen Framebuffer zurück, der dem Display zugeordnet ist. Dieser Puffer kann von Software und/oder GL gesperrt und beschrieben werden. Dieser Puffer muss mit einem Aufruf von returnTargetBufferForDisplay() zurückgegeben werden, auch wenn das Display nicht mehr sichtbar ist.

Proprietäre Pufferformate sind zwar technisch möglich, für Kompatibilitätstests muss der Puffer jedoch eines der vier unterstützten Formate haben: 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-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 dem Display mit, dass der Puffer angezeigt werden kann. Nur Puffer, die über einen Aufruf von getTargetBuffer() abgerufen wurden, können für diesen Aufruf verwendet werden. Der Inhalt von BufferDesc darf von der Client-App nicht geändert werden. Nach diesem Aufruf kann der Puffer nicht mehr vom Client verwendet werden. 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 eines EVS-Displays und die Anforderungen an eine EVS-Implementierung. Das HAL ist dafür verantwortlich, diese Struktur auszufüllen, um das EVS-Display zu beschreiben. Kann ein physisches oder ein virtuelles Display sein, das über ein anderes Präsentationsgerät gelegt oder mit diesem kombiniert wird.

  • display_id: Ein String, der das Display eindeutig identifiziert. Das 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 darüber undurchsichtig verwendet.
  • vendor_flags. Eine Methode zum Übergeben spezieller Kamerainformationen undurchsichtig vom Treiber an eine benutzerdefinierte EVS-App. Sie wird vom Treiber 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
}

Beschreibt den Status der EVS-Anzeige, die deaktiviert (für den Fahrer nicht sichtbar) oder aktiviert (zeigt dem Fahrer ein Bild) sein kann. Enthält einen vorübergehenden Status, in dem die Anzeige noch nicht sichtbar ist, aber darauf vorbereitet wird, mit der Bereitstellung des nächsten Bildframes mit dem returnTargetBufferForDisplay()-Aufruf sichtbar zu werden.

EVS Manager

Der EVS Manager bietet die öffentliche Schnittstelle zum EVS-System zum Erfassen und Präsentieren externer 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 Manager und der einzige Client, der Displaydaten 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 Dienst, da er mehrere gleichzeitige Clients unterstützt. Das bedeutet, dass mehr als ein Client eine Kamera über den EVS Manager öffnen und einen Videostream empfangen kann.

Diagramm für EVS Manager und EVS Hardware API.
Abbildung 2. Der 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 ausgeführt werden, mit der Ausnahme, dass die EVS Manager API den gleichzeitigen Zugriff auf Kamerastreams ermöglicht. Der EVS Manager ist selbst der einzige zulässige Client der EVS Hardware HAL-Schicht 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 Schnittstellenobjekt ab, das für die Interaktion mit einer bestimmten Kamera verwendet wird, die durch den eindeutigen String camera_id identifiziert wird. Gibt bei einem Fehler NULL zurück. Solange auf der EVS Manager-Ebene genügend Systemressourcen verfügbar sind, kann eine bereits geöffnete Kamera von einem anderen Prozess noch einmal geöffnet werden. So kann der Videostream an mehrere Consumer-Apps weitergeleitet werden. Die camera_id-Strings 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 wird intern virtualisiert, sodass sich Vorgänge an einer Kamera durch einen Client nicht auf andere Clients auswirken, die weiterhin unabhängig auf ihre Kameras zugreifen können.

startVideoStream(IEvsCameraStream receiver) generates (EvsResult result);

Startet Videostreams. Clients können Videostreams auf derselben zugrunde liegenden Kamera unabhängig voneinander starten und stoppen. 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 Kunde muss seine Frames zurückgeben, wenn er sie nicht mehr benötigt. Er darf sie aber so lange behalten, wie er möchte. Wenn die Anzahl der Frames, die ein Client enthält, das konfigurierte Limit erreicht, werden keine weiteren Frames empfangen, bis ein Frame zurückgegeben wird. Das Überspringen von Frames hat keine Auswirkungen auf andere Clients, die weiterhin alle Frames wie erwartet empfangen.

stopVideoStream();

Beendet einen Videostream. Jeder Client kann seinen Videostream jederzeit beenden, ohne dass sich dies auf andere Clients auswirkt. 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);

Sendet einen treiberspezifischen Wert, wodurch ein Client möglicherweise einen anderen Client beeinflussen kann. Da der EVS Manager die Auswirkungen von anbieterspezifischen Steuerwörtern nicht nachvollziehen kann, werden sie nicht virtualisiert und alle Nebenwirkungen gelten für alle Clients einer bestimmten Kamera. Wenn ein Anbieter diesen Aufruf beispielsweise verwendet hat, um die Bildrate zu ändern, würden alle Clients der betroffenen Hardwareebenenkamera Frames mit der neuen Rate empfangen.

IEvsDisplay

Es ist nur ein Eigentümer des Displays zulässig, auch auf EVS Manager-Ebene. Der Manager fügt keine Funktionen hinzu, sondern 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 Vehicle HAL kommuniziert, um grundlegende Rückfahrkamerafunktionen bereitzustellen. Die App soll sehr früh im Systemstartprozess gestartet werden. Je nach verfügbaren Kameras und Zustand des Autos (Gang und Blinker) wird ein geeignetes Video angezeigt. Fahrzeughersteller können die EVS-App mit ihrer eigenen fahrzeugspezifischen Logik und Darstellung ändern oder ersetzen.

Abbildung 3: Beispiellogik für die EVS-App, Kameraleiste abrufen.


Abbildung 4: Beispiellogik für EVS-App, Callback für den Empfang von Frames.

Da Bilddaten der App in einem standardmäßigen Grafikpuffer präsentiert werden, ist die App dafür verantwortlich, das Bild aus dem Quellpuffer in den Ausgabepuffer zu verschieben. Dadurch entstehen zwar Kosten für das Kopieren von Daten, aber die App kann das Bild auf beliebige Weise in den Displaypuffer rendern.

Die App kann beispielsweise die Pixeldaten selbst verschieben, möglicherweise mit einer Inline-Skalierungs- oder ‑Drehungsoperation. Die App kann das Quellbild auch als OpenGL-Textur verwenden und eine komplexe Szene in den Ausgabepuffer rendern, einschließlich virtueller Elemente wie Symbole, Richtlinien und Animationen. Eine anspruchsvollere App kann auch mehrere gleichzeitige Eingabekameras auswählen und sie in einem einzelnen Ausgabebild zusammenführen, z. B. für eine virtuelle Ansicht der Fahrzeugumgebung von oben.

EGL/SurfaceFlinger in der EVS Display HAL verwenden

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

Eine EVS-HAL-Referenzimplementierung verwendet EGL, um die Kameravorschau auf dem Bildschirm zu rendern, und libgui, um die EGL-Zielrenderingoberfläche zu erstellen. In Android 8 und höher wird libgui als VNDK-private klassifiziert. Dies bezieht sich auf eine Gruppe von Bibliotheken, die für VNDK-Bibliotheken verfügbar sind, aber nicht von Anbieterprozessen verwendet werden können. Da sich HAL-Implementierungen auf der Anbieterpartition befinden 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 zum Implementieren von libgui ist die Verwendung von frameworks/native/libs/gui direkt über ein zusätzliches Build-Ziel im Build-Script. Dieses Ziel ist mit dem Ziel libgui 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 SurfaceFlinger das Parcel nicht parsen. Dies wird als fcntl-Fehler angezeigt:

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 das 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 Beispielanleitungen für den Build. Sie erhalten eine $(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

Binder in der EVS-HAL-Implementierung verwenden

In Android 8 und höher ist der Geräteknoten /dev/binder ausschließlich 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 zwar AIDL-Schnittstellen, aber Anbieterprozesse können nur HIDL-Schnittstellen verwenden, um mit Framework-Prozessen zu kommunizieren. Die Konvertierung vorhandener AIDL-Schnittstellen in HIDL erfordert einen nicht unerheblichen Aufwand. Glücklicherweise bietet Android eine Methode, mit der der Binder-Treiber für libbinder ausgewählt werden kann, mit dem die Userspace-Bibliotheksprozesse 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 Methode vor dem Aufrufen von Process oder IPCThreadState oder vor dem Ausführen von Binder-Aufrufen aufrufen.

SELinux-Richtlinien

Wenn die Geräteimplementierung vollständig Treble-kompatibel ist, verhindert SELinux, dass Anbieterprozesse /dev/binder verwenden. Beispiel: Eine EVS HAL-Beispielimplementierung ist der Domain hal_evs_driver zugewiesen und erfordert Lese-/Schreibberechtigungen 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 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 zur Fehlerbehebung und für die Entwicklung bereitgestellt wird. Es kann auch verwendet werden, 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 an packages/services/Car/evs/Android.mk vornehmen. 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;