HIDL Java, HIDL Java

W systemie Android 8.0 architektura systemu operacyjnego Android została przebudowana w celu zdefiniowania przejrzystych interfejsów pomiędzy platformą Android niezależną od urządzenia a kodem specyficznym dla urządzenia i dostawcy. Android zdefiniował już wiele takich interfejsów w postaci interfejsów HAL, zdefiniowanych jako nagłówki C w hardware/libhardware . HIDL zastąpił te interfejsy HAL stabilnymi, wersjonowanymi interfejsami, które mogą być albo w Javie (opisane poniżej), albo być interfejsami HIDL po stronie klienta i serwera w C++ .

Interfejsy HIDL mają być używane głównie z kodu natywnego, dlatego HIDL koncentruje się na automatycznym generowaniu wydajnego kodu w C++. Jednak interfejsy HIDL muszą być również dostępne do użytku bezpośrednio z poziomu języka Java, ponieważ niektóre podsystemy Androida (takie jak telefonia) mają interfejsy Java HIDL.

Strony w tej sekcji opisują interfejs Java dla interfejsów HIDL, szczegółowo opisują sposób tworzenia, rejestrowania i korzystania z usług oraz wyjaśniają, w jaki sposób warstwy HAL i klienci HAL napisane w języku Java współdziałają z systemem HIDL RPC.

Będąc klientem

To jest przykład klienta interfejsu IFoo w pakiecie android.hardware.foo@1.0 , który jest zarejestrowany jako default nazwa usługi i usługa dodatkowa z niestandardową nazwą usługi second_impl .

Dodawanie bibliotek

Jeśli chcesz z niej skorzystać, musisz dodać zależności do odpowiedniej biblioteki pośredniczącej HIDL. Zwykle jest to biblioteka statyczna:

// in Android.bp
static_libs: [ "android.hardware.foo-V1.0-java", ],
// in Android.mk
LOCAL_STATIC_JAVA_LIBRARIES += android.hardware.foo-V1.0-java

Jeśli wiesz, że już pobierasz zależności z tych bibliotek, możesz także użyć łączenia współdzielonego:

// in Android.bp
libs: [ "android.hardware.foo-V1.0-java", ],
// in Android.mk
LOCAL_JAVA_LIBRARIES += android.hardware.foo-V1.0-java

Dodatkowe uwagi dotyczące dodawania bibliotek w systemie Android 10

Jeśli masz system lub aplikację dostawcy, która jest przeznaczona dla systemu Android 10 lub nowszego, możesz statycznie dołączyć te biblioteki. Możesz także używać (tylko) klas HIDL z niestandardowych plików JAR zainstalowanych na urządzeniu ze stabilnymi interfejsami API Java udostępnianymi przy użyciu istniejącego mechanizmu uses-library dla aplikacji systemowych. To drugie podejście pozwala zaoszczędzić miejsce na urządzeniu. Aby uzyskać więcej informacji, zobacz Implementowanie biblioteki Java SDK . W przypadku starszych aplikacji stare zachowanie zostaje zachowane.

Począwszy od Androida 10 dostępne są również „płytkie” wersje tych bibliotek. Obejmują one daną klasę, ale nie obejmują żadnej z klas zależnych. Na przykład android.hardware.foo-V1.0-java-shallow zawiera klasy w pakiecie foo, ale nie zawiera klas w android.hidl.base-V1.0-java , który zawiera klasę bazową wszystkich HIDL interfejsy. Jeśli tworzysz bibliotekę, która ma już dostępne klasy bazowe preferowanego interfejsu jako zależność, możesz użyć następujących opcji:

// in Android.bp
static_libs: [ "android.hardware.foo-V1.0-java-shallow", ],
// in Android.mk
LOCAL_STATIC_JAVA_LIBRARIES += android.hardware.foo-V1.0-java-shallow

Biblioteki podstawowe i menedżerskie HIDL również nie są już dostępne w ścieżce klas rozruchowych dla aplikacji (poprzednio były czasami używane jako ukryte API ze względu na moduł ładujący klasy z delegatem w systemie Android). Zamiast tego zostały przeniesione do nowej przestrzeni nazw z jarjar , a aplikacje korzystające z nich (koniecznie aplikacje prywatne) muszą mieć własne, osobne kopie. Moduły w ścieżce klas startowych używające HIDL muszą używać płytkich wariantów tych bibliotek Java i dodać jarjar_rules: ":framework-jarjar-rules" do swojego Android.bp , aby używać wersji tych bibliotek, która istnieje w ścieżce klas startowych.

Modyfikowanie źródła Java

Istnieje tylko jedna wersja ( @1.0 ) tej usługi, więc ten kod pobiera tylko tę wersję. Zobacz rozszerzenia interfejsu , aby dowiedzieć się, jak obsługiwać wiele różnych wersji usługi.

import android.hardware.foo.V1_0.IFoo;
...
// retry to wait until the service starts up if it is in the manifest
IFoo server = IFoo.getService(true /* retry */); // throws NoSuchElementException if not available
IFoo anotherServer = IFoo.getService("second_impl", true /* retry */);
server.doSomething(…);

Świadczenie usługi

Kod frameworka w Javie może wymagać obsługi interfejsów w celu odbierania asynchronicznych wywołań zwrotnych z warstw HAL.

W przypadku interfejsu IFooCallback w wersji 1.0 pakietu android.hardware.foo możesz zaimplementować swój interfejs w Javie, wykonując następujące kroki:

  1. Zdefiniuj swój interfejs w HIDL.
  2. Otwórz plik /tmp/android/hardware/foo/IFooCallback.java jako plik referencyjny.
  3. Utwórz nowy moduł dla swojej implementacji Java.
  4. Sprawdź klasę abstrakcyjną android.hardware.foo.V1_0.IFooCallback.Stub , a następnie napisz nową klasę, aby ją rozszerzyć i zaimplementować metody abstrakcyjne.

Przeglądanie plików wygenerowanych automatycznie

Aby wyświetlić automatycznie wygenerowane pliki, uruchom:

hidl-gen -o /tmp -Ljava \
  -randroid.hardware:hardware/interfaces \
  -randroid.hidl:system/libhidl/transport android.hardware.foo@1.0

Te polecenia generują katalog /tmp/android/hardware/foo/1.0 . W przypadku pliku hardware/interfaces/foo/1.0/IFooCallback.hal generuje to plik /tmp/android/hardware/foo/1.0/IFooCallback.java , który zawiera interfejs Java, kod proxy i kody pośredniczące (oba proxy i kody pośredniczące są zgodne z interfejsem).

-Lmakefile generuje reguły uruchamiające to polecenie w czasie kompilacji i umożliwia dołączenie android.hardware.foo-V1.0-java i powiązanie z odpowiednimi plikami. Skrypt, który automatycznie to robi dla projektu pełnego interfejsów, można znaleźć pod adresem hardware/interfaces/update-makefiles.sh . Ścieżki w tym przykładzie są względne; hardware/interfaces może być katalogiem tymczasowym w drzewie kodu, umożliwiającym opracowanie warstwy HAL przed jej opublikowaniem.

Prowadzenie usługi

HAL zapewnia interfejs IFoo , który musi wykonywać asynchroniczne wywołania zwrotne do platformy za pośrednictwem interfejsu IFooCallback . Interfejs IFooCallback nie jest zarejestrowany z nazwy jako usługa możliwa do wykrycia; zamiast tego IFoo musi zawierać metodę taką jak setFooCallback(IFooCallback x) .

Aby skonfigurować IFooCallback z wersji 1.0 pakietu android.hardware.foo , dodaj android.hardware.foo-V1.0-java do Android.mk . Kod do uruchomienia usługi to:

import android.hardware.foo.V1_0.IFoo;
import android.hardware.foo.V1_0.IFooCallback.Stub;
....
class FooCallback extends IFooCallback.Stub {
    // implement methods
}
....
// Get the service from which you will be receiving callbacks.
// This also starts the threadpool for your callback service.
IFoo server = IFoo.getService(true /* retry */); // throws NoSuchElementException if not available
....
// This must be a persistent instance variable, not local,
//   to avoid premature garbage collection.
FooCallback mFooCallback = new FooCallback();
....
// Do this once to create the callback service and tell the "foo-bar" service
server.setFooCallback(mFooCallback);

Rozszerzenia interfejsu

Zakładając, że dana usługa implementuje interfejs IFoo na wszystkich urządzeniach, możliwe jest, że na konkretnym urządzeniu usługa może udostępnić dodatkowe możliwości zaimplementowane w rozszerzeniu interfejsu IBetterFoo w następujący sposób:

interface IFoo {
   ...
};

interface IBetterFoo extends IFoo {
   ...
};

Wywołanie kodu obsługującego rozszerzony interfejs może wykorzystać metodę Java castFrom() w celu bezpiecznego rzutowania interfejsu podstawowego na interfejs rozszerzony:

IFoo baseService = IFoo.getService(true /* retry */); // throws NoSuchElementException if not available
IBetterFoo extendedService = IBetterFoo.castFrom(baseService);
if (extendedService != null) {
  // The service implements the extended interface.
} else {
  // The service implements only the base interface.
}