Android zawiera warstwę abstrakcji sprzętu (HAL) HIDL dla motoryzacji, 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 stos systemu widoku zewnętrznego (EVS) i jest zwykle używany do obsługi kamery cofania i wyświetlaczy widoku przestrzennego w pojazdach z systemami informacyjno-rozrywkowymi (IVI) opartymi na Androidzie. EVS umożliwia też wdrażanie zaawansowanych funkcji w aplikacjach użytkowników.
Android zawiera też interfejs sterownika przechwytywania i wyświetlania specyficzny dla EVS (w /hardware/interfaces/automotive/evs/1.0
). Chociaż można utworzyć aplikację kamery cofania na podstawie istniejących usług aparatu i wyświetlania Androida, taka aplikacja prawdopodobnie uruchamiałaby się zbyt późno w procesie uruchamiania Androida. Użycie dedykowanej warstwy HAL umożliwia uproszczenie interfejsu i wyraźne określenie, co producent OEM musi wdrożyć, aby obsługiwać stos EVS.
Elementy systemu
EVS obejmuje te komponenty systemu:

Aplikacja EVS
Przykładowa aplikacja EVS w C++ (/packages/services/Car/evs/app
) służy jako implementacja referencyjna. Ta aplikacja odpowiada za wysyłanie do Menedżera EVS żądań klatek wideo i wysyłanie do niego gotowych klatek do wyświetlenia.
Oczekuje się, że zostanie uruchomiony przez init, gdy tylko EVS i Car Service będą dostępne, w ciągu 2 sekund od włączenia zasilania. Producenci OEM mogą modyfikować lub zastępować aplikację EVS w dowolny sposób.
Menedżer EVS
Menedżer EVS (/packages/services/Car/evs/manager
) udostępnia elementy składowe potrzebne aplikacji EVS do wdrożenia dowolnego rozwiązania, od prostego wyświetlacza kamery cofania po renderowanie z wielu kamer w 6 stopniach swobody. Jego interfejs jest prezentowany za pomocą HIDL i jest przystosowany do obsługi wielu klientów jednocześnie.
Inne aplikacje i usługi (w szczególności usługa samochodowa) mogą wysyłać zapytania o stan menedżera EVS, aby dowiedzieć się, kiedy system EVS jest aktywny.
Interfejs HIDL usługi EVS
System EVS, zarówno kamera, jak i elementy wyświetlacza, jest zdefiniowany w android.hardware.automotive.evs
pakiecie. Przykładowa implementacja
korzystająca z interfejsu (generuje syntetyczne obrazy testowe i sprawdza, czy
obrazy przechodzą test) jest dostępna w /hardware/interfaces/automotive/evs/1.0/default
.
Producent OEM jest odpowiedzialny za implementację interfejsu 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 pomocą buforów pamięci współdzielonej rozpoznawanych przez Gralloc. Po stronie wyświetlacza implementacja odpowiada za udostępnianie bufora pamięci współdzielonej, który może być wypełniany przez aplikację (zwykle za pomocą renderowania EGL), oraz za wyświetlanie gotowych klatek przed innymi elementami, które mogą pojawiać się na wyświetlaczu fizycznym. Implementacje interfejsu EVS dostawców mogą być przechowywane w folderach /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 mogą obsługiwać funkcje wymagane przez EVS za pomocą istniejących sterowników sprzętu kamery lub wyświetlacza. Ponowne używanie sterowników może być korzystne, zwłaszcza w przypadku sterowników wyświetlania, w których przypadku prezentacja obrazu może wymagać koordynacji z innymi aktywnymi wątkami. Android 8.0 zawiera przykładowy sterownik oparty na v4l2 (w packages/services/Car/evs/sampleDriver
), który zależy od jądra w zakresie obsługi v4l2 i od SurfaceFlingera w zakresie prezentowania obrazu wyjściowego.
Opis interfejsu sprzętowego EVS
W tej sekcji opisujemy HAL. Dostawcy powinni udostępniać implementacje tego interfejsu API dostosowane do ich sprzętu.
IEvsEnumerator
Ten obiekt odpowiada za wyliczanie dostępnego sprzętu EVS w systemie (co najmniej 1 kamera i 1 wyświetlacz).
getCameraList() generates (vec<CameraDesc> cameras);
Zwraca wektor zawierający opisy wszystkich kamer w systemie. Zakłada się, że zestaw kamer jest stały i znany w momencie uruchomienia. Więcej informacji o opisach kamer znajdziesz w CameraDesc
.
openCamera(string camera_id) generates (IEvsCamera camera);
Pobiera obiekt interfejsu używany do interakcji z określonym aparatem zidentyfikowanym przez unikalny ciąg znaków camera_id. W przypadku niepowodzenia zwraca wartość NULL.
Próby ponownego otwarcia aparatu, który jest już otwarty, nie mogą się nie powieść. Aby uniknąć sytuacji wyścigu związanych z uruchamianiem i zamykaniem aplikacji, ponowne otwieranie kamery powinno zamykać poprzednią instancję, aby można było zrealizować nowe żądanie. Instancja kamery, która została w ten sposób wywłaszczona, musi zostać umieszczona w stanie nieaktywnym, oczekując na ostateczne zniszczenie i odpowiadając na każde żądanie zmiany stanu kamery kodem zwrotnym OWNERSHIP_LOST
.
closeCamera(IEvsCamera camera);
Zwalnia interfejs IEvsCamera (jest przeciwieństwem wywołania openCamera()
). Strumień wideo z kamery musi zostać zatrzymany przez wywołanie funkcji stopVideoStream()
przed wywołaniem funkcji closeCamera
.
openDisplay() generates (IEvsDisplay display);
Pobiera obiekt interfejsu używany do wyłącznej interakcji z wyświetlaczem EVS systemu. W danym momencie tylko jeden klient może mieć funkcjonalną instancję interfejsu IEvsDisplay. Podobnie jak w przypadku agresywnego otwierania opisanego w openCamera
, w dowolnym momencie można utworzyć nowy obiekt IEvsDisplay, który wyłącza wszystkie poprzednie instancje. Unieważnione instancje nadal istnieją i odpowiadają na wywołania funkcji od ich właścicieli, ale gdy są nieaktywne, nie mogą wykonywać żadnych operacji zmieniających stan. W końcu aplikacja kliencka powinna zauważyć kody błędów OWNERSHIP_LOST
i zamknąć oraz zwolnić nieaktywny interfejs.
closeDisplay(IEvsDisplay display);
Zwalnia interfejs IEvsDisplay (jest przeciwieństwem wywołania openDisplay()
). W przypadku wywołań getTargetBuffer()
otrzymane bufory oczekujące muszą zostać zwrócone do wyświetlacza przed jego zamknięciem.
getDisplayState() generates (DisplayState state);
Pobiera bieżący stan wyświetlacza. Implementacja HAL powinna zgłaszać aktualny stan, który może się różnić od ostatnio zgłoszonego stanu.
Logika odpowiedzialna za zmianę stanów wyświetlania powinna znajdować się powyżej warstwy urządzenia, co sprawia, że spontaniczna zmiana stanów wyświetlania przez implementację HAL jest niepożądana. Jeśli wyświetlacz nie jest obecnie używany przez żadnego klienta (w wyniku wywołania funkcji openDisplay), ta funkcja zwraca wartość NOT_OPEN
. W przeciwnym razie zwraca bieżący stan wyświetlacza EVS (patrz IEvsDisplay API).
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 w jądrze lub nazwa urządzenia, np. rearview. Wartość tego ciągu jest wybierana przez implementację HAL i używana w sposób nieprzejrzysty przez warstwę powyżej.vendor_flags
. Metoda przekazywania specjalnych informacji o kamerze w sposób nieprzejrzysty ze sterownika do niestandardowej aplikacji EVS. Informacje są przekazywane bez interpretacji ze sterownika do aplikacji EVS, która może je zignorować.
IEvsCamera
Ten obiekt reprezentuje pojedynczy aparat i jest głównym interfejsem do robienia zdjęć.
getCameraInfo() generates (CameraDesc info);
Zwraca CameraDesc
tej kamery.
setMaxFramesInFlight(int32 bufferCount) generates (EvsResult result);
Określa głębokość łańcucha buforów, które kamera ma obsługiwać. Klient interfejsu IEvsCamera może przechowywać jednocześnie maksymalnie tę liczbę klatek. Jeśli do odbiorcy dotarło tyle klatek, które nie zostały zwrócone przez doneWithFrame
, strumień pomija klatki, dopóki nie zostanie zwrócony bufor do ponownego użycia. To wywołanie może nastąpić 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 tego punktu wejścia, interfejs IEvsCamera domyślnie obsługuje co najmniej 1 klatkę, ale może obsługiwać ich więcej.
Jeśli nie można utworzyć bufora o żądanej liczbie elementów, funkcja zwraca wartość BUFFER_NOT_AVAILABLE
lub inny odpowiedni kod błędu. W takim przypadku system nadal działa z wcześniej ustawioną wartością.
startVideoStream(IEvsCameraStream receiver) generates (EvsResult result);
Wysyła żądania dostarczenia klatek z kamery EVS z tej kamery. Interfejs IEvsCameraStream
zaczyna odbierać okresowe wywołania z nowymi klatkami obrazu, dopóki nie zostanie wywołana funkcja
stopVideoStream()
. Klatki muszą zacząć być dostarczane w ciągu 500 ms od wywołania funkcji startVideoStream
, a po rozpoczęciu muszą być generowane z częstotliwością co najmniej 10 klatek na sekundę. Czas potrzebny na rozpoczęcie strumienia wideo jest wliczany do czasu uruchomienia kamery cofania. Jeśli strumień nie został uruchomiony, należy zwrócić kod błędu. W przeciwnym razie zwracana jest wartość OK.
oneway doneWithFrame(BufferDesc buffer);
Zwraca ramkę dostarczoną do IEvsCameraStream. Po zakończeniu korzystania z klatki dostarczonej do interfejsu IEvsCameraStream należy zwrócić ją do interfejsu IEvsCamera w celu ponownego użycia. Dostępna jest niewielka, skończona liczba buforów (może być ich nawet tylko jeden). Jeśli pula buforów się wyczerpie, nie będą dostarczane kolejne klatki, dopóki bufor nie zostanie zwrócony. Może to spowodować pominięcie klatek (bufor z uchwytem o wartości null oznacza koniec strumienia i nie musi być zwracany przez tę funkcję). Zwraca OK w przypadku powodzenia lub odpowiedni kod błędu, który może zawierać INVALID_ARG
lub BUFFER_NOT_AVAILABLE
.
stopVideoStream();
Zatrzymuje dostarczanie klatek z kamery EVS. Ponieważ dostawa jest asynchroniczna, klatki mogą nadal przychodzić przez pewien czas po powrocie z tego wywołania. Każda klatka musi być zwracana do momentu, gdy do interfejsu IEvsCameraStream zostanie wysłany sygnał zamknięcia strumienia. Wywołanie funkcji stopVideoStream
w strumieniu, który został już zatrzymany lub nigdy nie został uruchomiony, jest dozwolone. W takich przypadkach jest ono ignorowane.
getExtendedInfo(int32 opaqueIdentifier) generates (int32 value);
Wysyła do implementacji HAL żądania informacji dotyczących sterownika. Wartości
dozwolone dla parametru opaqueIdentifier
są specyficzne dla sterownika, ale brak przekazanej wartości
może spowodować awarię sterownika. Sterownik powinien zwracać wartość 0 w przypadku nierozpoznanych opaqueIdentifier
.
setExtendedInfo(int32 opaqueIdentifier, int32 opaqueValue) generates (EvsResult result);
Wysyła wartość specyficzną dla sterownika do implementacji HAL. To rozszerzenie jest udostępniane tylko w celu ułatwienia tworzenia rozszerzeń specyficznych dla pojazdu i żadna implementacja HAL nie powinna wymagać tego wywołania do działania w stanie domyślnym. Jeśli sterownik rozpoznaje i akceptuje wartości, powinien zwrócić OK. W przeciwnym razie powinien zwrócić 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 przekazany przez interfejs API. Sterownik 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 klient mógł zrekonstruować obiekt ANativeWindowBuffer
, co może być wymagane do używania obrazu z EGL z rozszerzeniem eglCreateImageKHR()
.
width
Szerokość wyświetlanego obrazu w pikselach.height
. Wysokość wyświetlanego obrazu w pikselach.stride
. Liczba pikseli, które każdy wiersz zajmuje w pamięci, z uwzględnieniem dopełnienia na potrzeby wyrównania wierszy. Wyrażona w pikselach, aby była zgodna z konwencją przyjętą przez gralloc w opisach buforów.pixelSize
. Liczba bajtów zajmowanych przez każdy piksel, co umożliwia obliczenie rozmiaru w bajtach niezbędnego do przejścia między wierszami obrazu (stride
w bajtach =stride
w pikselach *pixelSize
).format
. Format piksela używany przez obraz. Podany format musi być zgodny z implementacją OpenGL na platformie. Aby przejść testy zgodności,HAL_PIXEL_FORMAT_YCRCB_420_SP
powinno być preferowane w przypadku korzystania z aparatu, aRGBA
lubBGRA
– w przypadku wyświetlacza.usage
. Flagi użycia ustawione przez implementację HAL. Klienci HAL powinni przekazywać te flagi bez zmian (szczegółowe informacje znajdziesz wGralloc.h
powiązanych flagach).bufferId
– niepowtarzalna wartość określona przez implementację HAL, która umożliwia rozpoznanie bufora po przejściu przez interfejsy API HAL. Wartość przechowywana w tym polu może być dowolnie wybrana przez implementację HAL.memHandle
. Uchwyt podstawowego bufora pamięci, który zawiera dane obrazu. Implementacja HAL może przechowywać tutaj uchwyt bufora Gralloc.
IEvsCameraStream
Klient implementuje ten interfejs, aby odbierać asynchroniczne dostawy klatek wideo.
deliverFrame(BufferDesc buffer);
Otrzymuje wywołania z HAL za każdym razem, gdy klatka wideo jest gotowa do sprawdzenia.
Bufory otrzymane przez tę metodę muszą być zwracane przez wywołania funkcji IEvsCamera::doneWithFrame()
. Gdy strumień wideo zostanie zatrzymany przez wywołanie funkcji IEvsCamera::stopVideoStream()
, to wywołanie zwrotne może być kontynuowane, gdy potok jest opróżniany. Każda klatka musi być nadal zwracana. Gdy ostatnia klatka w strumieniu zostanie dostarczona, zwracana jest wartość NULL bufferHandle
, co oznacza koniec strumienia i brak dalszych dostaw klatek. Sama wartość NULL
bufferHandle
nie musi być odsyłana z wartością
doneWithFrame()
, ale wszystkie inne uchwyty muszą zostać zwrócone.
Wprawdzie formaty bufora o charakterze zastrzeżonym są technicznie możliwe, ale testy zgodności wymagają, aby bufor był w jednym z 4 obsługiwanych formatów: NV21 (YCrCb 4:2:0 Semi-Planar), YV12 (YCrCb 4:2:0 Planar), YUYV (YCrCb 4:2:2 Interleaved), RGBA (32-bitowy R:G:B:x), BGRA (32-bitowy B:G:R:x). Wybrany format musi być prawidłowym źródłem tekstury GL w implementacji GLES na platformie.
Aplikacja nie powinna opierać się na żadnej korespondencji między polem bufferId
a polem memHandle
w strukturze BufferDesc
. Wartości bufferId
są w zasadzie prywatne dla implementacji sterownika HAL i może on ich używać (i używać ponownie) w dowolny sposób.
IEvsDisplay
Ten obiekt reprezentuje wyświetlacz Evs, kontroluje jego stan i obsługuje rzeczywiste wyświetlanie obrazów.
getDisplayInfo() generates (DisplayDesc info);
Zwraca podstawowe informacje o wyświetlaczu EVS dostarczone przez system (patrz DisplayDesc).
setDisplayState(DisplayState state) generates (EvsResult result);
Ustawia stan wyświetlania. Klienci mogą ustawić stan wyświetlania, aby wyrazić żądany stan, a implementacja HAL musi prawidłowo akceptować żądanie dowolnego stanu, gdy jest w dowolnym innym stanie, chociaż odpowiedź może polegać na zignorowaniu żądania.
Po zainicjowaniu wyświetlacz jest ustawiony na stan NOT_VISIBLE
, po czym klient powinien zażądać stanu VISIBLE_ON_NEXT_FRAME
i zacząć przesyłać wideo. Gdy wyświetlanie nie jest już potrzebne, klient powinien po wyświetleniu ostatniej klatki filmu poprosić o stan NOT_VISIBLE
.
Możesz w każdej chwili poprosić o dowolny stan. Jeśli wyświetlacz jest już widoczny, powinien pozostać widoczny, jeśli ustawienie ma wartość VISIBLE_ON_NEXT_FRAME
. Zawsze zwraca OK, chyba że żądany stan jest nierozpoznaną wartością typu wyliczeniowego. W takim przypadku zwracany jest kod INVALID_ARG
.
getDisplayState() generates (DisplayState state);
Pobiera stan wyświetlacza. Implementacja HAL powinna raportować rzeczywisty bieżący stan, który może się różnić od ostatnio zgłoszonego stanu. Logika odpowiedzialna za zmianę stanów wyświetlania powinna znajdować się powyżej warstwy urządzenia, co sprawia, że niepożądane jest, aby implementacja HAL spontanicznie zmieniała stany wyświetlania.
getTargetBuffer() generates (handle bufferHandle);
Zwraca uchwyt do bufora ramki powiązanego z wyświetlaczem. Ten bufor może być zablokowany i zapisywany przez oprogramowanie lub GL. Ten bufor musi zostać zwrócony
w wywołaniu funkcji returnTargetBufferForDisplay()
, nawet jeśli wyświetlacz
nie jest już widoczny.
Własne formaty buforów są technicznie możliwe, ale testy zgodności wymagają, aby bufor był w jednym z 4 obsługiwanych formatów: NV21 (YCrCb 4:2:0 Semi-Planar), YV12 (YCrCb 4:2:0 Planar), YUYV (YCrCb 4:2:2 Interleaved), RGBA (32-bitowy R:G:B:x), BGRA (32-bitowy B:G:R:x). Wybrany format musi być prawidłowym celem renderowania GL w implementacji GLES na platformie.
W przypadku błędu zwracany jest bufor z uchwytem o wartości null, ale takiego bufora nie trzeba przekazywać z powrotem do funkcji returnTargetBufferForDisplay
.
returnTargetBufferForDisplay(handle bufferHandle) generates (EvsResult result);
Informuje wyświetlacz, że bufor jest gotowy do wyświetlenia. Tylko bufory pobrane przez wywołanie funkcji getTargetBuffer()
są prawidłowe do użycia w tym wywołaniu, a zawartość BufferDesc
nie może być modyfikowana przez aplikację klienta. Po tym wywołaniu bufor nie jest już prawidłowy do użycia przez klienta. W przypadku powodzenia zwraca OK, a w przypadku błędu odpowiedni kod błędu, np. 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 i jest wymagany w przypadku implementacji EVS. Warstwa HAL odpowiada za wypełnienie tej struktury, aby opisać wyświetlacz EVS. Może to być wyświetlacz fizyczny lub wirtualny, który jest nakładany na inne urządzenie do prezentacji lub z nim mieszany.
display_id
Ciąg znaków, który jednoznacznie identyfikuje wyświetlacz. Może to być nazwa urządzenia w jądrze lub nazwa urządzenia, np. lusterko wsteczne. Wartość tego ciągu znaków jest wybierana przez implementację HAL i używana w sposób nieprzejrzysty przez warstwę powyżej.vendor_flags
. Metoda przekazywania specjalnych informacji o kamerze w sposób nieprzejrzysty ze sterownika do niestandardowej aplikacji EVS. Informacje są przekazywane bez interpretacji ze sterownika do aplikacji EVS, która może je 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 }
Określa stan wyświetlacza EVS, który może być wyłączony (niewidoczny dla kierowcy) lub włączony (wyświetlający obraz kierowcy).
Obejmuje stan przejściowy, w którym wyświetlacz nie jest jeszcze widoczny, ale jest przygotowany do wyświetlenia następnej klatki obrazu po wywołaniu funkcji returnTargetBufferForDisplay()
.
Menedżer EVS
Menedżer EVS udostępnia publiczny interfejs do systemu EVS, który służy do zbierania i prezentowania widoków z kamer zewnętrznych. Jeśli sterowniki sprzętowe zezwalają tylko na 1 aktywny interfejs na zasób (kamerę lub wyświetlacz), menedżer EVS ułatwia współdzielenie dostępu do kamer. Jedna główna aplikacja EVS jest pierwszym klientem Menedżera EVS i jedynym klientem, który może zapisywać dane wyświetlania (dodatkowym klientom można przyznać dostęp tylko do odczytu obrazów z kamery).
Menedżer EVS implementuje ten sam interfejs API co sterowniki HAL i zapewnia rozszerzoną usługę, obsługując wielu klientów jednocześnie (więcej niż 1 klient może otworzyć kamerę za pomocą menedżera EVS i otrzymywać strumień wideo).

Aplikacje nie widzą różnic w działaniu w przypadku korzystania z implementacji EVS Hardware HAL lub interfejsu EVS Manager API, z wyjątkiem tego, że interfejs EVS Manager API umożliwia jednoczesny dostęp do strumienia z kamery. Menedżer EVS jest jedynym dozwolonym klientem warstwy HAL sprzętu EVS i działa jako serwer proxy dla warstwy HAL sprzętu EVS.
W sekcjach poniżej opisujemy tylko te wywołania, które mają inne (rozszerzone) działanie w implementacji Menedżera EVS. Pozostałe wywołania są identyczne z opisami w EVS HAL.
IEvsEnumerator
openCamera(string camera_id) generates (IEvsCamera camera);
Pobiera obiekt interfejsu używany do interakcji z określonym aparatem zidentyfikowanym przez unikalny ciąg znaków camera_id. W przypadku niepowodzenia zwraca wartość NULL.
Na warstwie Menedżera EVS, o ile dostępne są wystarczające zasoby systemowe, aparat, który jest już otwarty, może zostać ponownie otwarty przez inny proces, co umożliwia przekazywanie strumienia wideo do wielu aplikacji konsumenckich. Ciągi znaków camera_id
w warstwie Menedżera EVS są takie same jak te zgłaszane do warstwy sprzętowej EVS.
IEvsCamera
Dostarczona przez menedżera EVS 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 rozpoczynać i zatrzymywać strumienie wideo z tej samej kamery. Podstawowa kamera uruchamia się, gdy pierwszy klient rozpocznie korzystanie z niej.
doneWithFrame(uint32 frameId, handle bufferHandle) generates (EvsResult result);
Zwraca ramkę. Każdy klient musi zwrócić okulary po zakończeniu korzystania z nich, ale może je zatrzymać na dowolnie długo. Gdy liczba klatek przechowywanych przez klienta osiągnie skonfigurowany limit, nie będzie on otrzymywać kolejnych klatek, dopóki nie zwróci jednej z nich. Pomijanie klatek nie ma wpływu na innych klientów, którzy nadal otrzymują wszystkie klatki zgodnie z oczekiwaniami.
stopVideoStream();
Zatrzymuje strumień wideo. Każdy klient może w dowolnym momencie zatrzymać strumień wideo bez wpływu na innych klientów. Gdy ostatni klient danej kamery zatrzyma strumień, strumień kamery na poziomie sprzętowym zostanie zatrzymany.
setExtendedInfo(int32 opaqueIdentifier, int32 opaqueValue) generates (EvsResult result);
Wysyła wartość specyficzną dla sterownika, co może umożliwić jednemu klientowi wpływanie na innego klienta. Menedżer EVS nie rozumie implikacji słów kontrolnych zdefiniowanych przez dostawcę, więc nie są one wirtualizowane, a wszelkie efekty uboczne dotyczą wszystkich klientów danej kamery. Jeśli na przykład dostawca użyje tego wywołania do zmiany liczby klatek na sekundę, wszyscy klienci kamery warstwy sprzętowej, której dotyczy problem, będą otrzymywać klatki z nową szybkością.
IEvsDisplay
Nawet na poziomie menedżera EVS może być tylko jeden właściciel wyświetlacza. Menedżer nie dodaje żadnych funkcji i po prostu przekazuje interfejs IEvsDisplay bezpośrednio do bazowej implementacji HAL.
Aplikacja EVS
Android zawiera natywną implementację referencyjną w C++ aplikacji EVS, która komunikuje się z menedżerem EVS i HAL pojazdu, aby zapewnić podstawowe funkcje kamery cofania. Aplikacja powinna uruchamiać się bardzo wcześnie w procesie rozruchu systemu, a odpowiedni obraz wideo powinien być wyświetlany w zależności od dostępnych kamer i stanu samochodu (bieg i kierunkowskaz). Producenci OEM mogą modyfikować lub zastępować aplikację EVS własną logiką i prezentacją dostosowaną do konkretnego pojazdu.


Ponieważ dane obrazu są przekazywane do aplikacji w standardowym buforze graficznym, aplikacja jest odpowiedzialna za przeniesienie obrazu z bufora źródłowego do bufora wyjściowego. Wprawdzie wiąże się to z kosztem kopiowania danych, ale daje też aplikacji możliwość renderowania obrazu w buforze wyświetlania w dowolny sposób.
Aplikacja może na przykład przenieść dane pikseli, potencjalnie z operacją skalowania lub obracania wbudowaną w kodzie. Aplikacja może też użyć obrazu źródłowego jako tekstury OpenGL i wyrenderować w buforze wyjściowym złożoną scenę, w tym elementy wirtualne, takie jak ikony, wytyczne i animacje. Bardziej zaawansowana aplikacja może też wybierać kilka równoczesnych kamer wejściowych i łączyć je w jedną ramkę wyjściową (np. do użycia w widoku z góry wirtualnego otoczenia pojazdu).
Używanie EGL/SurfaceFlingera w warstwie HAL wyświetlacza EVS
Z tej sekcji dowiesz się, jak używać EGL do renderowania implementacji EVS Display HAL w Androidzie 10.
Implementacja referencyjna EVS HAL używa EGL do renderowania podglądu z kamery na ekranie i libgui
do tworzenia docelowej powierzchni renderowania EGL. W Androidzie 8 (i nowszych) libgui
jest klasyfikowany jako VNDK-private, co odnosi się do grupy bibliotek dostępnych dla bibliotek VNDK, których procesy dostawcy nie mogą używać.
Implementacje HAL muszą znajdować się w partycji dostawcy, więc dostawcy nie mogą używać interfejsu Surface w implementacjach HAL.
Kompilowanie libgui na potrzeby procesów dostawcy
Użycie libgui
jest jedyną opcją korzystania z EGL/SurfaceFlinger w implementacjach EVS Display HAL. Najprostszym sposobem wdrożenia libgui
jest użycie frameworks/native/libs/gui bezpośrednio przez dodanie dodatkowego celu kompilacji w skrypcie kompilacji. Ta lokalizacja docelowa jest dokładnie taka sama jak lokalizacja docelowa libgui
, z wyjątkiem 2 dodatkowych 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: miejsca docelowe dostawców są tworzone za pomocą makra NO_INPUT
, które usuwa z danych pakietu 1 32-bitowe słowo. Usługa SurfaceFlinger oczekuje tego pola, które zostało usunięte, więc nie może przeanalizować pakietu. W takim przypadku wystąpi 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 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);
Poniżej znajdziesz przykładowe instrukcje. 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 mechanizmu Binder w implementacji EVS HAL
W Androidzie 8 (i nowszych wersjach) /dev/binder
węzeł urządzenia stał się wyłączny dla procesów platformy, a tym samym niedostępny dla procesów dostawcy. Zamiast tego procesy dostawcy 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 dostawcy, użyj domeny bindera /dev/vndbinder
.
Domena IPC | Opis |
---|---|
/dev/binder |
Komunikacja międzyprocesowa między procesami platformy i aplikacji za pomocą interfejsów AIDL |
/dev/hwbinder |
IPC między procesami platformy/dostawcy z interfejsami HIDL IPC między procesami dostawcy z interfejsami HIDL |
/dev/vndbinder |
Komunikacja międzyprocesowa między procesami dostawcy za pomocą interfejsów AIDL |
SurfaceFlinger definiuje interfejsy AIDL, ale procesy dostawcy mogą używać tylko interfejsów HIDL do komunikacji z procesami platformy. Przekształcenie istniejących interfejsów AIDL w HIDL wymaga sporego nakładu pracy. Na szczęście Android udostępnia metodę, za pomocą której można wybrać sterownik bindera dla libbinder
, z którym połączone 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 dostawcy powinny wywoływać tę funkcję przed wywołaniem funkcji Process
lub IPCThreadState
albo przed wykonaniem jakichkolwiek wywołań interfejsu Binder.
Zasady SELinux
Jeśli implementacja urządzenia jest w pełni zgodna z technologią Treble, SELinux uniemożliwia procesom dostawcy korzystanie z /dev/binder
. Na przykład przykładowa implementacja EVS HAL jest przypisana do domeny hal_evs_driver
i wymaga uprawnień do odczytu i zapisu w domenie 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 błąd kompilacji, ponieważ narusza następujące reguły neverallow zdefiniowane w system/sepolicy/domain.te
dla urządzenia z pełnym treble.
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, który pomaga wykrywać błędy i ułatwia tworzenie aplikacji. Można go też używać do rozwiązania opisanego powyżej problemu z Androidem 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)
Tworzenie referencyjnej implementacji HAL EVS jako proces dostawcy
Możesz wprowadzić te zmiany w packages/services/Car/evs/Android.mk
. Upewnij się, ż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;