HIDL Java

Zadbaj o dobrą organizację dzięki kolekcji Zapisuj i kategoryzuj treści zgodnie ze swoimi preferencjami.

W systemie Android 8.0 system operacyjny Android został przeprojektowany, aby zdefiniować przejrzyste interfejsy mię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ć w Javie (opisane poniżej) lub być interfejsami HIDL po stronie klienta i serwera w C++ .

Interfejsy HIDL mają być używane głównie z kodu natywnego, w wyniku czego 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 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 używania 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.

Bycie klientem

To jest przykład klienta dla interfejsu IFoo w pakiecie android.hardware.foo@1.0 , który jest zarejestrowany jako default nazwa usługi oraz usługa dodatkowa o niestandardowej nazwie usługi second_impl .

Dodawanie bibliotek

Jeśli chcesz z niej korzystać, musisz dodać zależności od 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ż ściągasz zależności od tych bibliotek, możesz również użyć współdzielonego powiązania:

// 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 aplikację systemową lub dostawcę, która jest przeznaczona dla systemu Android 10 lub nowszego, możesz statycznie dołączyć te biblioteki. Możesz również używać (tylko) klas HIDL z niestandardowych plików JAR zainstalowanych na urządzeniu ze stabilnymi interfejsami Java API 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 zachowywane jest stare zachowanie.

Począwszy od systemu Android 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ż klasy bazowe preferowanego interfejsu dostępne jako zależność, możesz użyć następującego:

// 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żera HIDL nie są już dostępne w ścieżce klas rozruchowych dla aplikacji (wcześniej były one czasami używane jako ukryte API, ze względu na pierwszy ładowacz klas Androida). Zamiast tego zostały one przeniesione do nowej przestrzeni nazw za pomocą jarjar , a aplikacje korzystające z tych (koniecznie prywatnych aplikacji) muszą mieć własne oddzielne kopie. Moduły w ścieżce klasy rozruchowej 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żyć wersji tych bibliotek, która istnieje w ścieżce klas rozruchowych.

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 struktury 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 czynności:

  1. Zdefiniuj swój interfejs w HIDL.
  2. Otwórz /tmp/android/hardware/foo/IFooCallback.java jako odniesienie.
  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 automatycznie wygenerowanych plików

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 . Dla 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 (zarówno proxy i odgałęzienia 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 linkowanie do odpowiednich plików. Skrypt, który automatycznie robi to 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; sprzęt/interfejsy mogą być tymczasowym katalogiem w drzewie kodu, aby umożliwić opracowanie warstwy HAL przed jej opublikowaniem.

Prowadzenie usługi

HAL udostępnia interfejs IFoo , który musi wykonywać asynchroniczne wywołania zwrotne do platformy za pośrednictwem interfejsu IFooCallback . Interfejs IFooCallback nie jest zarejestrowany według nazwy jako usługa wykrywalna; 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 zapewnić dodatkowe możliwości zaimplementowane w rozszerzeniu interfejsu IBetterFoo , w następujący sposób:

interface IFoo {
   ...
};

interface IBetterFoo extends IFoo {
   ...
};

Wywołujący kod świadomy istnienia interfejsu rozszerzonego może użyć metody 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.
}