Warstwa abstrakcji sprzętowej (HAL) czujników to interfejs między platformą czujników Androida a czujnikami urządzenia, takimi jak akcelerometr czy żyroskop. Warstwa HAL czujników określa funkcje, które muszą zostać zaimplementowane, aby umożliwić platformie sterowanie czujnikami.
Warstwa HAL czujników AIDL jest dostępna w Androidzie 13 i nowszych wersjach na nowych i zaktualizowanych urządzeniach. Warstwa HAL czujników AIDL, która jest oparta na warstwie HAL czujników 2.1, używa interfejsu AIDL HAL i udostępnia typy czujników śledzenia głowy oraz czujników IMU o ograniczonej liczbie osi.
Interfejs AIDL HAL
Głównym źródłem dokumentacji warstwy HAL czujników AIDL jest definicja HAL w hardware/interfaces/sensors/aidl/android/hardware/sensors/ISensors.aidl.
Implementowanie warstwy HAL czujników AIDL
Aby zaimplementować warstwę HAL czujników AIDL, obiekt musi rozszerzać interfejs ISensors
i implementować wszystkie funkcje zdefiniowane w
hardware/interfaces/sensors/aidl/android/hardware/sensors/ISensors.aidl.
Inicjowanie warstwy HAL
Warstwa HAL czujników musi zostać zainicjowana przez platformę czujników Androida, zanim będzie można jej używać. Platforma wywołuje funkcję initialize(), aby przekazać do warstwy HAL czujników 3 parametry: 2 deskryptory FMQ i wskaźnik do obiektu ISensorsCallback.
Warstwa HAL używa pierwszego deskryptora do utworzenia FMQ zdarzeń, który służy do zapisywania zdarzeń czujników w platformie. Warstwa HAL używa drugiego deskryptora do utworzenia FMQ blokady wybudzania, który służy do synchronizowania momentu, w którym warstwa HAL zwalnia blokadę wybudzania w przypadku zdarzeń czujników WAKE_UP. Warstwa HAL musi zapisać wskaźnik do obiektu ISensorsCallback, aby można było wywoływać wszystkie niezbędne funkcje zwrotne.
Funkcja initialize() musi być pierwszą funkcją wywoływaną podczas inicjowania warstwy HAL czujników.
Udostępnianie dostępnych czujników
Aby uzyskać listę wszystkich dostępnych czujników statycznych w urządzeniu, użyj funkcji getSensorsList(). Ta funkcja zwraca listę czujników, z których każdy jest jednoznacznie identyfikowany przez swój uchwyt. Uchwyt danego czujnika nie może się zmieniać, gdy proces hostujący warstwę HAL czujników zostanie ponownie uruchomiony. Uchwyty mogą się zmieniać po ponownym uruchomieniu urządzenia i ponownym uruchomieniu serwera systemowego.
Jeśli kilka czujników ma ten sam typ i właściwość wybudzania, pierwszy czujnik na liście jest nazywany czujnikiem domyślnym i jest zwracany do aplikacji, które używają funkcji getDefaultSensor(int sensorType, bool wakeUp).
Stabilność listy czujników
Jeśli po ponownym uruchomieniu warstwy HAL czujników dane zwracane przez getSensorsList() wskazują na znaczącą zmianę w porównaniu z listą czujników pobraną przed ponownym uruchomieniem, platforma uruchamia ponownie środowisko wykonawcze Androida. Znaczące zmiany na liście czujników obejmują przypadki, w których brakuje czujnika o danym uchwycie lub zmieniły się jego atrybuty albo gdy wprowadzono nowe czujniki. Ponowne uruchomienie środowiska wykonawczego Androida jest uciążliwe dla użytkownika, ale jest wymagane, ponieważ platforma Androida nie może już spełniać umowy API Androida, zgodnie z którą czujniki statyczne (niestatyczne) nie zmieniają się podczas działania aplikacji. Może to również uniemożliwić platformie ponowne nawiązanie aktywnych żądań czujników wysyłanych przez aplikacje. Dlatego zalecamy dostawcom warstwy HAL, aby unikali niepotrzebnych zmian na liście czujników.
Aby zapewnić stabilne uchwyty czujników, warstwa HAL musi deterministycznie mapować dany czujnik fizyczny w urządzeniu na jego uchwyt. Interfejs warstwy HAL czujników nie wymaga żadnej konkretnej implementacji, ale deweloperzy mają do dyspozycji kilka opcji, które pozwalają spełnić to wymaganie.
Listę czujników można na przykład posortować za pomocą kombinacji stałych atrybutów każdego czujnika, takich jak dostawca, model i typ czujnika. Inna opcja polega na tym, że zestaw czujników statycznych urządzenia jest stały w sprzęcie, więc warstwa HAL musi wiedzieć, kiedy wszystkie oczekiwane czujniki zakończą inicjowanie, zanim zwróci wartość z getSensorsList(). Tę listę oczekiwanych czujników można skompilować do pliku binarnego HAL lub zapisać w pliku konfiguracyjnym w systemie plików, a kolejność występowania może służyć do uzyskiwania stabilnych uchwytów. Najlepsze rozwiązanie zależy od konkretnych szczegółów implementacji warstwy HAL, ale kluczowym wymaganiem jest, aby uchwyty czujników nie zmieniały się po ponownym uruchomieniu warstwy HAL.
Konfigurowanie czujników
Zanim czujnik zostanie aktywowany, należy go skonfigurować za pomocą funkcji batch() z okresem próbkowania i maksymalnym opóźnieniem raportowania.
Czujnik można w każdej chwili ponownie skonfigurować za pomocą funkcji batch() bez utraty danych z czujnika.
Okres próbkowania
Okres próbkowania ma inne znaczenie w zależności od konfigurowanego typu czujnika:
- Ciągły: zdarzenia czujników są generowane w sposób ciągły.
- Przy zmianie: zdarzenia są generowane nie szybciej niż w okresie próbkowania i mogą być generowane z częstotliwością mniejszą niż okres próbkowania, jeśli mierzona wartość się nie zmienia.
- Jednorazowy: okres próbkowania jest ignorowany.
- Specjalny: więcej informacji znajdziesz w sekcji Typy czujników.
Więcej informacji o interakcji między okresem próbkowania a trybami raportowania czujnika znajdziesz w sekcji Tryby raportowania.
Maksymalne opóźnienie raportowania
Maksymalne opóźnienie raportowania określa maksymalny czas w nanosekundach, o jaki można opóźnić zdarzenia i przechowywać je w sprzętowym FIFO, zanim zostaną zapisane w FMQ zdarzeń przez warstwę HAL, gdy SoC jest aktywny.
Wartość zero oznacza, że zdarzenia muszą być raportowane natychmiast po ich zmierzeniu, z pominięciem FIFO lub opróżnieniem FIFO, gdy tylko w FIFO pojawi się zdarzenie z czujnika.
Na przykład akcelerometr aktywowany z częstotliwością 50 Hz i maksymalnym opóźnieniem raportowania wynoszącym zero wywołuje przerwania 50 razy na sekundę, gdy SoC jest aktywny.
Gdy maksymalne opóźnienie raportowania jest większe od zera, zdarzenia czujników nie muszą być raportowane natychmiast po ich wykryciu. Zdarzenia mogą być tymczasowo przechowywane w sprzętowym FIFO i raportowane w partiach, o ile żadne zdarzenie nie jest opóźnione o więcej niż maksymalne opóźnienie raportowania. Wszystkie zdarzenia od poprzedniej partii są rejestrowane i zwracane jednocześnie. Zmniejsza to liczbę przerwań wysyłanych do SoC i umożliwia SoC przejście w tryb niższego zużycia energii, gdy czujnik rejestruje i grupuje dane.
Każde zdarzenie ma powiązaną z nim sygnaturę czasową. Opóźnienie czasu raportowania zdarzenia nie może wpływać na sygnaturę czasową zdarzenia. Sygnatura czasowa musi być dokładna i odpowiadać czasowi, w którym zdarzenie faktycznie miało miejsce, a nie czasowi, w którym zostało zgłoszone.
Więcej informacji i wymagań dotyczących raportowania zdarzeń czujników z maksymalnym opóźnieniem raportowania różnym od zera znajdziesz w sekcji Grupowanie.
Aktywowanie czujników
Platforma włącza i wyłącza czujniki za pomocą funkcji activate().
Zanim platforma aktywuje czujnik, musi go najpierw skonfigurować za pomocą funkcji batch().
Po dezaktywacji czujnika dodatkowe zdarzenia z tego czujnika nie mogą być zapisywane w FMQ zdarzeń.
Opróżnianie czujników
Jeśli czujnik jest skonfigurowany do grupowania danych z czujnika, platforma może wymusić natychmiastowe opróżnienie zgrupowanych zdarzeń z czujnika, wywołując funkcję flush(). Powoduje to natychmiastowe zapisanie zgrupowanych zdarzeń z czujnika o określonym uchwycie w FMQ zdarzeń. Warstwa HAL czujników musi dołączyć zdarzenie zakończenia opróżniania na końcu zdarzeń z czujnika, które są zapisywane w wyniku wywołania funkcji flush().
Opróżnianie odbywa się asynchronicznie (czyli ta funkcja musi zostać zwrócona natychmiast). Jeśli implementacja używa jednego FIFO dla kilku czujników, to FIFO jest opróżniane, a zdarzenie zakończenia opróżniania jest dodawane tylko w przypadku określonego czujnika.
Jeśli określony czujnik nie ma FIFO (nie można buforować) lub jeśli FIFO było puste w momencie wywołania, funkcja flush() musi się zakończyć powodzeniem i wysłać zdarzenie zakończenia opróżniania dla tego czujnika. Dotyczy to wszystkich czujników z wyjątkiem czujników jednorazowych.
Jeśli funkcja flush() zostanie wywołana w przypadku czujnika jednorazowego, musi ona zwrócić wartość BAD_VALUE i nie generować zdarzenia zakończenia opróżniania.flush()
Zapisywanie zdarzeń czujników w FMQ
Warstwa HAL czujników używa FMQ zdarzeń do przesyłania zdarzeń czujników do platformy czujników Androida.
FMQ zdarzeń to zsynchronizowany FMQ, co oznacza, że każda próba zapisania w FMQ większej liczby zdarzeń niż pozwala na to dostępne miejsce kończy się niepowodzeniem. W takim przypadku warstwa HAL powinna określić, czy zapisać bieżący zestaw zdarzeń jako 2 mniejsze grupy zdarzeń, czy zapisać wszystkie zdarzenia razem, gdy będzie dostępna wystarczająca ilość miejsca.
Gdy warstwa HAL czujników zapisze w FMQ zdarzeń żądaną liczbę zdarzeń czujników, musi powiadomić platformę, że zdarzenia są gotowe, zapisując bit EventQueueFlagBits::READ_AND_PROCESS w funkcji EventFlag::wake FMQ zdarzeń. Element EventFlag można utworzyć z FMQ zdarzeń za pomocą funkcji EventFlag::createEventFlag i funkcji getEventFlagWord() FMQ zdarzeń.
Warstwa HAL czujników AIDL obsługuje zarówno write, jak i writeBlocking w FMQ zdarzeń.
Implementacja domyślna zawiera odniesienie do użycia funkcji write. Jeśli używana jest funkcja writeBlocking, flaga readNotification musi być ustawiona na EventQueueFlagBits::EVENTS_READ, która jest ustawiana przez platformę, gdy odczytuje ona zdarzenia z FMQ zdarzeń. Flaga powiadomienia o zapisie musi być ustawiona na EventQueueFlagBits::READ_AND_PROCESS, która powiadamia platformę, że zdarzenia zostały zapisane w FMQ zdarzeń.
Zdarzenia WAKE_UP
Zdarzenia WAKE_UP to zdarzenia czujników, które powodują wybudzenie procesora aplikacji (AP) i natychmiastowe obsłużenie zdarzenia. Za każdym razem, gdy zdarzenie WAKE_UP jest zapisywane w FMQ zdarzeń, warstwa HAL czujników musi zabezpieczyć blokadę uśpienia, aby zapewnić, że system pozostanie aktywny, dopóki platforma nie będzie mogła obsłużyć zdarzenia. Po otrzymaniu zdarzenia WAKE_UP platforma zabezpiecza własną blokadę uśpienia, co umożliwia warstwie HAL czujników zwolnienie blokady uśpienia. Aby zsynchronizować moment, w którym warstwa HAL czujników zwalnia blokadę uśpienia, użyj FMQ blokady uśpienia.
Warstwa HAL czujników musi odczytać FMQ blokady wybudzania, aby określić liczbę zdarzeń WAKE_UP, które zostały obsłużone przez platformę. Warstwa HAL powinna zwalniać blokadę uśpienia tylko w przypadku zdarzeń WAKE_UP, jeśli łączna liczba nieobsłużonych zdarzeń WAKE_UP wynosi zero.
Po obsłużeniu zdarzeń czujników platforma zlicza liczbę zdarzeń oznaczonych jako zdarzenia WAKE_UP i zapisuje tę liczbę z powrotem w FMQ blokady wybudzania.
Platforma ustawia powiadomienie o zapisie WakeLockQueueFlagBits::DATA_WRITTEN w FMQ blokady wybudzania za każdym razem, gdy zapisuje dane w FMQ blokady wybudzania.
Czujniki dynamiczne
Czujniki dynamiczne to czujniki, które nie są fizycznie częścią urządzenia, ale mogą być używane jako dane wejściowe dla urządzenia, np. gamepad z akcelerometrem.
Gdy czujnik dynamiczny jest podłączony, warstwa HAL czujników musi wywołać funkcję onDynamicSensorConnected w ISensorsCallback. Powiadamia to platformę o nowym czujniku dynamicznym i umożliwia sterowanie czujnikiem za pomocą platformy oraz korzystanie ze zdarzeń czujników przez klientów.
Podobnie, gdy czujnik dynamiczny jest odłączony, należy wywołać funkcję onDynamicSensorDisconnected w ISensorsCallback, aby platforma mogła usunąć każdy czujnik, który nie jest już dostępny.
Kanał bezpośredni
Kanał bezpośredni to metoda działania, w której zdarzenia czujników są zapisywane w określonej pamięci zamiast w FMQ zdarzeń, z pominięciem platformy czujników Androida. Klient, który rejestruje kanał bezpośredni, musi odczytywać zdarzenia czujników bezpośrednio z pamięci użytej do utworzenia kanału bezpośredniego i nie będzie otrzymywać zdarzeń czujników za pośrednictwem platformy. Funkcja configDirectReport() jest podobna do funkcji batch() w przypadku normalnego działania i konfiguruje kanał raportowania bezpośredniego.
Funkcje registerDirectChannel() i unregisterDirectChannel() tworzą
lub niszczą nowy kanał bezpośredni.
Tryby działania
Funkcja setOperationMode() umożliwia platformie skonfigurowanie czujnika w taki sposób, aby platforma mogła wstrzykiwać dane z czujnika do czujnika. Jest to przydatne do testowania, zwłaszcza w przypadku algorytmów, które znajdują się poniżej platformy.
Funkcja injectSensorData() jest zwykle używana do przesyłania parametrów operacyjnych do warstwy HAL czujników. Funkcja może też służyć do wstrzykiwania zdarzeń czujników do określonego czujnika.
Weryfikacja
Aby zweryfikować implementację warstwy HAL czujników, uruchom testy CTS i VTS czujników.
Testy CTS
Testy CTS czujników są dostępne zarówno w zautomatyzowanych testach CTS, jak i w ręcznej aplikacji CTS Verifier.
Testy zautomatyzowane znajdują się w cts/tests/sensor/src/android/hardware/cts. Testy te weryfikują standardowe funkcje czujników, takie jak aktywowanie czujników, grupowanie i częstotliwość zdarzeń czujników.
Testy CTS Verifier znajdują się w cts/apps/CtsVerifier/src/com/android/cts/verifier/sensors. Testy te wymagają ręcznego wprowadzania danych przez operatora testu i zapewniają, że czujniki raportują dokładne wartości.
Pomyślne przejście testów CTS jest niezbędne, aby zapewnić, że testowane urządzenie spełnia wszystkie wymagania CDD.
Testy VTS
Testy VTS dla warstwy HAL czujników AIDL znajdują się w
hardware/interfaces/sensors/aidl/vts/.
Testy te zapewniają, że warstwa HAL czujników jest prawidłowo zaimplementowana i że wszystkie wymagania w ISensors.aidl i ISensorsCallback.aidl są prawidłowo spełnione.
Inicjowanie warstwy HAL
Aby ustanowić FMQ między platformą a warstwą HAL, należy obsługiwać funkcję initialize().
Udostępnianie dostępnych czujników
W warstwie HAL czujników AIDL funkcja getSensorsList() musi zwracać tę samą wartość podczas jednego uruchomienia urządzenia, nawet po ponownym uruchomieniu warstwy HAL czujników. Nowym wymaganiem funkcji getSensorsList() jest to, że musi ona zwracać tę samą wartość podczas jednego uruchomienia urządzenia, nawet po ponownym uruchomieniu warstwy HAL czujników. Umożliwia to platformie ponowne nawiązanie połączeń z czujnikami w przypadku ponownego uruchomienia serwera systemowego. Wartość zwracana przez getSensorsList() może się zmienić po ponownym uruchomieniu urządzenia.
Zapisywanie zdarzeń czujników w FMQ
Zamiast czekać na wywołanie funkcji poll(), w warstwie HAL czujników AIDL warstwa HAL czujników musi aktywnie zapisywać zdarzenia czujników w FMQ zdarzeń, gdy tylko będą one dostępne. Warstwa HAL jest też odpowiedzialna za zapisywanie prawidłowych bitów w EventFlag, aby spowodować odczyt FMQ w platformie.
Zdarzenia WAKE_UP
W warstwie HAL czujników 1.0 warstwa HAL mogła zwolnić blokadę uśpienia w przypadku dowolnego zdarzenia WAKE_UP przy każdym kolejnym wywołaniu funkcji poll() po opublikowaniu zdarzenia WAKE_UP w funkcji poll(), ponieważ wskazywało to, że platforma przetworzyła wszystkie zdarzenia czujników i w razie potrzeby uzyskała blokadę uśpienia. Ponieważ w warstwie HAL czujników AIDL warstwa HAL nie jest już powiadamiana o tym, że platforma przetworzyła zdarzenia zapisane w FMQ, FMQ blokady wybudzania umożliwia platformie komunikowanie się z warstwą HAL, gdy obsłuży ona zdarzenia WAKE_UP.
W warstwie HAL czujników AIDL blokada uśpienia zabezpieczona przez warstwę HAL czujników w przypadku zdarzeń WAKE_UP musi zaczynać się od SensorsHAL_WAKEUP.
Czujniki dynamiczne
Czujniki dynamiczne były zwracane za pomocą funkcji poll() w warstwie HAL czujników 1.0.
Warstwa HAL czujników AIDL wymaga, aby funkcje onDynamicSensorsConnected i onDynamicSensorsDisconnected w ISensorsCallback były wywoływane za każdym razem, gdy zmieniają się połączenia z czujnikami dynamicznymi. Te wywołania zwrotne są dostępne w ramach wskaźnika ISensorsCallback, który jest udostępniany za pomocą funkcji initialize().
Tryby działania
Należy obsługiwać tryb DATA_INJECTION w przypadku czujników WAKE_UP.
Obsługa wielu warstw HAL
Warstwa HAL czujników AIDL obsługuje wiele warstw HAL za pomocą platformy wielu warstw HAL czujników . Szczegóły implementacji znajdziesz w sekcji Przenoszenie z warstwy HAL czujników 2.1.