Android enthält eine HIDL Hardware Abstraction Layer (HAL) für die Automobilbranche, die schon sehr früh im Android-Bootvorgang für die Aufnahme und Anzeige von Bildern sorgt und während der gesamten Lebensdauer des Systems 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. 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ü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:
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
) stellt die Bausteine bereit, die eine EVS-App benötigt, um alles von einem einfachen Rückkameradisplay bis hin zum 6DOF-Multi-Kamera-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 Service) 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 mit 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- 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
Mit diesem Objekt wird die verfügbare EVS-Hardware im System aufgelistet (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 vorweggenommen wurde, muss in den Inaktivitätsstatus versetzt werden, bis sie endgültig gelöscht wird. Außerdem muss sie auf jede Anfrage, die den Kamerastatus betrifft, mit dem Rückgabecode OWNERSHIP_LOST
reagieren.
closeCamera(IEvsCamera camera);
Gibt die IEvsCamera-Oberfläche frei (und ist das Gegenteil des openCamera()
-Aufrufs). Der Kameravideostream muss durch Aufrufen von stopVideoStream()
angehalten werden, bevor closeCamera
aufgerufen wird.
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);
Gibt die IEvsDisplay-Oberfläche frei (und ist 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, 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 Gerätename des Kernels oder ein Name für das Gerät sein, z. B. rückkamera. 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 CameraDesc
dieser Kamera zurück.
setMaxFramesInFlight(int32 bufferCount) generates (EvsResult result);
Gibt die Tiefe der Pufferkette an, die von der Kamera unterstützt werden soll. Bis zu diese Anzahl von Frames kann vom Client von IEvsCamera gleichzeitig gehalten werden. Wenn diese Anzahl von Frames an den Empfänger geliefert wurde, ohne von doneWithFrame
zurückgegeben zu werden, überspringt der Stream die Frames, bis ein Zwischenspeicher 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 dieser Einstiegspunkt nicht aufgerufen wird, unterstützt die IEvsCamera standardmäßig mindestens einen Frame. Mehr ist akzeptabel.
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 die Schließung des Streams an den IEvsCameraStream signalisiert wird. Es ist zulässig, stopVideoStream
für einen Stream aufzurufen, der bereits angehalten oder nie gestartet wurde. In diesem Fall wird er ignoriert.
getExtendedInfo(int32 opaqueIdentifier) generates (int32 value);
Erfordert treiberspezifische Informationen von der HAL-Implementierung. Die für opaqueIdentifier
zulässigen Werte sind treiberspezifisch. Wenn kein Wert übergeben wird, kann der Treiber abstürzen. 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 mit EGL mit der eglCreateImageKHR()
-Erweiterung 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, sollteHAL_PIXEL_FORMAT_YCRCB_420_SP
für die Kamera undRGBA
oderBGRA
für das Display verwendet werden.usage
. Nutzungsflags, die von der HAL-Implementierung festgelegt werden. Es wird erwartet, dass HAL-Clients diese unveränderten übergeben. Weitere Informationen dazu finden Sie unterGralloc.h
-Flags.bufferId
: Ein eindeutiger Wert, der von der HAL-Implementierung angegeben wird, damit ein Puffer nach einem Durchlauf durch die HAL-APIs erkannt werden kann. Der in diesem Feld gespeicherte Wert kann von der HAL-Implementierung willkürlich ausgewählt werden.memHandle
: Der Handle für den zugrunde liegenden Arbeitsspeicherbuffer, der die Bilddaten enthält. In der HAL-Implementierung kann hier ein Gralloc-Zwischenspeicher-Handle gespeichert werden.
IEvsCameraStream
Der Client implementiert diese Schnittstelle, um asynchrone Videoframe-Übermittlungen zu empfangen.
deliverFrame(BufferDesc buffer);
Erhält von der HAL jedes Mal eine Anfrage, wenn ein Videoframe zur Überprüfung bereit ist.
Von dieser Methode empfangene Puffer-Handles müssen über Aufrufe von IEvsCamera::doneWithFrame()
zurückgegeben werden. Wenn der Videostream mit einem Aufruf von IEvsCamera::stopVideoStream()
angehalten wird, wird dieser Rückruf möglicherweise fortgesetzt, während die Pipeline abläuft. Jeder Frame muss zurückgegeben werden. Wenn der letzte Frame im Stream gesendet wurde, wird ein NULL-bufferHandle
gesendet, was das Ende des Streams bedeutet und es keine weiteren Frame-Übermittlungen gibt. Der NULL-Wert bufferHandle
muss nicht mit 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 App darf keine Korrespondenz zwischen dem Feld bufferId
und dem memHandle
in der Struktur BufferDesc
voraussetzen. 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 vom System bereitgestellten EVS-Display zurück (siehe DisplayDesc).
setDisplayState(DisplayState state) generates (EvsResult result);
Legt den Anzeigestatus fest. Clients können den Displaystatus so festlegen, dass der gewünschte Status angezeigt wird. Die HAL-Implementierung muss eine Anfrage für jeden Status akzeptieren, auch wenn die Antwort 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.
Es kann jederzeit ein beliebiger Status 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 ein Handle für einen Frame-Zwischenspeicher zurück, der der Anzeige 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.
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 Puffer, die über einen Aufruf von getTargetBuffer()
abgerufen wurden, sind für diesen Aufruf gültig. Der Inhalt von BufferDesc
darf von der Clientanwendung 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 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. Das kann der Gerätename des Kernels oder ein Name für das Gerät sein, z. B. rückkamera. Der Wert für diesen String wird von der HAL-Implementierung ausgewählt und vom obigen Stack undurchsichtig 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.
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 Status, in dem das Display noch nicht sichtbar ist, aber für die Sichtbarkeit vorbereitet wird, sobald der nächste Bildframe mit dem returnTargetBufferForDisplay()
-Aufruf gesendet wird.
EVS-Manager
Der EVS-Manager stellt die öffentliche Schnittstelle zum EVS-System für die Erfassung und Darstellung externer Kameraansichten bereit. 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 durch die Unterstützung mehrerer gleichzeitiger Clients (mehr als ein Client kann eine Kamera über den EVS Manager öffnen und einen Videostream empfangen).
Anwendungen sehen keine Unterschiede beim Betrieb mit der EVS Hardware HAL-Implementierung oder der EVS Manager API, außer dass die EVS Manager API gleichzeitigen Zugriff auf Kamerastreams 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 übrigen Aufrufe entsprechen den EVS HAL-Beschreibungen.
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.
Auf der EVS Manager-Ebene kann, sofern genügend Systemressourcen verfügbar sind, eine bereits geöffnete Kamera durch einen anderen Prozess wieder geöffnet werden. So kann der Videostream an mehrere Nutzeranwendungen übertragen werden. Die camera_id
-Strings auf der EVS Manager-Ebene sind mit denen identisch, die an die EVS-Hardwareebene gesendet werden.
IEvsCamera
Der EVS Manager hat die IEvsCamera-Implementierung intern virtualisiert, sodass Vorgänge auf einer Kamera durch einen Client keine Auswirkungen auf andere Clients haben, da diese den unabhängigen Zugriff auf ihre Kameras erhalten.
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 startet, 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 von einem Client gehaltene Frame-Anzahl das konfigurierte Limit erreicht, empfängt er keine weiteren Frames, bis er einen zurückgibt. Das Überspringen von Frames wirkt sich nicht auf andere Clients aus, 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.
IEvsDisplay
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 dem Fahrzeug-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.
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 bringt zwar die Kosten für eine Datenkopie mit sich, bietet der App aber auch die Möglichkeit, das Bild beliebig im Displayzwischenspeicher zu rendern.
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 komplexere App kann auch mehrere gleichzeitige Eingabekameras auswählen und in einem einzigen Ausgabeframe zusammenführen, z. B. zur Verwendung in einer virtuellen Draufsicht der Fahrzeugumgebung.
EGL/SurfaceFlinger im EVS-Display-HAL verwenden
In diesem Abschnitt wird beschrieben, 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. In Android 8 (und höher) wird libgui
als VNDK-privat klassifiziert. Das 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 in der Anbieterpartition befinden müssen, wird verhindert, dass Anbieter Surface 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 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
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 |
Während SurfaceFlinger AIDL-Schnittstellen definiert, können Anbieterprozesse nur HIDL-Schnittstellen verwenden, um mit Framework-Prozessen zu kommunizieren. Es ist ein nicht unerheblicher Aufwand erforderlich, um vorhandene AIDL-Schnittstellen in HIDL umzuwandeln. 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. Beispiel: Einer EVS HAL-Beispielimplementierung ist die Domain hal_evs_driver
zugewiesen und sie 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, 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. Vergewissern Sie sich, dass alle beschriebenen Änderungen für Ihre Implementierung geeignet sind.
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;