Czujniki AIDL HAL

Warstwy sprzętowej abstrakcji czujników (HAL) to interfejs między frameworkiem czujników Androida a czujnikami urządzenia, takimi jak akcelerometr czy żyroskop. Interfejs HAL czujników definiuje funkcje, które muszą zostać zaimplementowane, aby umożliwić platformie sterowanie czujnikami.

Interfejs HAL AIDL dla czujników jest dostępny w Androidzie 13 i nowszych wersjach na nowych i uaktualnionych urządzeniach. 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 dla czujników AIDL

Aby zaimplementować interfejs Sensors AIDL HAL, 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. Framework wywołuje funkcję initialize(), aby przekazać 3 parametry do interfejsu HAL czujników: 2 deskryptory FMQ i 1 wskaźnik do obiektu ISensorsCallback.

HAL używa pierwszego deskryptora do utworzenia kolejki zdarzeń FMQ, która służy do zapisywania zdarzeń czujników w ramce. HAL używa drugiego deskryptora do tworzenia WakeLock FMQ, który służy do synchronizacji, gdy HAL zwalnia blokadę aktywacji dla zdarzeń czujnika 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.

Wyświetlanie dostępnych czujników

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

Po ponownym uruchomieniu interfejsu HAL czujników, jeśli dane zwrócone przez getSensorsList() wskazują na znaczną zmianę w porównaniu z listą czujników pobraną przed ponownym uruchomieniem, framework uruchamia ponownie środowisko wykonawcze Androida. Znaczące zmiany w liście czujników to m.in. przypadki, w których brakuje czujnika o danym identyfikatorze lub zmieniły się jego atrybuty albo w których wprowadzono nowe czujniki. Restartowanie środowiska uruchomieniowego Androida może być uciążliwe dla użytkownika, ale jest to konieczne, ponieważ platforma Android nie może już spełniać wymagań interfejsu API Androida, które mówią, że czujniki statyczne (niedynamiczne) nie mogą się zmieniać w trakcie działania aplikacji. Może to też uniemożliwić platformie ponowne nawiązanie połączeń z aktywną prośbą o użycie czujnika wysyłaną przez aplikacje. Dlatego dostawcy HAL powinni zapobiegać niepotrzebnym zmianom listy czujników.

Aby zapewnić stabilne uchwyty czujników, interfejs HAL musi w sposób deterministyczny mapować dany czujnik fizyczny na urządzeniu na jego uchwyt. 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. Inną opcją jest wykorzystanie faktu, że zestaw czujników statycznych urządzenia jest stały, więc HAL musi wiedzieć, kiedy wszystkie oczekiwane czujniki zakończyły inicjalizację, zanim wróci z getSensorsList(). Tę listę oczekiwanych czujników można skompilować w plikach binarnych HAL lub zapisać w pliku konfiguracyjnym w systemie plików. Kolejność elementów na liście może posłużyć do wyodrębnienia stabilnych identyfikatoró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(), podając okres próbkowania i maksymalną latencję raportowania.

Czujnik musi być w każdej chwili możliwy do ponownej konfiguracji za pomocą batch() bez utraty danych.

Okres próbkowania

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

  • Ciągły: zdarzenia czujnika są generowane w ciągłym tempie.
  • 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 jaki 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, gdy tylko zostaną zmierzone, albo pomijanie kolejki FIFO, albo opróżnianie kolejki FIFO, gdy tylko w kole znajduje się jedno zdarzenie z czujnika.

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

Jeśli maksymalny czas opóźnienia raportowania jest większe niż 0, zdarzenia czujnika nie muszą być zgłaszane natychmiast po wykryciu. Zdarzenia mogą być tymczasowo przechowywane w buforze FIFO na sprzęcie i raportowane w partiach, o ile żadne zdarzenie nie jest opóźnione o więcej niż maksymalny czas opóźnienia raportowania. Wszystkie zdarzenia od poprzedniej partii są rejestrowane i zwracane od razu. Pozwala to zmniejszyć liczbę przerwań wysyłanych do SoC i umożliwia mu przejście 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 sygnaturę czasową zdarzenia. Musi być ona dokładna i odpowiadać czasowi wystąpienia zdarzenia, a nie czasowi jego zgłoszenia.

Więcej informacji i wymagania dotyczące raportowania zdarzeń czujnika z niezerową maksymalną latencją raportowania znajdziesz w sekcji Przesyłanie zbiorcze.

Aktywowanie czujników

Platforma włącza i wyłącza czujniki za pomocą funkcji activate(). Przed aktywacją czujnika framework musi go skonfigurować za pomocą 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().

Wyczyszczanie odbywa się asynchronicznie (czyli ta funkcja musi zwracać dane natychmiast). Jeśli implementacja używa jednego kolejki FIFO dla kilku czujników, ta kolej jest opróżniana, a zdarzenie o ukończeniu opróżniania jest dodawane tylko dla określonego czujnika.

Jeśli wskazany czujnik nie ma kolejki FIFO (nie można buforować) lub w momencie wywołania kolejka FIFO była pusta, flush() musi zakończyć się sukcesem i wysłać zdarzenie flushcomplete dla tego czujnika. Dotyczy to wszystkich czujników z wyjątkiem czujników 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 Zdarzenia jest używana przez interfejs HAL czujników do przesyłania zdarzeń czujnika do interfejsu czujników Androida.

Zdarzenie FMQ jest zsynchronizowane z 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 zestaw 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 o tym, że zdarzenia są gotowe, zapisując bit EventQueueFlagBits::READ_AND_PROCESS w funkcji EventFlag::wake Event FMQ. Flagę zdarzenia można utworzyć z pomocą kolejki zdarzeń za pomocą funkcji EventFlag::createEventFlag i funkcji getEventFlagWord() kolejki zdarzeń.

Interfejs HAL AIDL dla czujników obsługuje zarówno write, jak i writeBlocking w ramach kolejki zdarzeń FMQ. Domyślna implementacja zawiera informacje o używaniu write. Jeśli używana jest funkcja writeBlocking, flaga readNotification musi mieć wartość EventQueueFlagBits::EVENTS_READ, która jest ustawiana przez framework podczas odczytu zdarzeń z kolejki zdarzeń FMQ. Flaga powiadomienia o zapisie musi mieć wartość EventQueueFlagBits::READ_AND_PROCESS, która informuje framework, że zdarzenia zostały zapisane w kole Event FMQ.

Zdarzenia WAKE_UP

Zdarzenia WAKE_UP to zdarzenia czujnika, które powodują, że procesor aplikacji (AP) natychmiast się aktywuje i obsługuje zdarzenie. 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 czujnikom HAL zwolnić blokadę aktywacji. Aby zsynchronizować dane, gdy interfejs HAL czujników zwalnia blokadę aktywacji, użyj kolejki FMQ Wake Lock.

Interfejs HAL czujników musi odczytać Wake Lock FMQ, aby określić liczbę zdarzeń WAKE_UP, które zostały obsłużone przez ten interfejs. HAL powinien zwolnić blokadę aktywacji tylko w przypadku zdarzeń WAKE_UP, jeśli łączna liczba nieobsługiwanych 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.

Framework ustawia powiadomienie o zapisywaniu WakeLockQueueFlagBits::DATA_WRITTEN 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ą fizyczną częścią urządzenia, ale mogą służyć jako wejście do urządzenia, np. gamepad z akcelerometrem.

Gdy czujnik dynamiczny jest podłą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 wykorzystywanie 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 czujnika są zapisywane w specyficznej pamięci z pominięciem interfejsu Android Sensors Framework. 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 normalnego działania i konfiguruje kanał raportowania bezpośredniego.

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

Tryby działania

Funkcja setOperationMode() umożliwia frameworkowi skonfigurowanie czujnika, aby mógł wstrzyknąć dane do czujnika. Jest to przydatne podczas testowania, zwłaszcza w przypadku algorytmów, które działają poniżej frameworku.

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.

Automatyczne testy 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. Te testy wymagają ręcznego wprowadzania danych przez operatora testu i sprawdzają, czy czujniki podają prawidłowe 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 Sensors funkcja getSensorsList() musi zwracać tę samą wartość podczas uruchamiania urządzenia, nawet po ponownym uruchomieniu interfejsu HAL usługi Sensors. Nowym wymaganiem funkcji getSensorsList() jest to, że musi ona zwracać tę samą wartość podczas uruchamiania urządzenia, nawet po ponownym uruchomieniu czujników HAL. Dzięki temu framework może próbować ponownie nawiązać połączenia z czujnikami, jeśli serwer systemowy zostanie ponownie uruchomiony. 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() w interfejsie HAL AIDL dla czujników, interfejs HAL dla czujników musi aktywnie zapisywać zdarzenia czujników do kolejki zdarzeń FMQ, gdy tylko 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 przypadku każdego kolejnego wywołania 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 interfejs HAL nie jest już powiadamiany, gdy framework przetworzył zdarzenia zapisane w FMQ. Interfejs Wake Lock FMQ umożliwia frameworkowi 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 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łania zwrotnego są dostępne w ramach wskaźnika ISensorsCallback, który jest udostępniany przez funkcję initialize().

Tryby działania

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

Obsługa wielu HAL-i

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