Kamera pojazdu HAL

Android zawiera samochodową warstwę abstrakcji sprzętu HIDL (HAL), która umożliwia przechwytywanie i wyświetlanie obrazów na bardzo wczesnym etapie procesu uruchamiania Androida i działa przez cały okres użytkowania systemu. HAL obejmuje zestaw systemu widoku zewnętrznego (EVS) i jest zwykle używany do obsługi kamer cofania i wyświetlaczy widoku przestrzennego w pojazdach wyposażonych w pokładowe systemy informacyjno-rozrywkowe (IVI) oparte na systemie Android. EVS umożliwia także wdrażanie zaawansowanych funkcji w aplikacjach użytkownika.

Android zawiera także interfejs sterownika przechwytywania i wyświetlania specyficzny dla EVS (w /hardware/interfaces/automotive/evs/1.0 ). Chociaż możliwe jest zbudowanie aplikacji kamery cofania w oparciu o istniejące usługi kamer i wyświetlaczy w systemie Android, taka aplikacja prawdopodobnie uruchomiłaby się zbyt późno w procesie uruchamiania systemu Android. Korzystanie z dedykowanej warstwy HAL umożliwia usprawnienie interfejsu i wyjaśnia, co producent OEM musi wdrożyć, aby obsługiwać stos EVS.

Elementy systemu

EVS obejmuje następujące elementy systemu:

Schemat komponentów systemu EVS
Rysunek 1. Przegląd elementów systemu EVS

Aplikacja EVS

Przykładowa aplikacja C++ EVS ( /packages/services/Car/evs/app ) służy jako implementacja referencyjna. Ta aplikacja odpowiada za żądanie klatek wideo od Menedżera EVS i wysyłanie gotowych klatek do wyświetlenia z powrotem do Menedżera EVS. Oczekuje się, że zostanie uruchomiony przez init, gdy tylko EVS i serwis samochodowy będą dostępne, w ciągu dwóch (2) sekund od włączenia zasilania. Producenci OEM mogą modyfikować lub wymieniać aplikację EVS według potrzeb.

Menedżer EVS

Menedżer EVS ( /packages/services/Car/evs/manager ) zapewnia elementy potrzebne aplikacji EVS do wdrożenia czegokolwiek, od prostego wyświetlacza z kamery cofania po renderowanie z wielu kamer 6DOF. Jego interfejs jest prezentowany poprzez HIDL i jest zbudowany tak, aby akceptować wielu jednoczesnych klientów. Inne aplikacje i usługi (w szczególności Car Service) mogą wysyłać zapytania o stan Menedżera EVS, aby dowiedzieć się, kiedy system EVS jest aktywny.

Interfejs EVS HIDL

System EVS, zarówno kamera, jak i elementy wyświetlacza, zdefiniowany jest w pakiecie android.hardware.automotive.evs . Przykładowa implementacja ćwicząca interfejs (generująca syntetyczne obrazy testowe i sprawdzająca, czy obrazy działają w obie strony) znajduje się w /hardware/interfaces/automotive/evs/1.0/default .

Producent OEM jest odpowiedzialny za wdrożenie API wyrażonego w plikach .hal w /hardware/interfaces/automotive/evs . Takie implementacje odpowiadają za konfigurowanie i zbieranie danych z kamer fizycznych oraz dostarczanie ich za pośrednictwem buforów pamięci współdzielonej rozpoznawalnych przez Gralloc. Strona wyświetlająca implementacji jest odpowiedzialna za zapewnienie bufora pamięci współdzielonej, który może zostać wypełniony przez aplikację (zwykle poprzez renderowanie EGL) i prezentowanie gotowych klatek zamiast czegokolwiek innego, co mogłoby pojawić się na fizycznym wyświetlaczu. Implementacje interfejsu EVS dostawcy mogą być przechowywane w katalogu /vendor/… /device/… lub hardware/… (np. /hardware/[vendor]/[platform]/evs ).

Sterowniki jądra

Urządzenie obsługujące stos EVS wymaga sterowników jądra. Zamiast tworzyć nowe sterowniki, producenci OEM mają możliwość obsługi funkcji wymaganych przez EVS za pośrednictwem istniejących sterowników sprzętu aparatu i/lub wyświetlacza. Ponowne użycie sterowników może być korzystne, szczególnie w przypadku sterowników ekranu, gdzie prezentacja obrazu może wymagać koordynacji z innymi aktywnymi wątkami. Android 8.0 zawiera przykładowy sterownik oparty na wersji 4l2 (w packages/services/Car/evs/sampleDriver ), który zależy od jądra pod kątem obsługi wersji 4l2 i SurfaceFlingera do prezentacji obrazu wyjściowego.

Opis interfejsu sprzętowego EVS

W tej sekcji opisano HAL. Oczekuje się, że dostawcy zapewnią implementacje tego interfejsu API dostosowane do ich sprzętu.

IEvsEnumerator

Obiekt ten odpowiada za wyliczenie dostępnego w systemie sprzętu EVS (jedna lub więcej kamer i jedno urządzenie wyświetlające).

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

Zwraca wektor zawierający opisy wszystkich kamer w systemie. Zakłada się, że zestaw kamer jest stały i możliwy do rozpoznania w momencie uruchamiania systemu. Aby uzyskać szczegółowe informacje na temat opisów kamer, zobacz CameraDesc .

openCamera(string camera_id) generates (IEvsCamera camera);

Uzyskuje obiekt interfejsu używany do interakcji z określoną kamerą identyfikowaną przez unikalny ciąg Camera_id . Zwraca wartość NULL w przypadku niepowodzenia. Próby ponownego otwarcia kamery, która jest już otwarta, nie mogą się nie powieść. Aby uniknąć sytuacji wyścigowych związanych z uruchamianiem i zamykaniem aplikacji, ponowne otwarcie kamery powinno zamknąć poprzednią instancję, aby nowe żądanie mogło zostać spełnione. Wywłaszczona w ten sposób instancja kamery musi zostać przeprowadzona w stan nieaktywny, oczekując na ostateczne zniszczenie i odpowiadając na każdą prośbę o wpływ na stan kamery kodem zwrotnym OWNERSHIP_LOST .

closeCamera(IEvsCamera camera);

Zwalnia interfejs IEvsCamera (i jest przeciwieństwem wywołania openCamera() ). Strumień wideo z kamery musi zostać zatrzymany poprzez wywołanie stopVideoStream() przed wywołaniem closeCamera .

openDisplay() generates (IEvsDisplay display);

Uzyskuje obiekt interfejsu używany wyłącznie do interakcji z wyświetlaczem EVS systemu. Tylko jeden klient może w danym momencie posiadać funkcjonalną instancję IEvsDisplay. Podobnie do agresywnego zachowania przy otwieraniu opisanego w openCamera , w dowolnym momencie można utworzyć nowy obiekt IEvsDisplay, który wyłączy wszystkie poprzednie instancje. Unieważnione instancje nadal istnieją i odpowiadają na wywołania funkcji od swoich właścicieli, ale nie mogą wykonywać żadnych operacji mutowania, gdy są martwe. Oczekuje się, że ostatecznie aplikacja kliencka zauważy kody zwrotne błędu OWNERSHIP_LOST oraz zamknie i zwolni nieaktywny interfejs.

closeDisplay(IEvsDisplay display);

Zwalnia interfejs IEvsDisplay (i jest przeciwieństwem wywołania openDisplay() ). Zaległe bufory otrzymane poprzez wywołania getTargetBuffer() muszą zostać zwrócone na wyświetlacz przed zamknięciem wyświetlacza.

getDisplayState() generates (DisplayState state);

Pobiera bieżący stan wyświetlania. Implementacja HAL powinna raportować rzeczywisty bieżący stan, który może różnić się od ostatnio żądanego stanu. Logika odpowiedzialna za zmianę stanów wyświetlania powinna istnieć nad warstwą urządzenia, co sprawia, że ​​implementacja HAL nie powinna spontanicznie zmieniać stanów wyświetlania. Jeżeli wyświetlacz nie jest aktualnie utrzymywany przez żadnego klienta (poprzez wywołanie openDisplay), to funkcja ta zwraca NOT_OPEN . W przeciwnym razie raportuje bieżący stan wyświetlacza EVS (patrz API IEvsDisplay ).

struct CameraDesc {
    string      camera_id;
    int32       vendor_flags;       // Opaque value
}
  • camera_id . Ciąg znaków, który jednoznacznie identyfikuje daną kamerę. Może to być nazwa urządzenia jądra lub nazwa urządzenia, np. Rearview . Wartość tego ciągu jest wybierana przez implementację HAL i używana w sposób nieprzezroczysty przez powyższy stos.
  • vendor_flags . Metoda nieprzejrzystego przekazywania specjalistycznych informacji o kamerze od kierowcy do niestandardowej aplikacji EVS. Jest on przekazywany w niezinterpretowanej formie od kierowcy do aplikacji EVS, która może go zignorować.

Kamera IEvs

Obiekt ten reprezentuje pojedynczą kamerę i jest głównym interfejsem do przechwytywania obrazów.

getCameraInfo() generates (CameraDesc info);

Zwraca CameraDesc tej kamery.

setMaxFramesInFlight(int32 bufferCount) generates (EvsResult result);

Określa głębokość łańcucha buforów, który kamera ma obsługiwać. Klient IEvsCamera może jednocześnie przechowywać wiele klatek. Jeśli do odbiornika dostarczono taką liczbę ramek, a nie zostały one zwrócone przez doneWithFrame , strumień pomija ramki do czasu zwrócenia buforu do ponownego użycia. Wywołanie to jest legalne w dowolnym momencie, nawet gdy strumienie są już uruchomione, w takim przypadku należy odpowiednio dodać lub usunąć bufory z łańcucha. Jeśli nie zostanie wykonane żadne wywołanie do tego punktu wejścia, IEvsCamera domyślnie obsługuje co najmniej jedną ramkę; z bardziej akceptowalnymi.

Jeśli żądany buforCount nie może zostać obsłużony, funkcja zwraca BUFFER_NOT_AVAILABLE lub inny odpowiedni kod błędu. W takim przypadku system kontynuuje pracę z wcześniej ustawioną wartością.

startVideoStream(IEvsCameraStream receiver) generates (EvsResult result);

Żąda dostawy klatek aparatu EVS z tego aparatu. Metoda IEvsCameraStream zaczyna odbierać okresowe wywołania z nowymi klatkami obrazu, aż do wywołania funkcji stopVideoStream() . Dostarczanie ramek musi rozpocząć się w ciągu 500 ms od wywołania startVideoStream , a po uruchomieniu muszą być generowane z szybkością co najmniej 10 klatek na sekundę. Czas wymagany do uruchomienia strumienia wideo wlicza się do wymaganego czasu uruchomienia kamery cofania. Jeżeli strumień nie zostanie uruchomiony, należy zwrócić kod błędu; w przeciwnym razie zwracane jest OK.

oneway doneWithFrame(BufferDesc buffer);

Zwraca ramkę dostarczoną przez do IEvsCameraStream. Po zakończeniu zużywania ramki dostarczonej do interfejsu IEvsCameraStream ramka musi zostać zwrócona do IEvsCamera w celu ponownego użycia. Dostępna jest niewielka, skończona liczba buforów (prawdopodobnie tak mała jak jeden), a jeśli zasoby się wyczerpią, nie są dostarczane żadne dalsze ramki, dopóki bufor nie zostanie zwrócony, co może skutkować pominięciem ramek (bufor z uchwytem zerowym oznacza koniec strumienia i nie musi być zwracany przez tę funkcję). Zwraca OK w przypadku powodzenia lub odpowiedni kod błędu, potencjalnie zawierający INVALID_ARG lub BUFFER_NOT_AVAILABLE .

stopVideoStream();

Wstrzymuje dostawę ramek do kamer EVS. Ponieważ dostarczanie jest asynchroniczne, ramki mogą nadal docierać przez pewien czas po powrocie tego wywołania. Każda ramka musi zostać zwrócona, dopóki zamknięcie strumienia nie zostanie zasygnalizowane w IEvsCameraStream. Wywołanie stopVideoStream w przypadku strumienia, który został już zatrzymany lub nigdy nie został uruchomiony, jest dozwolone i w takim przypadku jest ono ignorowane.

getExtendedInfo(int32 opaqueIdentifier) generates (int32 value);

Żąda informacji specyficznych dla sterownika z implementacji HAL. Wartości dozwolone dla opaqueIdentifier są specyficzne dla sterownika, ale żadna przekazana wartość nie może spowodować awarii sterownika. Sterownik powinien zwrócić 0 dla każdego nierozpoznanego opaqueIdentifier .

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

Wysyła wartość specyficzną dla sterownika do implementacji HAL. To rozszerzenie jest udostępniane wyłącznie w celu ułatwienia rozszerzeń specyficznych dla pojazdu i żadna implementacja HAL nie powinna wymagać działania tego wywołania w stanie domyślnym. Jeżeli sterownik rozpozna i zaakceptuje wartości, powinien zostać zwrócony OK; w przeciwnym razie powinien zostać zwrócony INVALID_ARG lub inny reprezentatywny kod błędu.

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
}

Opisuje obraz przesłany przez interfejs API. Napęd HAL jest odpowiedzialny za wypełnienie tej struktury w celu opisania bufora obrazu, a klient HAL powinien traktować tę strukturę jako tylko do odczytu. Pola zawierają wystarczającą ilość informacji, aby umożliwić klientowi zrekonstruowanie obiektu ANativeWindowBuffer , co może być wymagane do użycia obrazu z EGL poprzez rozszerzenie eglCreateImageKHR() .

  • width . Szerokość w pikselach prezentowanego obrazu.
  • height . Wysokość w pikselach prezentowanego obrazu.
  • stride . Liczba pikseli faktycznie zajmowanych przez każdy wiersz w pamięci, uwzględniająca wszelkie dopełnienie w celu wyrównania wierszy. Wyrażony w pikselach, aby dopasować konwencję przyjętą przez graloc dla opisów buforów.
  • pixelSize . Liczba bajtów zajmowanych przez każdy pojedynczy piksel, umożliwiająca obliczenie rozmiaru w bajtach niezbędnego do przejścia pomiędzy wierszami obrazu ( stride w bajtach = stride w pikselach * pixelSize ).
  • format . Format pikseli używany przez obraz. Dostarczony format musi być zgodny z implementacją OpenGL platformy. Aby przejść testy zgodności, do użycia z kamerą należy preferować HAL_PIXEL_FORMAT_YCRCB_420_SP , a do wyświetlania RGBA lub BGRA .
  • usage . Flagi użycia ustawione przez implementację HAL. Oczekuje się, że klienci HAL przekażą te zmiany w postaci niezmodyfikowanej (więcej szczegółów można znaleźć w sekcji flagi powiązane z Gralloc.h ).
  • bufferId . Unikalna wartość określona przez implementację HAL, umożliwiająca rozpoznanie bufora po przejściu w obie strony przez interfejsy API HAL. Wartość przechowywana w tym polu może zostać dowolnie wybrana przez implementację HAL.
  • memHandle . Dojście do bazowego buforu pamięci zawierającego dane obrazu. Implementacja HAL może zdecydować się na przechowywanie tutaj uchwytu bufora Gralloc.

Strumień IEvsCamera

Klient implementuje ten interfejs w celu odbierania asynchronicznych dostaw klatek wideo.

deliverFrame(BufferDesc buffer);

Odbiera wywołania z HAL za każdym razem, gdy klatka wideo jest gotowa do kontroli. Uchwyty buforów odebrane tą metodą muszą zostać zwrócone poprzez wywołania IEvsCamera::doneWithFrame() . Gdy strumień wideo zostanie zatrzymany przez wywołanie IEvsCamera::stopVideoStream() , to wywołanie zwrotne może być kontynuowane w miarę wyczerpania się potoku. Każda ramka nadal musi zostać zwrócona; kiedy ostatnia ramka w strumieniu zostanie dostarczona, dostarczony zostanie bufor NULL, oznaczający koniec strumienia i dalsze dostarczanie ramek nie będzie miało miejsca. Sam NULL buforHandle nie musi być odsyłany za pomocą doneWithFrame() , ale wszystkie inne uchwyty muszą zostać zwrócone

Chociaż zastrzeżone formaty buforów są technicznie możliwe, testowanie zgodności wymaga, aby bufor był w jednym z czterech obsługiwanych formatów: NV21 (YCrCb 4:2:0 półpłaski), YV12 (YCrCb 4:2:0 planarny), YUYV (YCrCb 4: 2:2 z przeplotem), RGBA (32 bity R:G:B:x), BGRA (32 bity B:G:R:x). Wybrany format musi być prawidłowym źródłem tekstury GL w implementacji GLES platformy.

Aplikacja nie powinna opierać się na żadnej korespondencji pomiędzy polem bufferId a memHandle w strukturze BufferDesc . Wartości bufferId są zasadniczo prywatne dla implementacji sterownika HAL i może je używać (i ponownie wykorzystywać) według własnego uznania.

IEvsDisplay

Obiekt ten reprezentuje wyświetlacz EVS, steruje stanem wyświetlacza i obsługuje faktyczną prezentację obrazów.

getDisplayInfo() generates (DisplayDesc info);

Zwraca podstawowe informacje o wyświetlaczu EVS dostarczonym przez system (zobacz DisplayDesc ).

setDisplayState(DisplayState state) generates (EvsResult result);

Ustawia stan wyświetlania. Klienci mogą ustawić stan wyświetlania tak, aby wyrażał pożądany stan, a implementacja HAL musi bezproblemowo zaakceptować żądanie dotyczące dowolnego stanu, będąc w dowolnym innym stanie, chociaż odpowiedzią może być zignorowanie żądania.

Po inicjalizacji wyświetlanie rozpoczyna się w stanie NOT_VISIBLE , po czym oczekuje się, że klient zażąda stanu VISIBLE_ON_NEXT_FRAME i rozpocznie dostarczanie wideo. Gdy wyświetlanie nie jest już potrzebne, oczekuje się, że klient zażąda stanu NOT_VISIBLE po przejściu ostatniej klatki wideo.

Obowiązkiem jest żądanie dowolnego stanu w dowolnym momencie. Jeśli wyświetlacz jest już widoczny, powinien pozostać widoczny, jeśli jest ustawiony na VISIBLE_ON_NEXT_FRAME . Zawsze zwraca OK, chyba że żądany stan jest nierozpoznaną wartością wyliczeniową. W takim przypadku zwracana jest INVALID_ARG .

getDisplayState() generates (DisplayState state);

Pobiera stan wyświetlania. Implementacja HAL powinna raportować rzeczywisty bieżący stan, który może różnić się od ostatnio żądanego stanu. Logika odpowiedzialna za zmianę stanów wyświetlania powinna istnieć nad warstwą urządzenia, co sprawia, że ​​implementacja HAL nie powinna spontanicznie zmieniać stanów wyświetlania.

getTargetBuffer() generates (handle bufferHandle);

Zwraca uchwyt do bufora ramki powiązanego z wyświetlaczem. Bufor ten może być blokowany i zapisywany przez oprogramowanie i/lub GL. Bufor ten musi zostać zwrócony poprzez wywołanie metody returnTargetBufferForDisplay() nawet jeśli ekran nie jest już widoczny.

Chociaż zastrzeżone formaty buforów są technicznie możliwe, testowanie zgodności wymaga, aby bufor był w jednym z czterech obsługiwanych formatów: NV21 (YCrCb 4:2:0 półpłaski), YV12 (YCrCb 4:2:0 planarny), YUYV (YCrCb 4: 2:2 z przeplotem), RGBA (32 bity R:G:B:x), BGRA (32 bity B:G:R:x). Wybrany format musi być prawidłowym celem renderowania GL w implementacji GLES platformy.

W przypadku błędu zwracany jest bufor z uchwytem zerowym, ale taki bufor nie musi być przekazywany z powrotem do returnTargetBufferForDisplay .

returnTargetBufferForDisplay(handle bufferHandle) generates (EvsResult result);

Informuje wyświetlacz, że bufor jest gotowy do wyświetlenia. Tylko bufory pobrane poprzez wywołanie metody getTargetBuffer() mogą być używane w tym wywołaniu, a zawartość BufferDesc nie może być modyfikowana przez aplikację kliencką. Po tym wywołaniu bufor nie nadaje się już do użytku przez klienta. Zwraca OK w przypadku powodzenia lub odpowiedni kod błędu, potencjalnie zawierający INVALID_ARG lub BUFFER_NOT_AVAILABLE .

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

Opisuje podstawowe właściwości wyświetlacza EVS wymagane przy wdrażaniu EVS. HAL jest odpowiedzialny za wypełnienie tej struktury w celu opisania wyświetlacza EVS. Może to być wyświetlacz fizyczny lub wyświetlacz wirtualny nałożony na inne urządzenie prezentacyjne lub zmieszany z nim.

  • display_id . Ciąg, który jednoznacznie identyfikuje wyświetlacz. Może to być nazwa urządzenia jądra lub nazwa urządzenia, np. rearview . Wartość tego ciągu jest wybierana przez implementację HAL i używana w sposób nieprzezroczysty przez powyższy stos.
  • vendor_flags . Metoda nieprzejrzystego przekazywania specjalistycznych informacji o kamerze ze sterownika do niestandardowej aplikacji EVS. Jest on przekazywany w niezinterpretowanej formie od kierowcy do aplikacji EVS, która może go zignorować.
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
}

Opisuje stan wyświetlacza EVS, który można wyłączyć (niewidoczny dla kierowcy) lub włączyć (pokazując obraz kierowcy). Obejmuje stan przejściowy, w którym wyświetlacz nie jest jeszcze widoczny, ale jest gotowy, aby stać się widoczny wraz z dostarczeniem następnej klatki obrazu za pośrednictwem wywołania returnTargetBufferForDisplay() .

Menedżer EVS

Menedżer EVS zapewnia publiczny interfejs systemu EVS umożliwiający gromadzenie i prezentowanie widoków z kamer zewnętrznych. Tam, gdzie sterowniki sprzętu dopuszczają tylko jeden aktywny interfejs na zasób (aparat lub wyświetlacz), Menedżer EVS ułatwia współdzielony dostęp do kamer. Pojedyncza główna aplikacja EVS jest pierwszym klientem Menedżera EVS i jedynym klientem uprawnionym do zapisywania wyświetlanych danych (dodatkowym klientom można przyznać dostęp tylko do odczytu do obrazów z kamer).

Menedżer EVS implementuje ten sam interfejs API, co podstawowe sterowniki HAL i zapewnia rozszerzoną usługę, obsługując wielu jednoczesnych klientów (więcej niż jeden klient może otworzyć kamerę za pośrednictwem Menedżera EVS i odbierać strumień wideo).

Schemat API menedżera EVS i sprzętu EVS.
Rysunek 2. Menedżer EVS odzwierciedla podstawowy sprzętowy interfejs API EVS

Aplikacje nie widzą żadnych różnic podczas działania poprzez implementację HAL sprzętu EVS lub API EVS Manager, z wyjątkiem tego, że API EVS Manager umożliwia jednoczesny dostęp do strumienia z kamer. Menedżer EVS sam w sobie jest jedynym dozwolonym klientem warstwy sprzętowej HAL EVS i działa jako serwer proxy dla sprzętowej warstwy HAL EVS.

W poniższych sekcjach opisano tylko te zaproszenia, które mają inne (rozszerzone) zachowanie w przypadku wdrożenia Menedżera EVS; pozostałe wywołania są identyczne z opisami EVS HAL.

IEvsEnumerator

openCamera(string camera_id) generates (IEvsCamera camera);

Uzyskuje obiekt interfejsu używany do interakcji z określoną kamerą identyfikowaną przez unikalny ciąg Camera_id . Zwraca wartość NULL w przypadku niepowodzenia. W warstwie Menedżera EVS, o ile dostępne są wystarczające zasoby systemowe, otwarta już kamera może zostać ponownie otwarta przez inny proces, umożliwiając skierowanie strumienia wideo do wielu aplikacji konsumenckich. Ciągi camera_id w warstwie Menedżera EVS są takie same, jak te zgłaszane w warstwie Sprzętu EVS.

Kamera IEvs

Menedżer EVS zapewnił, że implementacja IEvsCamera jest wewnętrznie zwirtualizowana, więc operacje na kamerze wykonywane przez jednego klienta nie mają wpływu na innych klientów, którzy zachowują niezależny dostęp do swoich kamer.

startVideoStream(IEvsCameraStream receiver) generates (EvsResult result);

Uruchamia strumienie wideo. Klienci mogą niezależnie uruchamiać i zatrzymywać strumienie wideo na tej samej kamerze. Podstawowa kamera uruchamia się wraz z uruchomieniem pierwszego klienta.

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

Zwraca ramkę. Każdy klient musi zwrócić swoje ramki po zakończeniu, ale może zatrzymać je tak długo, jak sobie tego życzy. Kiedy liczba ramek przechowywanych przez klienta osiągnie skonfigurowany limit, nie otrzyma on więcej ramek, dopóki nie zwróci jednej. To pomijanie ramek nie ma wpływu na innych klientów, którzy nadal odbierają wszystkie ramki zgodnie z oczekiwaniami.

stopVideoStream();

Zatrzymuje strumień wideo. Każdy klient może zatrzymać strumień wideo w dowolnym momencie, bez wpływu na innych klientów. Podstawowy strumień kamery w warstwie sprzętowej zostaje zatrzymany, gdy ostatni klient danej kamery zatrzyma swój strumień.

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

Wysyła wartość specyficzną dla sterownika, potencjalnie umożliwiając jednemu klientowi wpływ na innego klienta. Ponieważ Menedżer EVS nie jest w stanie zrozumieć konsekwencji słów kontrolnych zdefiniowanych przez dostawcę, nie są one zwirtualizowane i wszelkie skutki uboczne dotyczą wszystkich klientów danej kamery. Na przykład, jeśli sprzedawca użył tego wywołania do zmiany szybkości klatek, wszyscy klienci kamery z warstwą sprzętową, której dotyczy problem, otrzymają klatki z nową szybkością.

IEvsDisplay

Dozwolony jest tylko jeden właściciel ekspozycji, nawet na poziomie Menedżera EVS. Menedżer nie dodaje żadnej funkcjonalności i po prostu przekazuje interfejs IEvsDisplay bezpośrednio do podstawowej implementacji HAL.

Aplikacja EVS

Android zawiera natywną implementację referencyjną C++ aplikacji EVS, która komunikuje się z menedżerem EVS i HAL pojazdu, aby zapewnić podstawowe funkcje kamery cofania. Oczekuje się, że aplikacja uruchomi się bardzo wcześnie w procesie uruchamiania systemu, a odpowiedni obraz będzie wyświetlany w zależności od dostępnych kamer i stanu samochodu (stan biegu i kierunkowskazów). Producenci OEM mogą modyfikować lub zastępować aplikację EVS własną logiką i prezentacją specyficzną dla pojazdu.

Rysunek 3. Przykładowa logika aplikacji EVS, pobierz listę kamer.


Rysunek 4. Przykładowa logika aplikacji EVS, wywołanie zwrotne odbioru ramki.

Ponieważ dane obrazu są prezentowane aplikacji w standardowym buforze graficznym, aplikacja odpowiada za przeniesienie obrazu z bufora źródłowego do bufora wyjściowego. Chociaż powoduje to wzrost kosztów kopii danych, daje również aplikacji możliwość renderowania obrazu do bufora wyświetlania w dowolny sposób.

Na przykład aplikacja może sama przenieść dane pikseli, potencjalnie za pomocą wbudowanej operacji skalowania lub obrotu. Aplikacja może również użyć obrazu źródłowego jako tekstury OpenGL i wyrenderować złożoną scenę do bufora wyjściowego, łącznie z elementami wirtualnymi, takimi jak ikony, linie pomocnicze i animacje. Bardziej wyrafinowana aplikacja może również wybrać wiele jednoczesnych kamer wejściowych i połączyć je w jedną ramkę wyjściową (na przykład do wykorzystania w wirtualnym widoku z góry na otoczenie pojazdu).

Użyj EGL/SurfaceFlinger na wyświetlaczu EVS HAL

W tej sekcji wyjaśniono, jak używać EGL do renderowania implementacji HAL wyświetlacza EVS w systemie Android 10.

Implementacja referencyjna EVS HAL wykorzystuje EGL do renderowania podglądu kamery na ekranie i używa libgui do tworzenia docelowej powierzchni renderowania EGL. W systemie Android 8 (i nowszych) libgui jest klasyfikowana jako VNDK-private , co odnosi się do grupy bibliotek dostępnych dla bibliotek VNDK, z których nie mogą korzystać procesy dostawców. Ponieważ implementacje HAL muszą znajdować się w partycji dostawcy, dostawcy nie mogą używać Surface w implementacjach HAL.

Tworzenie libgui dla procesów dostawców

Użycie libgui jest jedyną opcją użycia EGL/SurfaceFlinger w implementacjach HAL EVS Display. Najprostszym sposobem wdrożenia libgui jest bezpośrednie użycie frameworks/native/libs/gui przy użyciu dodatkowego celu kompilacji w skrypcie kompilacji. Ten cel jest dokładnie taki sam jak cel libgui , z wyjątkiem dodania dwóch pól:

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

Uwaga: Elementy docelowe dostawców są tworzone przy użyciu makra NO_INPUT , które usuwa jedno 32-bitowe słowo z danych przesyłki. Ponieważ SurfaceFlinger oczekuje, że to pole zostało usunięte, SurfaceFlinger nie może przeanalizować paczki. Jest to obserwowane jako awaria fcntl :

W Parcel  : Attempt to read object from Parcel 0x78d9cffad8 at offset 428 that is not in the object list
E Parcel  : fcntl(F_DUPFD_CLOEXEC) failed in Parcel::read, i is 0, fds[i] is 0, fd_count is 20, error: Unknown error 2147483647
W Parcel  : Attempt to read object from Parcel 0x78d9cffad8 at offset 544 that is not in the object list

Aby rozwiązać ten stan:

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

Przykładowe instrukcje budowania znajdują się poniżej. Spodziewaj się otrzymania $(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

Używanie spoiwa w implementacji EVS HAL

W systemie Android 8 (i nowszych) węzeł urządzenia /dev/binder stał się dostępny wyłącznie dla procesów frameworka i dlatego stał się niedostępny dla procesów dostawców. Zamiast tego procesy dostawców powinny używać /dev/hwbinder i muszą konwertować wszystkie interfejsy AIDL na HIDL. Jeśli chcesz nadal używać interfejsów AIDL między procesami dostawców, użyj domeny bindera /dev/vndbinder .

Domena IPC Opis
/dev/binder IPC pomiędzy procesami frameworku/aplikacji z interfejsami AIDL
/dev/hwbinder IPC pomiędzy procesami frameworku/dostawcy z interfejsami HIDL
IPC pomiędzy procesami dostawców za pomocą interfejsów HIDL
/dev/vndbinder IPC pomiędzy procesami dostawcy/dostawcy za pomocą interfejsów AIDL

Podczas gdy SurfaceFlinger definiuje interfejsy AIDL, procesy dostawców mogą używać interfejsów HIDL wyłącznie do komunikacji z procesami struktury. Konwersja istniejących interfejsów AIDL na HIDL wymaga nietrywialnej ilości pracy. Na szczęście Android udostępnia metodę wyboru sterownika bindera dla libbinder , z którym powiązane są procesy biblioteki przestrzeni użytkownika.

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

Uwaga: Procesy dostawców powinny wywołać tę opcję przed wywołaniem Process lub IPCThreadState lub przed wykonaniem jakichkolwiek wywołań segregatora.

Zasady SELinuksa

Jeśli implementacja urządzenia jest pełna, SELinux uniemożliwia procesom dostawcy używanie /dev/binder . Na przykład przykładowa implementacja EVS HAL jest przypisana do domeny hal_evs_driver i wymaga uprawnień r/w do domeny 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

Dodanie tych uprawnień powoduje jednak niepowodzenie kompilacji, ponieważ narusza następujące reguły Neverallow zdefiniowane w system/sepolicy/domain.te dla urządzenia z pełną częstotliwością tonów wysokich.

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 to atrybut służący do wychwytywania błędów i wspierania rozwoju. Można go również wykorzystać do rozwiązania opisanego powyżej naruszenia zasad Androida 10.

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)

Budowanie implementacji referencyjnej EVS HAL jako procesu dostawcy

Jako odniesienie możesz zastosować następujące zmiany w packages/services/Car/evs/Android.mk . Pamiętaj, aby potwierdzić, że wszystkie opisane zmiany działają w Twojej implementacji.

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;