Dostawca APEX

Do pakowania i instalowania modułów systemu operacyjnego Android niższego poziomu możesz używać formatu pliku APEX. Umożliwia on niezależne tworzenie i instalowanie komponentów, takich jak usługi i biblioteki natywne, implementacje HAL, oprogramowanie układowe, pliki konfiguracyjne itp.

System kompilacji automatycznie instaluje APEX-y dostawcy w partycji /vendor i aktywuje je w czasie działania za pomocą apexd, tak jak APEX-y w innych partycjach.

Przypadki użycia

Modularyzacja obrazów dostawcy

APEX-y ułatwiają naturalne łączenie i modularyzację implementacji funkcji w obrazach dostawcy.

Gdy obrazy dostawcy są tworzone jako połączenie niezależnie utworzonych APEX-ów dostawcy, producenci urządzeń mogą łatwo wybierać konkretne implementacje dostawcy, które chcą mieć na swoim urządzeniu. Producenci mogą nawet utworzyć nowy APEX dostawcy, jeśli żaden z dostarczonych APEX-ów nie spełnia ich potrzeb lub jeśli mają zupełnie nowy sprzęt niestandardowy.

Na przykład producent OEM może zdecydować się na skomponowanie urządzenia z APEX-em implementacji Wi-Fi AOSP, APEX-em implementacji Bluetooth SoC i APEX-em implementacji telefonii OEM.

Bez APEX-ów dostawcy implementacja z tak wieloma zależnościami między komponentami dostawcy wymaga starannego koordynowania i śledzenia. Dzięki opakowaniu wszystkich komponentów (w tym plików konfiguracyjnych i dodatkowych bibliotek) w APEX-y z jasno określonymi interfejsami w dowolnym momencie komunikacji między funkcjami różne komponenty stają się wymienne.

Iteracja dewelopera

APEX-y dostawcy pomagają deweloperom szybciej iterować podczas tworzenia modułów dostawcy, ponieważ łączą całą implementację funkcji, np. HAL Wi-Fi, w APEX-ie dostawcy. Deweloperzy mogą następnie tworzyć i indywidualnie przesyłać APEX dostawcy, aby testować zmiany, zamiast ponownie tworzyć cały obraz dostawcy.

Upraszcza to i przyspiesza cykl iteracji dewelopera w przypadku deweloperów, którzy pracują głównie w jednym obszarze funkcji i chcą iterować tylko w tym obszarze.

Naturalne łączenie obszaru funkcji w APEX upraszcza też proces tworzenia, przesyłania i testowania zmian w tym obszarze. Na przykład ponowne zainstalowanie APEX-a automatycznie aktualizuje wszystkie dołączone biblioteki lub pliki konfiguracyjne, które zawiera APEX.

Łączenie obszaru funkcji w APEX upraszcza też debugowanie lub przywracanie, gdy obserwowane jest nieprawidłowe działanie urządzenia. Jeśli na przykład telefonia działa słabo w nowej kompilacji, deweloperzy mogą spróbować zainstalować na urządzeniu starszy APEX implementacji telefonii (bez konieczności flashowania pełnej kompilacji) i sprawdzić, czy przywrócone zostanie prawidłowe działanie.

Przykładowy proces:

# Build the entire device and flash. OR, obtain an already-flashed device.
source build/envsetup.sh && lunch oem_device-userdebug
m
fastboot flashall -w

# Test the device.
... testing ...

# Check previous behavior using a vendor APEX from one week ago, downloaded from
# your continuous integration build.
... download command ...
adb install <path to downloaded APEX>
adb reboot
... testing ...

# Edit and rebuild just the APEX to change and test behavior.
... edit APEX source contents ...
m <apex module name>
adb install out/<path to built APEX>
adb reboot
... testing ...

Przykłady

Podstawy

Ogólne informacje o APEX-ach, w tym wymagania dotyczące urządzeń, szczegóły formatu pliku i instrukcje instalacji, znajdziesz na głównej stronie Format pliku APEX.

W Android.bp ustawienie właściwości vendor: true powoduje, że moduł APEX staje się APEX-em dostawcy.

apex {
  ..
  vendor: true,
  ..
}

Pliki binarne i biblioteki współdzielone

APEX zawiera zależności przechodnie w ładunku APEX, chyba że mają one stabilne interfejsy.

Stabilne interfejsy natywne dla zależności APEX dostawcy obejmują cc_library z stubs i biblioteki LLNDK. Te zależności są wykluczone z pakowania, a zależności są rejestrowane w manifeście APEX. Manifest jest przetwarzany przez linkerconfig, dzięki czemu zewnętrzne zależności natywne są dostępne w czasie działania.

W poniższym fragmencie kodu APEX zawiera zarówno plik binarny (my_service), jak i jego niestabilne zależności (pliki *.so).

apex {
  ..
  vendor: true,
  binaries: ["my_service"],
  ..
}

W poniższym fragmencie kodu APEX zawiera bibliotekę współdzieloną my_standalone_lib i wszystkie jej niestabilne zależności (jak opisano powyżej).

apex {
  ..
  vendor: true,
  native_shared_libs: ["my_standalone_lib"],
  ..
}

Zmniejszanie rozmiaru APEX-a

APEX może się powiększać, ponieważ zawiera niestabilne zależności. Zalecamy używanie łączenia statycznego. Typowe biblioteki, takie jak libc++.so i libbase.so, można statycznie połączyć z plikami binarnymi HAL. Inną opcją może być utworzenie zależności, która będzie udostępniać stabilny interfejs. Zależność nie zostanie dołączona do APEX-a.

Implementacje HAL

Aby zdefiniować implementację HAL, podaj odpowiednie pliki binarne i biblioteki w APEX-ie dostawcy, podobnie jak w tych przykładach:

Aby w pełni enkapsulować implementację HAL, APEX powinien też określać odpowiednie fragmenty VINTF i skrypty init.

Fragmenty VINTF

Fragmenty VINTF mogą być udostępniane z APEX-a dostawcy, gdy znajdują się w etc/vintf APEX-a.

Aby osadzić fragmenty VINTF w APEX-ie, użyj właściwości prebuilts.

apex {
  ..
  vendor: true,
  prebuilts: ["fragment.xml"],
  ..
}

prebuilt_etc {
  name: "fragment.xml",
  src: "fragment.xml",
  sub_dir: "vintf",
}

Interfejsy API zapytań

Gdy fragmenty VINTF są dodawane do APEX-a, użyj interfejsów API libbinder_ndk, aby uzyskać mapowania interfejsów HAL i nazw APEX.

  • AServiceManager_isUpdatableViaApex("com.android.foo.IFoo/default") : true jeśli instancja HAL jest zdefiniowana w APEX-ie.
  • AServiceManager_getUpdatableApexName("com.android.foo.IFoo/default", ...) : pobiera nazwę APEX-a, który definiuje instancję HAL.
  • AServiceManager_openDeclaredPassthroughHal("mapper", "instance", ...) : użyj tej funkcji, aby otworzyć HAL passthrough.

Skrypty init

APEX-y mogą zawierać skrypty init na 2 sposoby: (A) jako wstępnie utworzony plik tekstowy w ładunku APEX lub (B) jako zwykły skrypt init w /vendor/etc. Możesz ustawić oba te skrypty dla tego samego APEX-a.

Skrypt init w APEX-ie:

prebuilt_etc {
  name: "myinit.rc",
  src: "myinit.rc"
}

apex {
  ..
  vendor: true,
  prebuilts: ["myinit.rc"],
  ..
}

Skrypty init w APEX-ach dostawcy mogą zawierać service definicje i on <property or event> dyrektywy.

Upewnij się, że definicja service wskazuje plik binarny w tym samym APEX-ie. Na przykład APEX com.android.foo może definiować usługę o nazwie foo-service.

on foo-service /apex/com.android.foo/bin/foo
  ...

Uważaj podczas używania dyrektyw on. Ponieważ skrypty init w APEX-ach są analizowane i wykonywane po aktywowaniu APEX-ów, nie można używać niektórych zdarzeń ani właściwości. Aby uruchamiać działania jak najwcześniej, użyj apex.all.ready=true. APEX-y wczytywania mogą używać on init, ale nie on early-init.

Oprogramowanie układowe

Przykład:

Osadź oprogramowanie układowe w APEX-ie dostawcy za pomocą typu modułu prebuilt_firmware w ten sposób:

prebuilt_firmware {
  name: "my.bin",
  src: "path_to_prebuilt_firmware",
  vendor: true,
}

apex {
  ..
  vendor: true,
  prebuilts: ["my.bin"],  // installed inside APEX as /etc/firmware/my.bin
  ..
}

Moduły prebuilt_firmware są instalowane w katalogu <apex name>/etc/firmware APEX-a. ueventd skanuje katalogi /apex/*/etc/firmware, aby znaleźć moduły oprogramowania układowego.

file_contexts APEX-a powinny prawidłowo oznaczać wszystkie wpisy ładunku oprogramowania układowego, aby zapewnić, że te pliki są dostępne dla ueventd w czasie działania. Zwykle wystarczy etykieta vendor_file. Przykład:

(/.*)? u:object_r:vendor_file:s0

Moduły jądra

Osadź moduły jądra w APEX-ie dostawcy jako wstępnie utworzone moduły w ten sposób:

prebuilt_etc {
  name: "my.ko",
  src: "my.ko",
  vendor: true,
  sub_dir: "modules"
}

apex {
  ..
  vendor: true,
  prebuilts: ["my.ko"],  // installed inside APEX as /etc/modules/my.ko
  ..
}

file_contexts APEX-a powinny prawidłowo oznaczać wszystkie wpisy ładunku modułu jądra. Przykład:

/etc/modules(/.*)? u:object_r:vendor_kernel_modules:s0

Moduły jądra muszą być instalowane jawnie. Ten przykładowy skrypt init w partycji dostawcy pokazuje instalację za pomocą insmod:

my_init.rc:

on early-boot
  insmod /apex/myapex/etc/modules/my.ko
  ..

Nakładki zasobów środowiska wykonawczego

Przykład:

Osadź nakładki zasobów środowiska wykonawczego w APEX-ie dostawcy za pomocą właściwości rros.

runtime_resource_overlay {
    name: "my_rro",
    soc_specific: true,
}


apex {
  ..
  vendor: true,
  rros: ["my_rro"],  // installed inside APEX as /overlay/my_rro.apk
  ..
}

Inne pliki konfiguracyjne

APEX-y dostawcy obsługują różne inne pliki konfiguracyjne, które zwykle znajdują się w partycji dostawcy jako wstępnie utworzone pliki w APEX-ach dostawcy. Dodajemy też kolejne.

Przykłady:

APEX-y wczytywania dostawcy

Niektóre usługi HAL, takie jak keymint, powinny być dostępne przed aktywowaniem APEX-ów. Te HAL-e zwykle ustawiają early_hal w definicji usługi w skrypcie init. Innym przykładem są zajęcia animation, które zwykle są uruchamiane wcześniej niż zdarzenie post-fs-data. Gdy taka wczesna usługa HAL jest spakowana w APEX-ie dostawcy, ustaw w manifeście APEX-a wartość "vendorBootstrap": true, aby można było ją aktywować wcześniej. Pamiętaj, że APEX-y wczytywania można aktywować tylko z wstępnie utworzonej lokalizacji, np. /vendor/apex, a nie z /data/apex.

Właściwości systemowe

Oto właściwości systemowe, które framework odczytuje, aby obsługiwać APEX-y dostawcy:

  • input_device.config_file.apex=<apex name> – gdy ta właściwość jest ustawiona, pliki konfiguracyjne wejścia (*.idc, *.kl i *.kcm) są wyszukiwane w katalogu /etc/usr APEX-a.
  • ro.vulkan.apex=<apex name> – gdy ta właściwość jest ustawiona, sterownik Vulkan jest wczytywany z APEX-a. Ponieważ sterownik Vulkan jest używany przez wczesne HAL-e, ustaw APEX APEX wczytywania i skonfiguruj widoczną przestrzeń nazw linkera.

Ustaw właściwości systemowe w skryptach init za pomocą setprop polecenia.

Dodatkowe funkcje

Wybór APEX-a podczas uruchamiania

Przykład:

APEX-y dostawcy można opcjonalnie aktywować podczas uruchamiania. Jeśli określisz nazwę pliku za pomocą właściwości systemowej ro.vendor.apex.<apex name>, tylko APEX pasujący do nazwy pliku jest aktywowany dla konkretnego <apex name>. Jeśli ta właściwość systemowa jest ustawiona na none, APEX z <apex name> jest ignorowany (nie jest aktywowany). Za pomocą tej funkcji możesz zainstalować wiele kopii APEX-a o tej samej nazwie. Jeśli istnieje kilka wersji tego samego APEX-a, powinny one mieć ten sam klucz.

Przykłady zastosowań:

  • Zainstaluj 3 wersje APEX-a dostawcy HAL Wi-Fi: zespoły QA mogą przeprowadzać testy ręczne lub automatyczne za pomocą jednej wersji, a następnie uruchomić ponownie urządzenie w innej wersji i ponownie uruchomić testy, a potem porównać wyniki końcowe.
  • Zainstaluj 2 wersje APEX-a dostawcy HAL aparatu: current i experimental: użytkownicy testujący mogą używać wersji eksperymentalnej bez pobierania i instalowania dodatkowego pliku, dzięki czemu mogą łatwo przełączać się między wersjami.

Podczas uruchamiania apexd szuka właściwości systemowych w określonym formacie, aby aktywować odpowiednią wersję APEX-a.

Oczekiwane formaty klucza właściwości:

  • Bootconfig
    • Służy do ustawiania wartości domyślnej w BoardConfig.mk.
    • androidboot.vendor.apex.<apex name>
  • Trwała właściwość systemowa
    • Służy do zmiany wartości domyślnej, ustawianej na już uruchomionym urządzeniu.
    • Jeśli jest dostępna, zastępuje wartość bootconfig.
    • persist.vendor.apex.<apex name>

Wartością właściwości powinna być nazwa pliku APEX-a, który ma zostać aktywowany, lub none, aby wyłączyć APEX-a.

// Default version.
apex {
  name: "com.oem.camera.hal.my_apex_default",
  vendor: true,
  ..
}

// Non-default version.
apex {
  name: "com.oem.camera.hal.my_apex_experimental",
  vendor: true,
  ..
}

Wersję domyślną należy też skonfigurować za pomocą bootconfig w BoardConfig.mk:

# Example for APEX "com.oem.camera.hal" with the default above:
BOARD_BOOTCONFIG += \
    androidboot.vendor.apex.com.oem.camera.hal=com.oem.camera.hal.my_apex_default

Po uruchomieniu urządzenia zmień aktywną wersję, ustawiając trwałą właściwość systemową:

$ adb root;
$ adb shell setprop \
    persist.vendor.apex.com.oem.camera.hal \
    com.oem.camera.hal.my_apex_experimental;
$ adb reboot;

Jeśli urządzenie obsługuje aktualizowanie bootconfig po flashowaniu (np. za pomocą poleceń fastboot oem), zmiana właściwości bootconfig dla APEX-a zainstalowanego w wielu wersjach powoduje też zmianę wersji aktywowanej podczas uruchamiania.

W przypadku wirtualnych urządzeń referencyjnych opartych na Cuttlefish, możesz użyć polecenia --extra_bootconfig_args, aby ustawić właściwość bootconfig bezpośrednio podczas uruchamiania. Przykład:

launch_cvd --noresume \
  --extra_bootconfig_args "androidboot.vendor.apex.com.oem.camera.hal:=com.oem.camera.hal.my_apex_experimental";