Android zawiera warstwę abstrakcji sprzętowej HIDL (HAL) do zastosowań motoryzacyjnych, która umożliwia przechwytywanie i wyświetlanie obrazów na bardzo wczesnym etapie procesu uruchamiania Androida i działa przez cały czas działania systemu. HAL obejmuje stos zewnętrznego systemu widoku (EVS) i zwykle służy do obsługi tylnego aparatu i wyświetlaczy przestrzennego w pojazdach z systemami multimedialnymi pokładowymi opartymi na Androidzie (IVI). EVS umożliwia też wdrażanie funkcji zaawansowanych w aplikacjach użytkowników.
Android zawiera też interfejs sterownika do rejestrowania i wyświetlania danych EVS (w /hardware/interfaces/automotive/evs/1.0
). Chociaż można tworzyć aplikacje kamery cofania na podstawie istniejących usług kamery i wyświetlania w Androidzie, takie aplikacje prawdopodobnie będą działać zbyt późno w procesie uruchamiania Androida. Zastosowanie dedykowanego interfejsu HAL umożliwia uproszczenie interfejsu i określa, co musi wdrożyć OEM, aby obsługiwać pakiet EVS.
Elementy systemu
EVS obejmuje te komponenty systemu:
Rysunek 1. Omówienie komponentów systemu EVS
Aplikacja EVS
Przykładowa aplikacja EVS w C++ (/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.
Ma być uruchamiany przez init, gdy tylko EVS i Car Service są dostępne, w ciągu 2 sekund od włączenia. OEM może w razie potrzeby modyfikować lub zastępować
aplikację EVS.
Menedżer EVS
Menedżer EVS (/packages/services/Car/evs/manager
) udostępnia elementy potrzebne aplikacji EVS do implementowania różnych funkcji, od prostego wyświetlacza kamery cofania po renderowanie z użyciem wielu kamer w technologii 6DOF. Interfejs jest prezentowany za pomocą HIDL i zaprojektowany tak, aby obsługiwał wielu klientów jednocześnie.
Inne aplikacje i usługi (zwłaszcza 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 wyświetlacze, jest zdefiniowany w pakiecie android.hardware.automotive.evs
. Przykładowa implementacja, która testuje interfejs (generuje syntetyczne testowe obrazy i sprawdza, czy obrazy wracają do źródła), znajduje się w pliku /hardware/interfaces/automotive/evs/1.0/default
.
Producent OEM jest odpowiedzialny za implementację interfejsu API określonego przez pliki .hal w /hardware/interfaces/automotive/evs
. Takie implementacje odpowiadają za konfigurowanie i zbieranie danych z fizycznych kamer oraz przekazywanie ich przez wspólne bufory pamięci rozpoznawalne przez Gralloc. Implementacja po stronie wyświetlacza odpowiada za udostępnianie wspólnego bufora pamięci, który może być wypełniany przez aplikację (zwykle przez renderowanie EGL) i prezentowanie gotowych klatek z pominięciem innych elementów, które mogłyby się wyświetlać na fizycznym wyświetlaczu. Implementacje interfejsu EVS przez dostawców mogą być przechowywane w folderze /vendor/… /device/…
lub hardware/…
(np.
/hardware/[vendor]/[platform]/evs
).
Sterowniki jądra
Urządzenie obsługujące pakiet EVS wymaga sterowników jądra. Zamiast tworzyć nowe sterowniki, OEM może obsługiwać funkcje wymagane przez EVS za pomocą dotychczasowych sterowników sprzętu kamery lub wyświetlacza. Ponowne używanie sterowników może być korzystne, zwłaszcza w przypadku sterowników wyświetlacza, w których przypadku wyświetlanie obrazu może wymagać koordynacji z innymi aktywnymi wątkami. Android 8.0 zawiera przykładowy sterownik (w packages/services/Car/evs/sampleDriver
) oparty na v4l2, który wymaga jądra do obsługi v4l2 i SurfaceFlingera do wyświetlania obrazu wyjściowego.
Opis interfejsu sprzętowego EVS
Sekcja opisuje HAL. Producenci powinni udostępniać implementacje tego interfejsu API dostosowane do ich sprzętu.
Narzędzie IEvsEnumerator
Ten obiekt odpowiada za wyliczanie dostępnego sprzętu EVS w systemie (co najmniej 1 kamera i 1 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 znany w momencie uruchamiania. Szczegółowe informacje o opisach kamer znajdziesz w artykule CameraDesc
.
openCamera(string camera_id) generates (IEvsCamera camera);
Pobiera obiekt interfejsu służący do interakcji z konkretną kamerą, którą identyfikuje unikalny ciąg znaków camera_id. W przypadku niepowodzenia zwraca wartość NULL.
Nieudane próby ponownego otwarcia aparatu, który jest już otwarty, nie mogą się zdarzyć. Aby uniknąć warunków współzawodnictwa związanych z uruchamianiem i zamykaniem aplikacji, ponowne otwarcie aparatu powinno spowodować zamknięcie poprzedniej instancji, aby można było spełnić nowe żądanie. W przypadku instancji kamery, która została w ten sposób zablokowana, należy ją przełączyć w stan nieaktywny, oczekując na ostateczne zniszczenie, i odpowiedzieć na dowolne żądanie zmiany stanu kamery z kodem zwrotnym OWNERSHIP_LOST
.
closeCamera(IEvsCamera camera);
Zwalnia interfejs IEvsCamera (jest przeciwieństwem wywołania openCamera()
). Strumień wideo z kamery należy zatrzymać, wywołując funkcję stopVideoStream()
przed wywołaniem funkcji closeCamera
.
openDisplay() generates (IEvsDisplay display);
Pobiera obiekt interfejsu służący wyłącznie do interakcji z wyświetlaczem EVS systemu. W danym momencie tylko jeden klient może mieć aktywną instancję IEvsDisplay. Podobnie jak w przypadku agresywnego otwierania opisanego w openCamera
, nowy obiekt IEvsDisplay może zostać utworzony w dowolnym momencie i wyłączy wszystkie poprzednie wystąpienia. Nieważne instancje nadal istnieją i reagują na wywołania funkcji od swoich właścicieli, ale nie mogą wykonywać operacji mutacji, gdy są martwe. W przyszłości aplikacja kliencka powinna zauważyć zwracany kod błędu OWNERSHIP_LOST
oraz zamknąć i zwolnić nieaktywny interfejs.
closeDisplay(IEvsDisplay display);
Zwalnia interfejs IEvsDisplay (jest przeciwieństwem wywołania openDisplay()
). Niewykorzystane bufory otrzymane przez połączenia getTargetBuffer()
muszą zostać zwrócone do wyświetlacza przed zamknięciem wyświetlacza.
getDisplayState() generates (DisplayState state);
Pobiera bieżący stan wyświetlania. Implementacja HAL powinna podawać rzeczywisty bieżący stan, który może się różnić od ostatniego żądanego stanu.
Logika odpowiedzialna za zmianę stanu wyświetlania powinna istnieć nad warstwą urządzenia, co uniemożliwia samoistne zmienianie stanów wyświetlania przez wdrożenie HAL. Jeśli ekran nie jest obecnie wyświetlany przez żadnego klienta (za pomocą wywołania openDisplay), funkcja zwraca NOT_OPEN
. W przeciwnym razie zwraca aktualny stan wyświetlacza EVS (patrz interfejs IEvsDisplay API).
struct CameraDesc { string camera_id; int32 vendor_flags; // Opaque value }
camera_id
. Ciąg jednoznacznie identyfikujący daną kamerę. Może to być nazwa jądra urządzenia lub nazwa urządzenia, na przykład widok wsteczny. Wartość tego ciągu jest wybierana przez implementację HAL i używana przez warstwę powyżej w nieprzejrzysty sposób.vendor_flags
. Metoda przekazywania zaszyfrowanych informacji o specjalnej kamerze z sterownika do niestandardowej aplikacji EVS. Informacje są przekazywane bez interpretacji od sterownika do aplikacji EVS, która może je zignorować.
IEvsCamera
Ten obiekt reprezentuje jedną kamerę 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 IEvsCamera może przechowywać jednocześnie maksymalnie tyle klatek. Jeśli to
wiele klatek zostało dostarczonych do odbiornika bez ich zwrócenia przez
doneWithFrame
, strumień pomija klatki, dopóki nie zostanie zwrócony bufor do ponownego użycia. Wywołanie może nastąpić w dowolnym momencie, nawet gdy strumienie są już uruchomione. W takim przypadku bufory należy dodać lub usunąć z łańcucha. Jeśli nie zostanie wywołany ten punkt wejścia, IEvsCamera domyślnie obsługuje co najmniej 1 klatkę, z większą akceptacją.
Jeśli nie można dopasować żądanej wartości bufferCount, funkcja zwraca BUFFER_NOT_AVAILABLE
lub inny odpowiedni kod błędu. W takim przypadku system kontynuuje pracę z ustawioną wcześniej wartością.
startVideoStream(IEvsCameraStream receiver) generates (EvsResult result);
Wysyła żądanie dostarczenia ramek z kamery EVS z tego urządzenia. IEvsCameraStream zaczyna otrzymywać okresowe wywołania z nowymi ramkami obrazu, dopóki nie zostanie wywołana funkcja stopVideoStream()
. Ramki muszą zacząć być dostarczane w ciągu 500 ms od wywołania funkcji startVideoStream
i po rozpoczęciu muszą być generowane z minimalną szybkością 10 FPS. Czas potrzebny do rozpoczęcia przesyłania strumienia wideo jest uwzględniany w wymaganiach dotyczących czasu uruchamiania kamery cofania. Jeśli strumień nie został uruchomiony, musi zostać zwrócony kod błędu. W przeciwnym razie zwracany jest kod OK.
oneway doneWithFrame(BufferDesc buffer);
Zwraca klatkę dostarczoną przez IEvsCameraStream. Po zakończeniu przetwarzania ramki dostarczonej do interfejsu IEvsCameraStream musi ona zostać zwrócona do IEvsCamera, aby ją ponownie wykorzystać. Dostępnych jest niewielka, skończona liczba buforów (być może nawet 1 mały), a po wyczerpaniu zapasów kolejne klatki nie są przesyłane, dopóki bufor nie zostanie zwrócony, co może skutkować pominięciem klatek (bufor z uchwytem o wartości null wskazuje koniec strumienia i nie musi być zwracany za pomocą tej funkcji). W przypadku powodzenia zwraca wartość OK, a w przypadku błędu – odpowiedni kod błędu, który może zawierać INVALID_ARG
lub BUFFER_NOT_AVAILABLE
.
stopVideoStream();
Zatrzymuje przesyłanie klatek z kamery EVS. Ponieważ przesyłanie jest asynchroniczne, ramki mogą nadal docierać przez jakiś czas po zakończeniu wywołania. Każda klatka musi zostać zwrócona, dopóki IEvsCameraStream nie otrzyma sygnału o zamknięciu strumienia. Można wywołać funkcję stopVideoStream
w przypadku strumienia, który został już zatrzymany lub nigdy nie został uruchomiony. W takich przypadkach jest on ignorowany.
getExtendedInfo(int32 opaqueIdentifier) generates (int32 value);
Powoduje żądanie informacji dotyczących sterownika do implementacji HAL. Wartości dozwolone dla parametru opaqueIdentifier
zależą od sterownika, ale brak wartości może spowodować awarię sterownika. W przypadku nierozpoznanych wartości opaqueIdentifier
funkcja powinna zwracać 0.
setExtendedInfo(int32 opaqueIdentifier, int32 opaqueValue) generates (EvsResult result);
Wysyła wartość związaną z sterownikiem do implementacji HAL. To rozszerzenie służy wyłącznie do ułatwiania rozszerzeń związanych z pojazdami. Żadne wdrożenie HAL nie powinno wymagać tego wywoł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 odpowiedni 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. Za wypełnienie tej struktury w celu opisania bufora obrazu odpowiada HAL, a klient HAL powinien traktować tę strukturę jako tylko do odczytu. Pola zawierają wystarczającą ilość informacji, aby umożliwić klientowi odtworzenie obiektu ANativeWindowBuffer
, co może być wymagane do użycia obrazu z EGL za pomocą rozszerzenia eglCreateImageKHR()
.
width
. Szerokość prezentowanego obrazu w pikselach.height
. Wysokość prezentowanego obrazu w pikselach.stride
. Liczba pikseli, które zajmuje każdy wiersz w pamięci, uwzględniająca wszelkie wypełnienie służące do wyrównania wierszy. Wyrażone w pikselach, aby pasować do konwencji przyjętej przez gralloc w przypadku opisów buforów.pixelSize
. Liczba bajtów zajmowanych przez każdy piksel, czyli obliczenie w bajtach rozmiaru wymaganego do przechodzenia między wierszami na obrazie (stride
w bajtach =stride
w pikselach *pixelSize
).format
. Format pikseli użyty w obrazie. Podany format musi być zgodny z implementacją OpenGL na platformie. Aby przejść testy zgodności, w przypadku aparatu należy preferować wartośćHAL_PIXEL_FORMAT_YCRCB_420_SP
, a w przypadku wyświetlacza – wartośćRGBA
lubBGRA
.usage
. Flagi użycia ustawione przez implementację HAL. Klienty HAL powinny przekazać te niezmodyfikowane flagi (szczegółowe informacje znajdziesz w powiązanych flagachGralloc.h
).bufferId
. Unikalna wartość określona przez implementację HAL, która umożliwia rozpoznawanie bufora po dwukierunkowym przejściu przez interfejsy HAL. Wartość zapisana w tym polu może być wybrana dowolnie przez implementację HAL.memHandle
. Uchwyt bazowego bufora pamięci, który zawiera dane obrazu. Implementacja HAL może tu przechowywać uchwyt bufora Gralloc.
IEvsCameraStream
Klient implementuje ten interfejs, aby odbierać asynchroniczne przesyłki klatek wideo.
deliverFrame(BufferDesc buffer);
Odbiera wywołania od HAL za każdym razem, gdy ramka wideo jest gotowa do sprawdzenia.
Identyfikatory buforów otrzymane przez tę metodę muszą być zwracane przez wywołania do IEvsCamera::doneWithFrame()
. Gdy strumień wideo zostanie zatrzymany przez wywołanie funkcji IEvsCamera::stopVideoStream()
, ta funkcja może być nadal wykonywana, dopóki nie wyczerpie się potok. Każda ramka musi zostać zwrócona. Po dostarczeniu ostatniej ramki strumienia zostanie dostarczone NULL bufferHandle, co oznacza koniec strumienia i brak dalszych ramek. Obiektu NULLbufferHandle nie trzeba wysyłać z poziomu interfejsu doneWithFrame()
, ale wszystkie inne uchwyty muszą zostać zwrócone.
Chociaż technicznie możliwe jest używanie zastrzeżonych formatów buforów, testowanie zgodności wymaga, 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-bit R:G:B:x), BGRA (32-bit B:G:R:x). Wybrany format musi być prawidłowym źródłem tekstury OpenGL w implementacji GLES na danej platformie.
Aplikacja nie powinna korzystać z korespondencji między polem bufferId
a właściwością memHandle
w strukturze BufferDesc
. Wartości bufferId
są w zasadzie prywatne dla implementacji sterownika HAL i może on ich używać (oraz używać ponownie) według własnego uznania.
IEvsDisplay
Ten obiekt reprezentuje wyświetlacz Evs, kontroluje jego stan i obsługuje faktyczną prezentację obrazów.
getDisplayInfo() generates (DisplayDesc info);
Zwraca podstawowe informacje o wyświetlaczu EVS udostępnianym przez system (patrz DisplayDesc).
setDisplayState(DisplayState state) generates (EvsResult result);
Ustawia stan wyświetlania. Klienci mogą ustawić stan wyświetlania, aby wyrazić pożądany stan, a implementacja HAL musi w sposób płynny zaakceptować żądanie w dowolnym stanie w dowolnym innym stanie, chociaż reakcją może być zignorowanie żądania.
Po zainicjowaniu wyświetlany jest stan NOT_VISIBLE
, po którym klient powinien zażądać stanu VISIBLE_ON_NEXT_FRAME
i rozpocząć udostępnianie filmu. Gdy wyświetlanie nie jest już wymagane, klient powinien poprosić o stan NOT_VISIBLE
po przekazaniu ostatniej klatki wideo.
Jest ona ważna w przypadku każdego stanu, którego dotyczy prośba, w dowolnym momencie. Jeśli wyświetlacz jest już widoczny, powinien pozostać widoczny, jeśli ustawienie to ma wartość VISIBLE_ON_NEXT_FRAME
. Zawsze zwraca OK, chyba że żądany stan jest nieznana wartość wyliczenia, w którym przypadku zwracana jest wartość INVALID_ARG
.
getDisplayState() generates (DisplayState state);
Pobiera stan wyświetlania. Implementacja HAL powinna raportować rzeczywisty bieżący stan, który może się różnić od stanu z ostatniego żądania. Logika odpowiedzialna za zmianę stanów wyświetlacza powinna znajdować się nad warstwą urządzenia, co uniemożliwia implementacji HAL spontaniczne zmienianie stanów wyświetlacza.
getTargetBuffer() generates (handle bufferHandle);
Zwraca uchwyt do bufora ramki powiązanego z wyświetlaczem. Ten bufor może zostać zablokowany i zapisany w oprogramowaniu lub GL. Ten bufor musi zostać zwrócony przez wywołanie funkcji returnTargetBufferForDisplay()
, nawet jeśli ekran nie jest już widoczny.
Choć zastrzeżone formaty buforów są technicznie możliwe, testowanie zgodności wymaga, aby bufor był w jednym z 4 obsługiwanych formatów: NV21 (YCrCb 4:2:0), YV12 (YCrCb 4:2:0 Planar), YUYV (YCrCb 4:2:2-bitowy), RGB: BG: (32-bitowy: BG:B). Wybrany format musi być prawidłowym miejscem docelowym renderowania GL w implementacji GLES na platformie.
W wyniku błędu zwracany jest bufor z uchwytem o wartości null, ale nie trzeba go przesyłać z powrotem do funkcji returnTargetBufferForDisplay
.
returnTargetBufferForDisplay(handle bufferHandle) generates (EvsResult result);
Informuje wyświetlacz, że bufor jest gotowy do wyświetlenia. Z tym wywołaniem można używać tylko buforów pobranych za pomocą wywołania getTargetBuffer()
, a aplikacja kliencka nie może zmieniać zawartości BufferDesc
. Po tym wywołaniu bufor nie może już być używany przez klienta. Zwraca kod OK w przypadku powodzenia lub odpowiedni kod błędu (potencjalnie w tym 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 wymagane przez implementację EVS. HAL odpowiada za wypełnienie tej struktury w celu opisania wyświetlacza EVS. Może to być wyświetlacz fizyczny lub wirtualny, nakładany lub łączony z innym urządzeniem do prezentacji.
display_id
. Ciąg jednoznacznie identyfikujący wyświetlacz. Może to być nazwa jądra urządzenia lub nazwa urządzenia, na przykład widok wsteczny. Wartość tego ciągu jest wybierana przez implementację HAL i używana przez warstwę powyżej w nieprzejrzysty sposób.vendor_flags
. Metoda przekazywania zaszyfrowanych informacji o specjalnej kamerze z sterownika do niestandardowej aplikacji EVS. Informacje są przekazywane bez interpretacji od 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 }
Opisuje stan wyświetlacza EVS, który może być wyłączony (niewidoczny dla kierowcy) lub włączony (wyświetlanie obrazu kierowcy).
Obejmuje stan przejściowy, w którym ekran nie jest jeszcze widoczny, ale jest gotowy do wyświetlenia przy przekazaniu następnej klatki obrazu za pomocą wywołania returnTargetBufferForDisplay()
.
Menedżer EVS
Menedżer EVS udostępnia publiczny interfejs systemu EVS do gromadzenia i prezentowania zewnętrznych widoków z kamery. Jeśli sterowniki sprzętowe umożliwiają tylko 1 aktywne interfejsy na zasób (kamerę lub wyświetlacz), menedżer EVS umożliwia udostępnianie dostępu do kamer. Pojedyncza główna aplikacja EVS jest pierwszym klientem menedżera EVS i jedynym klientem, któremu zezwala się na zapisywanie danych wyświetlania (dodatkowym klientom można przyznać dostęp tylko do odczytu do zdjęć z aparatu).
Menedżer EVS wdraża ten sam interfejs API co podstawowe sterowniki HAL i zapewnia rozszerzoną usługę przez obsługę wielu klientów jednocześnie (więcej niż 1 klient może otworzyć kamerę w menedżerze EVS i odebrać strumień wideo).
Aplikacje nie widzą żadnych różnic w przypadku korzystania z implementacji interfejsu HAL dla sprzętu EVS lub interfejsu EVS Manager API, z tym wyjątkiem, że interfejs EVS Manager API umożliwia jednoczesny dostęp do strumienia z aparatu. Menedżer EVS jest jedynym dozwolonym klientem warstwy EVS Hardware HAL i działa jako serwer proxy dla EVS Hardware HAL.
W następnych sekcjach opisano tylko te wywołania, które w implementacji Menedżera EVS mają inne (rozszerzone) działanie. Pozostałe wywołania są identyczne z opisami w EVS HAL.
IEvsEnumerator
openCamera(string camera_id) generates (IEvsCamera camera);
Pobiera obiekt interfejsu służący do interakcji z konkretną kamerą, którą identyfikuje unikalny ciąg znaków camera_id. W przypadku niepowodzenia zwraca wartość NULL.
W warstwie menedżera EVS, o ile dostępna jest wystarczająca ilość zasobów systemowych, już otwarta kamera może zostać otwarta przez inny proces, co pozwoli na połączenie strumienia wideo z wieloma aplikacjami dla użytkowników indywidualnych. Ciągi znaków camera_id
na poziomie menedżera EVS są takie same jak te zgłoszone na poziomie sprzętu EVS.
Kamera IEVS
Implementacja IEvsCamera udostępniona przez menedżera EVS jest wirtualizowana wewnętrznie, więc operacje na kamerze wykonywane przez jednego klienta nie wpływają 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. Kamera zaczyna działać, gdy uruchamia się pierwszy klient.
doneWithFrame(uint32 frameId, handle bufferHandle) generates (EvsResult result);
Zwraca ramkę. Każdy klient musi zwrócić swoje klatki po zakończeniu, ale może je zatrzymać tak długo, jak chce. Gdy liczba klatek zatrzymana przez klienta osiągnie skonfigurowany limit, nie otrzyma kolejnych klatek, dopóki nie zwróci jednej. 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. Podstawowy strumień kamery na poziomie sprzętowym jest zatrzymywany, gdy ostatni klient danej kamery zatrzymuje swój strumień.
setExtendedInfo(int32 opaqueIdentifier, int32 opaqueValue) generates (EvsResult result);
Wysyła wartość związaną z kierowcą, dzięki czemu jeden klient może wpływać na innego klienta. Menedżer EVS nie rozumie konsekwencji słów kontrolnych zdefiniowanych przez dostawcę, dlatego nie są one wirtualizowane, a wszelkie skutki uboczne dotyczą wszystkich klientów danej kamery. Jeśli na przykład dostawca użyje tego wywołania, aby zmienić częstotliwość klatek, wszyscy klienci kamery na poziomie sprzętu otrzymają ramki z nową częstotliwością.
IEvsDisplay
Wyświetlacz może mieć tylko jednego właściciela, nawet na poziomie menedżera EVS. Menedżer nie dodaje żadnych funkcji i tylko przekazuje interfejs IEvsDisplay bezpośrednio do implementacji HAL.
aplikacja EVS
Android zawiera natywną implementację referencji w języku C++ aplikacji EVS, która komunikuje się z menedżerem EVS i interfejsem HAL pojazdu, aby zapewnić podstawowe funkcje kamery cofania. Aplikacja powinna uruchomić się już na wczesnym etapie uruchamiania systemu. Film powinien pokazać się w zależności od dostępnych kamer i stanu samochodu (stanu skrzyni biegów i sygnału kierunkowego). Producenci OEM mogą zmodyfikować lub zastąpić aplikację EVS swoją logiką i prezentacją.
Rysunek 3. przykładowa logika aplikacji EVS, pobierz listę kamer.
Rysunek 4. Przykładowa logika aplikacji EVS, wywołanie zwrotne 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. Wprawdzie wiąże się to z dodatkowymi kosztami kopiowania danych, ale daje też aplikacji możliwość wyrenderowania obrazu w buforze wyświetlania w dowolny sposób.
Na przykład aplikacja może przenieść dane piksela, co może być przydatne przy użyciu wbudowanej skali lub operacji obracania. Aplikacja może też użyć obrazu źródłowego jako tekstury OpenGL i wyrenderować złożoną scenę do bufora wyjściowego, w tym elementy wirtualne, takie jak ikony, wskazówki i animacje. Bardziej zaawansowana aplikacja może też wybrać kilka równoczesnych kamer wejściowych i połączyć je w pojedynczą ramkę wyjściową (np. do wyświetlania z góry w wirtualnym widoku otoczenia pojazdu).
Używaj EGL/SurfaceFlinger w HAL wyświetlacza EVS
W tej sekcji wyjaśniamy, jak za pomocą EGL renderować implementację EVS Display HAL w Androidzie 10.
Implementacja referencyjna HAL używa EGL do renderowania podglądu kamery na ekranie i używa libgui
do utworzenia docelowej powierzchni renderowania EGL. W Androidzie 8 (i wyższych) libgui
jest klasyfikowany jako VNDK-private, co oznacza grupę bibliotek dostępnych dla bibliotek VNDK, których nie mogą używać procesy dostawcy.
Implementacje HAL muszą znajdować się na partycji dostawcy, dlatego dostawcy nie mogą używać interfejsów Surface w implementacjach HAL.
Tworzenie biblioteki libgui na potrzeby procesów dostawcy
Użycie libgui
to jedyna opcja umożliwiająca korzystanie z EGL/SurfaceFlinger w implementacjach EVS Display HAL. Najprostszym sposobem implementacji libgui
jest użycie frameworks/native/libs/gui bezpośrednio w dodatkowym celu kompilacji w skrypcie kompilacji. Ta grupa docelowa jest identyczna z grupą docelową libgui
, z tym że zawiera 2 dodatkowe pola:
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: cele dotyczące dostawcy są tworzone za pomocą makra NO_INPUT
, które usuwa jedno 32-bitowe słowo z danych pakietu. Ponieważ usługa SurfaceFlinger oczekuje tego pola, które zostało usunięte, nie może przeanalizować pakietu. Jest to błąd 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 tworzenia. Spodziewaj się, że otrzymasz $(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 powiązania w implementacji HAL EVS
W Androidzie 8 (i nowszych) węzeł urządzenia /dev/binder
stał się dostępny wyłącznie dla procesów frameworka, a dlatego niedostępny dla procesów dostawców. Zamiast tego procesy dostawcy powinny używać interfejsu /dev/hwbinder
i koniecznie muszą przekształcić wszystkie interfejsy AIDL w HIDL. Jeśli chcesz nadal używać interfejsów AIDL między procesami dostawców, użyj domeny binder, /dev/vndbinder
.
Domena IPC | Opis |
---|---|
/dev/binder |
IPC między procesami frameworku/aplikacji a interfejsami AIDL |
/dev/hwbinder |
IPC między procesami frameworku lub dostawcy za pomocą interfejsów HIDL IPC między procesami dostawcy za pomocą interfejsów HIDL |
/dev/vndbinder |
IPC między dostawcą a procesami dostawcy za pomocą interfejsów AIDL |
Chociaż SurfaceFlinger definiuje interfejsy AIDL, procesy dostawcy mogą komunikować się z procesami frameworku tylko za pomocą interfejsów HIDL. Konwersja istniejących interfejsów AIDL na HIDL wymaga sporo pracy. Na szczęście Android udostępnia metodę wyboru sterownika separatora 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 dostawcy powinny wywoływać tę funkcję przed wywołaniem funkcji Process
lub IPCThreadState
albo przed wykonaniem jakichkolwiek wywołań funkcji binder.
Zasady SELinux
Jeśli implementacja na urządzeniu to pełny dostęp, SELinux uniemożliwia procesom dostawcy korzystanie z /dev/binder
. Na przykład implementacja EVS HAL jest przypisana do domeny hal_evs_driver
i wymaga uprawnień r/w 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 niepowodzenie kompilacji, ponieważ narusza te reguły neverallow zdefiniowane w system/sepolicy/domain.te
na urządzeniu z procesorem 32-bitowym.
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 pozwala wykrywać błędy i kierować proces tworzenia. Można go też użyć do rozwiązania opisanego powyżej naruszenia zasad w Androidzie 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 implementacji referencyjnej EVS HAL jako procesu dostawcy
W ramach odwołania możesz zastosować poniższe zmiany w funkcji packages/services/Car/evs/Android.mk
. Sprawdź, czy wszystkie opisane zmiany działają w przypadku 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;