Czujniki AIDL HAL

Warstwę sprzętową czujników (HAL) stanowi interfejs między frameworkiem czujników Androida a czujnikami urządzenia, takimi jak akcelerometr czy żyroskop. HAL Sensors definiuje funkcje, które należy wdrożyć, aby umożliwić platformie sterowanie czujnikami.

Interfejs Sensors AIDL HAL jest dostępny na Androidzie 13 i nowszych dla nowych i uaktualnionych urządzeń. Interfejs Sensors AIDL HAL, który jest oparty na interfejsie Sensors HAL 2.1, korzysta z interfejsu AIDL HAL i wyświetla typy czujników: śledzenie głowy i czujnik IMU o ograniczonej liczbie osi.

Interfejs HAL AIDL

Głównym źródłem dokumentacji interfejsu HAL Sensors AIDL jest definicja HAL w pliku hardware/interfaces/sensors/aidl/android/hardware/sensors/ISensors.aidl.

Wdróż interfejs HAL czujników AIDL

Aby zaimplementować interfejs HAL AIDL dla czujników, obiekt musi rozszerzać interfejs ISensors i realizować wszystkie funkcje z pliku hardware/interfaces/sensors/aidl/android/hardware/sensors/ISensors.aidl.

Inicjowanie HAL

Zanim można użyć interfejsu HAL czujników, musi on zostać zainicjowany przez interfejs API czujników Androida. Platforma wywołuje funkcję initialize(), aby przekazać do HAL Sensors 3 parametry: 2 deskryptory FMQ i 1 wskaźnik obiektu ISensorsCallback.

HAL używa pierwszego deskryptora do utworzenia zdarzenia FMQ służącego do zapisywania zdarzeń czujnika w platformie. HAL używa drugiego deskryptora do utworzenia FMQ blokady uśpienia używanego do synchronizacji, gdy HAL zwalnia blokadę uśpienia dla zdarzeń WAKE_UP. HAL musi zapisać wskaźnik do obiektu ISensorsCallback, aby można było wywołać dowolne wymagane funkcje wywołania zwrotnego.

Funkcja initialize() musi być pierwszą funkcją wywoływaną podczas inicjowania interfejsu HAL czujników.

Udostępnij dostępne czujniki

Aby uzyskać listę wszystkich dostępnych czujników statycznych na urządzeniu, użyj funkcji getSensorsList(). Ta funkcja zwraca listę czujników, z których każdy jest jednoznacznie zidentyfikowany przez swój identyfikator. Identyfikator danego czujnika nie może się zmieniać, gdy proces hostujący interfejs HAL czujników jest ponownie uruchamiany. Nicki mogą się zmieniać po ponownym uruchomieniu urządzenia i po ponownym uruchomieniu serwera systemu.

Jeśli kilka czujników ma ten sam typ i właściwość aktywacji, pierwszy czujnik na liście jest nazywany czujnikiem domyślnym i jest zwracany do aplikacji, które korzystają z funkcji getDefaultSensor(int sensorType, bool wakeUp).

Stabilność listy czujników

Jeśli po ponownym uruchomieniu HAL Sensors dane zwrócone przez getSensorsList() wskazują na znaczącą zmianę w porównaniu z listą czujników pobraną przed ponownym uruchomieniem, platforma uruchomi ponowne uruchomienie środowiska wykonawczego Androida. Znaczące zmiany na liście czujników obejmują przypadki, gdy brakuje czujnika z danym uchwytem, zmieniły się jego atrybuty lub wprowadzono nowe. Chociaż ponowne uruchomienie środowiska wykonawczego Androida jest uciążliwe dla użytkownika, jest to wymagane, ponieważ platforma Androida nie jest już w stanie spełnić umowy dotyczącej interfejsu Android API, zgodnie z którą czujniki statyczne (niedynamiczne) nie zmieniają się przez cały czas działania aplikacji. Może to też uniemożliwić platformie ponowne przywrócenie przez platformę żądań aktywnych czujników wysyłanych przez aplikacje. Dlatego dostawcy HAL powinni zapobiegać niepotrzebnym zmianom listy czujników.

Aby zapewnić stabilne uchwyty czujników, HAL musi w sposób deterministyczny przypisać dany czujnik fizyczny na urządzeniu do jego uchwytu. Chociaż interfejs HAL czujników nie wymaga konkretnej implementacji, deweloperzy mają do dyspozycji kilka opcji, które umożliwiają spełnienie tego wymagania.

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 zależy od tego, że zestaw czujników statycznych urządzenia jest zamontowany na stałe, więc HAL musi wiedzieć, kiedy wszystkie oczekiwane czujniki zostały zainicjowane, zanim wróciją z getSensorsList(). Ta lista oczekiwanych czujników może zostać skompilowana w plik binarny HAL lub zapisana w pliku konfiguracyjnym w systemie plików. Kolejność występowania może posłużyć do wyodrębnienia stabilnych uchwytów. Najlepsze rozwiązanie zależy od szczegółów implementacji HAL, ale kluczowym wymaganiem jest to, aby uchwyty czujnika nie zmieniały się po ponownym uruchomieniu HAL.

Konfigurowanie czujników

Przed aktywacją czujnika należy skonfigurować go za pomocą funkcji batch(), określając okres próbkowania i maksymalną latencję raportowania.

W dowolnym momencie za pomocą funkcji batch() można ponownie skonfigurować czujnik bez utraty danych.

Okres próbkowania

Okres próbkowania ma inne znaczenie w zależności od typu konfigurowanego czujnika:

  • Ciągła: zdarzenia z czujników są generowane w sposób ciągły.
  • W przypadku zmiany: zdarzenia są generowane nie szybciej niż w okresie próbkowania i mogą być generowane z częstotliwością niższą niż okres próbkowania, jeśli zmierzona wartość się nie zmienia.
  • jednorazowy: okres próbkowania jest ignorowany;
  • Specjalne: więcej informacji znajdziesz w sekcji Typy czujników.

Informacje o zależności 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, przez który zdarzenia mogą być opóźniane i przechowywane w FIFO sprzętowym, zanim zostaną zapisane do Event FMQ za pomocą HAL, gdy SoC jest aktywny.

Wartość 0 oznacza, że zdarzenia muszą być zgłaszane od razu po ich pomiarze, czyli albo z całkowitym pominięciem FIFO, albo opróżnieniem FIFO, gdy tylko w FIFO znajdzie się jedno zdarzenie z czujnika.

Na przykład akcelerometr aktywowany z częstotliwością 50 Hz z maksymalną latencją raportowania wynoszącą 0 wywołuje przerwania 50 razy na sekundę, gdy SoC jest aktywny.

Jeśli maksymalny czas oczekiwania na raportowanie jest większy niż 0, zdarzenia z czujnika nie muszą być zgłaszane od razu po ich wykryciu. Zdarzenia mogą być tymczasowo przechowywane w sprzętowym FIFO i raportowane partiami, 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 od razu. Pozwala to zmniejszyć liczbę przerwań wysyłanych do SoC i pozwala SoC przejść w tryb oszczędzania energii, gdy czujnik rejestruje i zbiera dane.

Z każdym zdarzeniem jest powiązana sygnatura czasowa. Opóźnienie raportowania zdarzenia nie może wpływać na jego sygnaturę czasową. Musi być ona dokładna i odpowiadać czasowi wystąpienia zdarzenia, a nie czasowi jego zgłoszenia.

Dodatkowe informacje i wymagania dotyczące raportowania zdarzeń z czujnika z niezerowym maksymalnym czasem oczekiwania na raportowanie znajdziesz w artykule Grupowanie wsadowe.

Aktywowanie czujników

Platforma włącza i wyłącza czujniki za pomocą funkcji activate(). Przed aktywacją czujnika platforma musi go najpierw skonfigurować przy użyciu funkcji batch().

Po dezaktywowaniu czujnika nie można zapisywać dodatkowych zdarzeń z tego czujnika do kolejki FMQ zdarzeń.

Czujniki spłukowania

Jeśli czujnik jest skonfigurowany do zbiorczego przesyłania danych, framework może wymusić natychmiastowe opróżnianie zbiorczego bufora zdarzeń czujnika, wywołując funkcję flush(). W efekcie zdarzenia czujnika w grupie dotyczące określonego identyfikatora czujnika są natychmiast zapisywane w kole Event FMQ. HAL czujników musi dołączyć zdarzenie flush complete do końca zdarzeń czujnika zapisywanych w wyniku wywołania funkcji flush().

Usuwanie danych odbywa się asynchronicznie (czyli ta funkcja musi zwrócić natychmiastowe ustawienia). Jeśli implementacja używa jednego kolejki FIFO dla kilku czujników, ta kolej jest opróżniana, a zdarzenie o pełnym opróżnieniu jest dodawane tylko w przypadku określonego czujnika.

Jeśli wskazany czujnik nie ma kolejki FIFO (nie można buforować) lub jeśli kolejka FIFO była pusta w momencie wywołania, flush() musi zakończyć się sukcesem i wysłać zdarzenie flushcomplete dla tego czujnika. Dotyczy to wszystkich czujników oprócz jednorazowych.

Jeśli funkcja flush() jest wywoływana w przypadku czujnika jednorazowego, funkcja flush() musi zwracać wartość BAD_VALUE i nie może generować zdarzenia FlushComplete.

Zapisywanie zdarzeń czujnika do kolejki FMQ

Komunikacja FMQ zdarzeń jest używana przez interfejs HAL czujników do przesyłania zdarzeń czujnika do interfejsu czujników Androida.

Event FMQ to zsynchronizowany FMQ, co oznacza, że każda próba zapisania większej liczby zdarzeń do FMQ niż pozwala na to dostępne miejsce kończy się niepowodzeniem zapisu. W takim przypadku HAL powinien określić, czy zapisać bieżący zbiór zdarzeń jako 2 mniejsze grupy zdarzeń, czy zapisać wszystkie zdarzenia razem, jeśli jest wystarczająco dużo miejsca.

Gdy interfejs HAL czujników zapisze żądaną liczbę zdarzeń czujnika w Event FMQ, musi powiadomić framework, że zdarzenia są gotowe, zapisując bit EventQueueFlagBits::READ_AND_PROCESS w funkcji EventFlag::wake w Event FMQ. Flagę zdarzenia można utworzyć z kolejki zdarzeń za pomocą funkcji EventFlag::createEventFlag i funkcji getEventFlagWord() tej kolejki.

HAL czujnika AIDL obsługuje zarówno write, jak i writeBlocking w zdarzeniu FMQ. Domyślna implementacja zawiera informacje o używaniu write. Jeśli używasz funkcji writeBlocking, flaga readNotification musi mieć wartość EventQueueFlagBits::EVENTS_READ, która jest ustawiana przez framework podczas odczytu zdarzeń z kolejki zdarzeń FMQ. Flaga powiadomienia zapisu musi być ustawiona na EventQueueFlagBits::READ_AND_PROCESS, co powoduje powiadamianie platformy o zapisaniu zdarzeń w FMQ.

Zdarzenia WAKE_UP

Zdarzenia WAKE_UP to zdarzenia z czujnika, które powodują, że procesor aplikacji (AP) wybudza urządzenie i od razu je obsługuje. Za każdym razem, gdy zdarzenie WAKE_UP jest zapisywane w Event FMQ, interfejs HAL czujników musi zabezpieczyć blokadę aktywacji, aby system pozostał aktywny, dopóki framework nie będzie mógł obsłużyć zdarzenia. Po otrzymaniu zdarzenia WAKE_UP framework zabezpiecza własny blokadę aktywacji, co pozwala Sensorom HAL zwolnić blokadę aktywacji. Aby zsynchronizować dane, gdy interfejs HAL czujników zwolni blokadę aktywacji, użyj kolejki FMQ blokady aktywacji.

Interfejs HAL czujników musi odczytać interfejs FMQ Wake Lock, aby określić liczbę zdarzeń WAKE_UP, które zostały obsłużone przez ten interfejs. HAL powinna zwalniać blokadę uśpienia w przypadku zdarzeń WAKE_UP tylko wtedy, gdy łączna liczba nieobsłużonych zdarzeń WAKE_UP wynosi 0. Po obsłudze zdarzeń czujnika platforma zlicza liczbę zdarzeń oznaczonych jako zdarzenia WAKE_UP i zapisują tę liczbę z powrotem do Wake Lock FMQ.

Platforma ustawia powiadomienie WakeLockQueueFlagBits::DATA_WRITTEN o zapisie w Wake Lock FMQ za każdym razem, gdy zapisuje dane w Wake Lock FMQ.

Czujniki dynamiczne

Czujniki dynamiczne to czujniki, które nie są fizycznie częścią urządzenia, ale mogą służyć jako dane wejściowe, np. pad do gier z akcelerometrem.

Gdy czujnik dynamiczny jest połączony, z HAL czujników należy wywołać funkcję onDynamicSensorConnectedISensorsCallback. Informuje on framework o nowym czujniku dynamicznym i umożliwia sterowanie nim za pomocą frameworku oraz wykorzystanie zdarzeń czujnika przez klientów.

Podobnie, gdy czujnik dynamiczny zostanie odłączony, należy wywołać funkcję onDynamicSensorDisconnected w funkcji 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 z czujników są zapisywane w określonej pamięci, a nie w zdarzeniu FMQ z pominięciem struktury czujników Androida. Klient, który rejestruje kanał bezpośredni, musi odczytać zdarzenia czujnika bezpośrednio z pamięci, która została użyta do utworzenia tego kanału, i nie otrzyma zdarzeń czujnika za pomocą interfejsu. Funkcja configDirectReport() działa podobnie do funkcji batch() w przypadku zwykłego działania i konfiguruje kanał raportów bezpośrednich.

Funkcje registerDirectChannel()unregisterDirectChannel() tworzą lub usuwają nowy kanał bezpośredni.

Tryby działania

Funkcja setOperationMode() umożliwia platformie skonfigurowanie czujnika tak, aby platforma mogła wstrzykiwać do niego dane. Jest to przydatne podczas testowania, zwłaszcza w przypadku algorytmów, które znajdują się poniżej frameworka.

Funkcja injectSensorData() jest zwykle używana do przesyłania parametrów operacyjnych do interfejsu HAL czujników. Funkcję można też wykorzystać do wstrzykiwania zdarzeń czujnika do konkretnego czujnika.

Weryfikacja

Aby sprawdzić implementację interfejsu HAL czujników, uruchom testy CTS i VTS czujników.

Testy CTS

Testy CTS dotyczące czujników są dostępne zarówno w ramach automatycznych testów CTS, jak i w aplikacji CTS Verifier.

Testy automatyczne znajdują się w katalogu cts/tests/sensor/src/android/hardware/cts. Te testy sprawdzają standardową funkcjonalność czujników, np. aktywowanie czujników, grupowanie i częstotliwość zdarzeń czujników.

Testy CTS Verifier znajdują się w katalogu cts/apps/CtsVerifier/src/com/android/cts/verifier/sensors. Testy te wymagają ręcznego wprowadzenia danych przez operatora testowego i zapewniają, że czujniki przekazują dokładne wartości.

Przejście testów CTS jest kluczowe, aby mieć pewność, że testowane urządzenie spełnia wszystkie wymagania CDD.

Testy VTS

Testy VTS dla interfejsu HAL AIDL czujników znajdują się w folderze hardware/interfaces/sensors/aidl/vts/. Te testy sprawdzają, czy interfejs HAL czujników jest prawidłowo zaimplementowany i czy są spełnione wszystkie wymagania w dokumentach ISensors.aidlISensorsCallback.aidl.

Inicjowanie HAL

Aby utworzyć FMQ między frameworkiem a HAL, musi być obsługiwana funkcja initialize().

Wyświetlanie dostępnych czujników

W interfejsie HAL usługi AIDL dotyczącej czujników funkcja getSensorsList() musi zwracać tę samą wartość podczas uruchamiania urządzenia, nawet po ponownym uruchomieniu interfejsu HAL czujników. Zgodnie z nowym wymaganiem funkcji getSensorsList() musi ona zwracać tę samą wartość podczas jednego rozruchu urządzenia, nawet w przypadku ponownych uruchomień HAL Sensors. Dzięki temu platforma może spróbować ponownie nawiązać połączenia z czujnikiem po ponownym uruchomieniu serwera systemu. Wartość zwracana przez getSensorsList() może się zmienić po ponownym uruchomieniu urządzenia.

Zapisywanie zdarzeń czujnika do kolejki FMQ

Zamiast czekać na wywołanie funkcji poll(), interfejs HAL Sensors AIDL musi aktywnie zapisywać zdarzenia z czujnika w FMQ zdarzenia, gdy są dostępne. HAL odpowiada też za zapisywanie odpowiednich bitów do EventFlag, aby wywołać odczyt FMQ w ramach frameworka.

Zdarzenia WAKE_UP

W wersji 1.0 interfejsu HAL dla czujników można było zwolnić blokadę aktywacji dla dowolnego zdarzenia WAKE_UP w dowolnym kolejnym wywołaniu funkcji poll() po opublikowaniu zdarzenia WAKE_UP w funkcji poll(), ponieważ oznaczało to, że framework przetworzył wszystkie zdarzenia czujnika i w razie potrzeby uzyskał blokadę aktywacji. W interfejsie API AIDL czujników HAL nie jest już wysyłana wiadomość o tym, że framework przetworzył zdarzenia zapisane w FMQ. Dlatego interfejs Wake Lock FMQ umożliwia mu komunikowanie się z interfejsem HAL po obsłudze zdarzeń WAKE_UP.

W interfejsie HAL usługi AIDL dotyczącej czujników blokada aktywacji zabezpieczona przez interfejs HAL usługi czujników w przypadku zdarzeń WAKE_UP musi się zaczynać od SensorsHAL_WAKEUP.

Czujniki dynamiczne

Dynamiczne czujniki zostały zwrócone przy użyciu funkcji poll() w interfejsie HAL 1.0. Interfejs HAL AIDL dla czujników wymaga, aby funkcje onDynamicSensorsConnectedonDynamicSensorsDisconnectedISensorsCallback były wywoływane za każdym razem, gdy zmieniają się dynamiczne połączenia czujników. Te funkcje wywoławcze są dostępne w ramach wskaźnika ISensorsCallback, który jest udostępniany przez funkcję initialize().

Tryby działania

Tryb DATA_INJECTION czujników WAKE_UP musi być obsługiwany.

Obsługa wielu HAL

Interfejs HAL czujników AIDL obsługuje interfejsy wielopoziomowe za pomocą ramy wielopoziomowego interfejsu HAL czujników. Szczegółowe informacje o wdrożeniu znajdziesz w artykule Przenoszenie z Sensors HAL 2.1.