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 Sensorów 2.0 jest dostępny w Androidzie 10 i nowszych na nowych i uaktualnionych urządzeniach. Interfejs Sensors HAL 2.0 jest oparty na interfejsie Sensors HAL 1.0, ale ma kilka kluczowych różnic, które uniemożliwiają zgodność wsteczną. Interfejs HAL czujników 2.0 używa szybkich kolejek wiadomości (FMQ) do wysyłania zdarzeń czujnika z interfejsu HAL do interfejsu czujników Androida.
Interfejs HAL czujników 2.1 jest dostępny w Androidzie 11 i nowszych na nowych i uaktualnionych urządzeniach. Interfejs HAL czujników 2.1 to wersja interfejsu HAL czujników 2.0, która udostępnia typ czujnika HINGE_ANGLE i aktualizuje różne metody, aby akceptować typ HINGE_ANGLE
.
Interfejs HAL 2.1
Głównym źródłem dokumentacji interfejsu HAL Sensors HAL 2.1 jest definicja HAL w pliku hardware/interfaces/sensors/2.1/ISensors.hal.
Jeśli występuje sprzeczność między wymaganiami na tej stronie a na stronie ISensors.hal
, zastosuj wymagania na stronie ISensors.hal
.
Interfejs HAL 2.0
Głównym źródłem dokumentacji interfejsu Sensors HAL 2.0 jest definicja HAL w pliku hardware/interfaces/sensors/2.0/ISensors.hal.
Jeśli wymagania na tej stronie są sprzeczne z wymaganiami na stronie ISensors.hal
, zastosuj wymagania na stronie ISensors.hal
.
Wdrażanie interfejsu HAL 2.0 i HAL 2.1 dla czujników
Aby zaimplementować interfejs Sensors HAL 2.0 lub 2.1, obiekt musi rozszerzać interfejs ISensors
i implementować wszystkie funkcje zdefiniowane w 2.0/ISensors.hal
lub 2.1/ISensors.hal
.
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()
w przypadku HAL 2.0 i funkcję initialize_2_1()
w przypadku HAL 2.1, 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ń Event 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()
lub initialize_2_1()
musi być pierwszą funkcją wywoływaną podczas inicjowania interfejsu HAL czujników.
Udostępnianie dostępnych czujników
Aby uzyskać listę wszystkich dostępnych czujników statycznych na urządzeniu, użyj funkcji getSensorsList()
w HAL 2.0 i funkcji getSensorsList_2_1()
w HAL 2.1. 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ę zmienić, gdy proces hostujący interfejs HAL czujników zostanie ponownie uruchomiony. Nicki mogą się zmieniać po ponownym uruchomieniu urządzenia oraz 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()
lub getSensorsList_2_1()
wskazują na znaczną zmianę w porównaniu z listą czujników pobraną przed ponownym uruchomieniem, framework uruchamia ponowne uruchamianie środowiska wykonawczego Androida. Znaczące zmiany w liście czujników obejmują 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 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, 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ócą z getSensorsList()
lub getSensorsList_2_1()
. Ta lista oczekiwanych czujników może zostać skompilowana w plikach binarnych HAL lub przechowywana 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()
, określając okres próbkowania i maksymalną latencję raportowania.
Czujnik musi mieć możliwość zmiany konfiguracji w dowolnym momencie 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 artykule 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 kolejki FMQ zdarzeń za pomocą interfejsu HAL, gdy SoC jest aktywny.
Wartość 0 oznacza, że zdarzenia muszą być zgłaszane natychmiast po ich zmierzeniu, co oznacza, że pomija się kolejkę FIFO lub opróżnia ją, gdy w FIFO jest obecne co najmniej 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 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 przetwarza 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.
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 o tym, że zdarzenia są gotowe, zapisując bit EventQueueFlagBits::READ_AND_PROCESS
w funkcji EventFlag::wake
interfejsu Event FMQ. Flagę zdarzenia można utworzyć z Event FMQ za pomocą funkcji EventFlag::createEventFlag
i funkcji getEventFlagWord()
Event FMQ.
Interfejs HAL Sensorów 2.0/2.1 obsługuje zarówno write
, jak i writeBlocking
w ramach kolejki zdarzeń FMQ.
Domyślna implementacja zawiera odniesienie do 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 o zapisie musi mieć wartość EventQueueFlagBits::READ_AND_PROCESS
, która informuje framework, że zdarzenia zostały zapisane w kole FMQ zdarzeń.
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ć interfejs FMQ Wake Lock, aby określić liczbę zdarzeń WAKE_UP
, które zostały obsłużone przez ten interfejs. HAL powinien zwalniać 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ą być używane jako dane wejściowe, np. gamepad z akcelerometrem.
Gdy czujnik dynamiczny jest podłączony, z HAL czujników należy wywołać funkcję onDynamicSensorConnected
w ISensorsCallback
. 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 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ł raportów bezpośrednich.
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()
w HAL 2.0 i funkcja injectSensorsData_2_1()
w HAL 2.0 są zwykle używane do przesyłania parametrów operacyjnych do interfejsu HAL czujników. Funkcji tej można też używać do wstrzykiwania zdarzeń czujnika do konkretnego czujnika.
Weryfikacja
Aby zweryfikować 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. 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, ponieważ pozwala upewnić się, że testowane urządzenie spełnia wszystkie wymagania CDD.
Testy VTS
Testy VTS dla interfejsu Sensors HAL 2.0 znajdują się w folderze hardware/interfaces/sensors/2.0/vts.
Testy VTS dla interfejsu Sensors HAL 2.1 znajdują się w folderze hardware/interfaces/sensors/2.1/vts.
Te testy sprawdzają, czy interfejs HAL czujników jest prawidłowo zaimplementowany i czy są spełnione wszystkie wymagania w dokumentach ISensors.hal
i ISensorsCallback.hal
.
Przejście z wersji 2.0 na 2.1 interfejsu API HAL czujników
Podczas aktualizacji z wersji 2.0 na 2.1 interfejsu HAL dla czujników implementacja interfejsu HAL musi zawierać metody initialize_2_1()
, getSensorsList_2_1()
i injectSensorsData_2_1()
oraz typy HAL 2.1. Te metody muszą spełniać te same wymagania, które zostały opisane powyżej w przypadku HAL 2.0.
HAL-e w wersji podrzędnej muszą obsługiwać wszystkie funkcje z poprzednich wersji, dlatego HAL-e w wersji 2.1 muszą obsługiwać inicjowanie jako HAL-e w wersji 2.0. Aby uniknąć złożoności związanej z obsługą obu wersji HAL, zdecydowanie zalecamy używanie Multi-HAL 2.1.
Przykład implementacji własnego interfejsu HAL interfejsu Sensors 2.1 znajdziesz w pliku Sensors.h.
Przejście z wersji 1.0 na 2.0 interfejsu API HAL czujników
Podczas aktualizacji z HAL 1.0 na 2.0 sprawdź, czy implementacja HAL spełnia te wymagania.
Inicjowanie HAL
Aby utworzyć FMQ między frameworkiem a HAL, musi być obsługiwana funkcja initialize()
.
Udostępnianie dostępnych czujników
W Sensors HAL 2.0 funkcja getSensorsList()
musi zwracać tę samą wartość podczas uruchamiania urządzenia, nawet po ponownym uruchomieniu interfejsu Sensors HAL. 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 Sensors HAL 2.0 czujniki muszą aktywnie zapisywać zdarzenia czujników w Event FMQ, gdy tylko są dostępne. HAL odpowiada też za zapisywanie odpowiednich bitów do EventFlag
, aby wywołać odczyt FMQ w ramach interfejsu.
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 wersji 2.0 interfejsu HAL interfejs HAL nie wie już, kiedy przetworzył zdarzenia zapisane w FMQ. Blokada aktywacji FMQ umożliwia mu komunikowanie się z interfejsem HAL, gdy przetworzy zdarzenia WAKE_UP
.
W Sensors HAL 2.0 blokada wybudzania zabezpieczona przez interfejs Sensors HAL 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 czujników 2.0 wymaga wywołania funkcji onDynamicSensorsConnected
i onDynamicSensorsDisconnected
w funkcji ISensorsCallback
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
czujników WAKE_UP
musi być obsługiwany w Sensors HAL 2.0.
Obsługa wielu HAL-i
Interfejs HAL czujników w wersji 2.0 i 2.1 obsługuje interfejs multi-HAL za pomocą ramy interfejsu multi-HAL czujników. Szczegółowe informacje o wdrożeniu znajdziesz w artykule Przenoszenie z Sensors HAL 1.0.