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. Interfejs HAL obejmuje pakiet systemu widoku zewnętrznego (EVS) i zwykle służy do obsługi kamery cofania i wyświetlaczy widoku otoczenia w pojazdach z systemami pokładowymi 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:
Aplikacja EVS
Przykładowa aplikacja EVS w języku C++ (/packages/services/Car/evs/app
) służy jako implementacja referencyjna. Ta aplikacja odpowiada za żądania klatek wideo z menedżera EVS i wysyłanie ukończonych 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 (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 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 gromadzenie danych z fizycznych kamer oraz dostarczanie ich za pomocą wspólnych buforów pamięci rozpoznawalnych przez Gralloc. Strona wyświetlacza w implementacji odpowiada za udostępnianie wspólnego bufora pamięci, który może być wypełniany przez aplikację (zwykle za pomocą renderowania EGL) i prezentowanie ukończonych klatek w pierwszej kolejności, zanim pojawią się na ekranie fizycznym. 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, producenci OEM mogą obsługiwać funkcje wymagane przez EVS za pomocą dotychczasowych sterowników sprzętu kamery lub wyświetlacza. Ponowne wykorzystanie sterowników może być korzystne, zwłaszcza w przypadku sterowników wyświetlacza, w których prezentacja obrazów 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.
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ładamy, że zestaw kamer jest stały i rozpoznawalny w momencie uruchomienia. 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 wyścigu związanych z uruchamianiem i wyłączaniem aplikacji, ponowne otwarcie kamery powinno spowodować zamknięcie poprzedniej instancji i umożliwienie realizacji nowego żądania. Instancja kamery, która została w ten sposób wywłaszczona, musi stać się nieaktywna, oczekiwać na ostateczne zniszczenie i odpowiadać na każde żądanie zmiany stanu kamery za pomocą kodu zwrotu 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 zachowania przy otwarciu opisanego w openCamera
w każdej chwili można utworzyć nowy obiekt IEvsDisplay, który wyłącza wszystkie poprzednie instancje. Unieważnione instancje nadal istnieją i odpowiadają na wywołania funkcji wysyłane przez ich właścicieli, ale nie mogą wykonywać operacji mutacji, gdy są nieaktywne. Ostatecznie aplikacja kliencka powinna zauważyć kody zwracane przez błąd OWNERSHIP_LOST
i zamknąć interfejs nieaktywny.
closeDisplay(IEvsDisplay display);
Udostępnia interfejs IEvsDisplay (i odwrotność wywołania openDisplay()
). Niewykorzystane bufory otrzymane w ramach połączeń getTargetBuffer()
muszą zostać zwrócone do wyświetlacza przed jego zamknięciem.
getDisplayState() generates (DisplayState state);
Pobiera bieżący stan wyświetlania. Implementacja HAL powinna przekazywać 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ę powyżej warstwy urządzenia, co uniemożliwia spontaniczne zmienianie stanów wyświetlacza przez implementację HAL. Jeśli wyświetlacz nie jest obecnie obsługiwany przez żadnego klienta (przez wywołanie 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 znaków jednoznacznie identyfikujący 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 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ć.
Kamera IEVS
Ten obiekt reprezentuje pojedynczą kamerę i jest głównym interfejsem do rejestrowania 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 maksymalną liczbę 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 wykonane żadne wywołanie do tego punktu wejścia, IEvsCamera obsługuje co najmniej 1 ramkę domyślnie; więcej jest dopuszczalne.
Jeśli nie można uwzględnić żą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);
Żąda przesłania klatek z kamery EVS. IEvsCameraStream zaczyna okresowo odbierać 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 transmisji wideo wlicza się do wszelkich wymagań dotyczących czasu uruchomienia kamery cofania. Jeśli strumień nie został uruchomiony, musi zostać zwrócony kod błędu. W przeciwnym razie zwracana jest wartość OK.
oneway doneWithFrame(BufferDesc buffer);
Zwraca klatkę dostarczoną przez IEvsCameraStream. Po zakończeniu przetwarzania ramki dostarczonej do interfejsu IEvsCameraStream należy ją zwrócić do IEvsCamera w celu ponownego użycia. Dostępna jest niewielka, skończona liczba buforów (być może jeden), a jeśli zapas się wyczerpie, do czasu zwrócenia bufora nie będą dostarczane żadne kolejne klatki, co może spowodować pominięcie klatek (bufor z wartością null oznacza koniec strumienia i nie musi być zwracany przez tę funkcję). Zwraca kod OK w przypadku powodzenia lub odpowiedni kod błędu, potencjalnie zawierający 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 być zwracana, dopóki zamknięcie strumienia nie zostanie zasygnalizowane komunikatem IEvsCameraStream. 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);
Wymaga informacji dotyczących sterownika z implementacji HAL. Wartości dozwolone dla atrybutu opaqueIdentifier
są związane z kierowcą, ale żadna przekazana wartość może spowodować awarię kierowcy. Kierowca powinien zwrócić 0 w przypadku każdego nierozpoznanego polecenia opaqueIdentifier
.
setExtendedInfo(int32 opaqueIdentifier, int32 opaqueValue) generates (EvsResult result);
Wysyła do implementacji HAL wartość związaną z sterownikami. To rozszerzenie jest udostępniane tylko w celu ułatwienia korzystania z rozszerzeń dotyczących pojazdów. Żadna implementacja HAL nie powinna 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 z rozszerzeniem 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żona w pikselach, zgodnie z konwencją przyjętą przez gralloc przy opisach 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. Klienci HAL powinni przekazywać te flagi bez zmian (szczegóły znajdziesz w przypadku flagGralloc.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
. Identyfikator uchwytu do podstawowego bufora pamięci, który zawiera dane obrazu. Implementacja HAL może tu przechowywać uchwyt bufora Gralloc.
Strumień kamery IEvs
Klient wdraża ten interfejs, aby otrzymywać asynchroniczne klatki wideo.
deliverFrame(BufferDesc buffer);
Odbiera wywołania z interfejsu HAL za każdym razem, gdy klatka 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, gdy kończy się potok danych. Każda klatka musi zostać zwrócona. Gdy ostatnia klatka strumienia została dostarczona, dostarczana jest wartość NULL bufferHandle
, co oznacza koniec strumienia i brak dalszych dostaw klatek. Nie musisz wysyłać z powrotem obiektu NULLbufferHandle
z elementemdoneWithFrame()
, 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 rzeczywiste wyświetlanie 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ć stan docelowy, a implementacja HAL musi prawidłowo zaakceptować żądanie stanu w dowolnym stanie, choć odpowiedzią może być zignorowanie prośby.
Po uruchomieniu wyświetlacz jest definiowany jako element rozpoczynający działanie w stanie NOT_VISIBLE
, po czym klient powinien poprosić o stan VISIBLE_ON_NEXT_FRAME
i rozpocząć przesyłanie filmu. Gdy wyświetlacz nie jest już wymagany, klient powinien zażądać stanu NOT_VISIBLE
po upływie ostatniej klatki wideo.
Obowiązuje w przypadku każdego stanu, o którego żądanie można poprosić w dowolnym momencie. Jeśli wyświetlacz jest już widoczny, powinien pozostać widoczny, jeśli ustawiona jest 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ę stanu wyświetlania powinna istnieć nad warstwą urządzenia, co uniemożliwia samoistne zmienianie stanów wyświetlania przez wdrożenie HAL.
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 z wywołaniem returnTargetBufferForDisplay()
, nawet jeśli wyświetlacz nie jest już widoczny.
Chociaż technicznie możliwe jest używanie zastrzeżonych formatów buforów, 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-bit R:G:B:x), BGRA (32-bit B:G:R:x). Wybrany format musi być prawidłowym celem renderowania GL w ramach implementacji GLES na platformie.
W przypadku błędu zwracany jest bufor z uchwytem null, ale nie trzeba go przekazywać z powrotem do funkcji returnTargetBufferForDisplay
.
returnTargetBufferForDisplay(handle bufferHandle) generates (EvsResult result);
Informuje wyświetlacz, że bufor jest gotowy do wyświetlenia. Do tego wywołania można używać tylko buforów pobranych za pomocą wywołania getTargetBuffer()
. Aplikacja klienta nie może modyfikować zawartości BufferDesc
. Po tym wywołaniu bufor nie jest już dostępny dla klienta. W przypadku powodzenia zwraca OK, a w przeciwnym razie – odpowiedni kod błędu, potencjalnie 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, aby opisać wyświetlanie EVS. Może to być wyświetlacz fizyczny lub wirtualny, nakładany lub łączony z innym urządzeniem do prezentacji.
display_id
. Ciąg znaków jednoznacznie identyfikujący wyświetlacz. Może to być nazwa urządzenia jądra lub nazwa urządzenia, na przykład rearview. Wartość tego ciągu jest wybierana przez implementację HAL i używana przez warstwę powyżej w nieprzejrzysty sposób.vendor_flags
. Metoda przekazywania za pomocą sterownika skomplikowanych informacji o kamerze 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świetla obraz dla kierowcy).
Obejmuje stan przejściowy, w którym wyświetlacz nie jest jeszcze widoczny, ale jest gotowy do wyświetlenia następnego obrazu w ramach wywołania returnTargetBufferForDisplay()
.
Menedżer EVS
Menedżer EVS udostępnia publiczny interfejs systemu EVS do zbierania i prezentowania widoków z zewnętrznych kamer. 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 korzysta z tego samego interfejsu API co podstawowe sterowniki HAL i oferuje 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 otrzymać strumień wideo).
Aplikacje działające z wykorzystaniem implementacji sprzętowej HAL EVS lub interfejsu EVS Manager API nie różnią się pod tym względem. Wyjątkiem jest interfejs EVS Manager API, który umożliwia równoczesny dostęp do strumienia danych z kamery. Menedżer EVS jest jedynym dozwolonym klientem warstwy EVS Hardware HAL i działa jako serwer proxy dla EVS Hardware HAL.
W sekcjach poniżej opisujemy tylko te wywołania, które mają odmienne (rozszerzone) działanie w implementacji EVS Manager. Pozostałe wywołania są identyczne z opisami HAL EVS.
Narzędzie 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.
Na poziomie menedżera EVS, o ile są dostępne wystarczające zasoby systemowe, aparat, który jest już otwarty, może zostać otwarty ponownie przez inny proces, co umożliwia udostępnianie strumienia wideo wielu aplikacjom dla użytkowników. Ciągi camera_id
w warstwie menedżera EVS są takie same jak ciągi zgłoszone do warstwy sprzętowej EVS.
IEvsCamera
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ć ramki po zakończeniu korzystania, ale może je trzymać tak długo, jak chce. Gdy liczba klatek przechowywana przez klienta osiągnie skonfigurowany limit, nie będzie on otrzymywać 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, nie wpływając na działanie 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 kierowcami, co umożliwia jednemu klientowi wpływanie na innego. Ponieważ menedżer EVS nie jest w stanie zrozumieć znaczenia słów sterujących zdefiniowanych przez dostawcę, nie są one wirtualizowane, a efekty uboczne dotyczą wszystkich klientów danej kamery. Jeśli na przykład dostawca użył tego wywołania do zmiany liczby klatek, wszystkie klienty kamery w warstwie sprzętowej, której dotyczy problem, będą otrzymywać klatki z nową liczbą klatek.
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ę w C++ referencyjnej aplikacji EVS, która komunikuje się z menedżerem EVS i interfejsem HAL pojazdu, aby zapewniać podstawowe funkcje kamery cofania. Aplikacja powinna uruchamiać się bardzo wcześnie w procesie uruchamiania systemu, wyświetlając odpowiedni film w zależności od dostępnych kamer i stanu samochodu (stan biegów i kierunkowskazów). OEM może modyfikować lub zastępować aplikację EVS własną treścią i konfiguracją specyficzną dla danego pojazdu.
Dane obrazu są prezentowane aplikacji w standardowym buforze graficznym, dlatego aplikacja jest odpowiedzialna za przeniesienie obrazu z bufora źródłowego do bufora wyjściowego. Wiąże się to z kosztami kopiowania danych, ale daje też szansę aplikacji na wyrenderowanie obrazu w buforze wyświetlacza 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ć w buforze wyjściowym skomplikowaną scenę, w tym elementy wirtualne, takie jak ikony, wytyczne i animacje. Bardziej zaawansowana aplikacja może też wybrać kilka równoczesnych kamer wejściowych i połączyć je w jedną ramkę wyjściową (np. do użycia w ramach wirtualnego widoku z lotu ptaka na otoczenie 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ę w partycji dostawcy, dlatego dostawcy nie mogą używać 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 wdrożenia libgui
jest bezpośrednie użycie frameworks/native/libs/gui z użyciem dodatkowego miejsca docelowego 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 bindera w implementacji EVS HAL
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ć /dev/hwbinder
i muszą konwertować wszystkie interfejsy AIDL na HIDL. Jeśli nadal chcesz używać interfejsów AIDL między procesami dostawcy, użyj domeny wiązań (/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ę, za pomocą której można wybrać binderdriver dla libbinder
, do którego są powiązane procesy biblioteki w 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 przykładowa implementacja HAL EVS 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 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;