Dostawca APEX

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

APEX-y dostawcy są instalowane przez system kompilacji automatycznie w /vendor i aktywowane w czasie działania przez apexd, tak jak APEX-y w innych partycjach.

Przypadki użycia

Modularyzacja obrazów dostawców

Pliki APEX ułatwiają naturalne łączenie i modularyzację implementacji funkcji na obrazach dostawców.

Gdy obrazy dostawcy są tworzone jako kombinacja niezależnie zbudowanych pakietów APEX dostawcy, producenci urządzeń mogą łatwo wybierać konkretne implementacje dostawcy, które chcą mieć na swoim urządzeniu. Producenci mogą nawet utworzyć nowy pakiet APEX dostawcy, jeśli żaden z dostępnych pakietów APEX nie spełnia ich potrzeb lub jeśli mają zupełnie nowe niestandardowe urządzenie.

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 niestandardowym APEX-em implementacji telefonii OEM.

Bez interfejsów APEX dostawców wdrożenie z tak wieloma zależnościami między komponentami dostawców wymaga starannej koordynacji i śledzenia. Dzięki umieszczeniu wszystkich komponentów (w tym plików konfiguracyjnych i dodatkowych bibliotek) w pakietach APEX z jasno określonymi interfejsami w dowolnym momencie komunikacji między funkcjami różne komponenty stają się wymienne.

Iteracja dewelopera

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

Upraszcza to i przyspiesza cykl iteracji programistó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 ponowna instalacja APEX automatycznie aktualizuje wszystkie dołączone biblioteki lub pliki konfiguracyjne, które zawiera APEX.

Połączenie obszaru funkcji z pakietem APEX upraszcza też debugowanie lub przywracanie w przypadku zaobserwowania nieprawidłowego działania urządzenia. Jeśli na przykład w nowej kompilacji funkcja telefonii działa słabo, deweloperzy mogą spróbować zainstalować na urządzeniu starszą implementację telefonii APEX (bez konieczności flashowania pełnej kompilacji) i sprawdzić, czy przywróci to prawidłowe działanie.

Przykładowy przepływ pracy:

# 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, w tym wymagania dotyczące urządzenia, szczegóły formatu pliku i instrukcje instalacji, znajdziesz na głównej stronie Format pliku APEX.

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

apex {
  ..
  vendor: true,
  ..
}

Pliki binarne i biblioteki udostępnione

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

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

W tym fragmencie 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 pakiet APEX zawiera bibliotekę współdzielonąmy_standalone_libi wszystkie jej niestabilne zależności (jak opisano powyżej).

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

Zmniejszanie rozmiaru APEX

APEX może być większy, ponieważ zawiera niestabilne zależności. Zalecamy używanie linkowania statycznego. Typowe biblioteki, takie jak libc++.solibbase.so, można statycznie połączyć z binarnymi plikami HAL. Inną opcją może być utworzenie zależności, aby zapewnić stabilny interfejs. Zależność nie zostanie dołączona do pakietu APEX.

Implementacje HAL

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

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

Fragmenty VINTF

Fragmenty VINTF mogą być udostępniane z APEX-u dostawcy, jeśli znajdują się w etc/vintf tego APEX-u.

Użyj właściwości prebuilts, aby osadzić fragmenty VINTF w APEX.

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

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

Interfejsy Query API

Gdy fragmenty VINTF są dodawane do APEX, używaj 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.
  • AServiceManager_getUpdatableApexName("com.android.foo.IFoo/default", ...): pobiera nazwę APEX, która definiuje instancję HAL.
  • AServiceManager_openDeclaredPassthroughHal("mapper", "instance", ...): służy do otwierania interfejsu HAL typu passthrough.

Skrypty inicjujące

Pakiety APEX mogą zawierać skrypty inicjujące na 2 sposoby: (A) w postaci wstępnie utworzonego pliku tekstowego w ładunku APEX lub (B) w postaci zwykłego skryptu inicjującego w /vendor/etc. Możesz ustawić oba te parametry dla tego samego pakietu APEX.

Skrypt inicjujący w APEX:

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

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

Skrypty inicjujące w modułach APEX dostawcy mogą zawierać definicje service i dyrektywy on <property or event>.

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

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

Podczas korzystania z dyrektyw on zachowaj ostrożność. Skrypty inicjujące w pakietach APEX są analizowane i wykonywane po aktywowaniu pakietów APEX, więc nie można używać niektórych zdarzeń ani właściwości. Używaj apex.all.ready=true, aby wywoływać działania jak najwcześniej. Pakiety APEX wczytywania mogą używać on init, ale nie on early-init.

Oprogramowanie układowe

Przykład:

Osadź oprogramowanie układowe w APEX dostawcy z typem 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 w APEX. ueventd skanuje /apex/*/etc/firmware katalogi w poszukiwaniu modułów oprogramowania.

file_contexts APEX powinien prawidłowo oznaczać wszystkie wpisy ładunku oprogramowania układowego, aby zapewnić dostępność tych plików dla ueventd w czasie działania. Zwykle wystarczy etykieta vendor_file. Na przykład:

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

Moduły jądra

Osadź moduły jądra w APEX dostawcy jako wstępnie skompilowane 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 powinien prawidłowo oznaczać wszystkie wpisy ładunku modułu jądra. Na przykład:

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

Moduły jądra muszą być zainstalowane w sposób jednoznaczny. Poniższy przykładowy skrypt inicjujący 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:

Osadzaj nakładki zasobów w czasie działania w APEX 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 dostawców obsługują różne inne pliki konfiguracyjne, które zwykle znajdują się na partycji dostawcy jako wstępnie skompilowane pliki w APEX-ach dostawców. Dodajemy kolejne.

Przykłady:

Wczytywanie początkowe pakietów APEX dostawcy

Niektóre usługi HAL, takie jak keymint, powinny być dostępne przed aktywacją pakietów APEX. Te warstwy HAL zwykle ustawiają early_hal w definicji usługi w skrypcie inicjującym. Innym przykładem jest animation, która zwykle rozpoczyna się wcześniej niż zdarzenie post-fs-data. Jeśli taki wczesny interfejs HAL jest spakowany w APEX dostawcy, oznacz go jako "vendorBootstrap": true w pliku APEX Manifest, aby można go było wcześniej aktywować. Pamiętaj, że APEX-y rozruchowe można aktywować tylko z wbudowanej lokalizacji, np. /vendor/apex, a nie z /data/apex.

Właściwości systemowe

Są to właściwości systemu, które platforma odczytuje, aby obsługiwać pakiety APEX dostawców:

  • input_device.config_file.apex=<apex name> – po ustawieniu tej wartości pliki konfiguracji wejściowej (*.idc, *.kl*.kcm) są wyszukiwane w katalogu /etc/usr pakietu APEX.
  • ro.vulkan.apex=<apex name> – po ustawieniu sterownik Vulkan jest ładowany z APEX. Sterownik Vulkan jest używany przez wczesne warstwy HAL, więc utwórz pakiet APEX Bootstrap i skonfiguruj widoczność przestrzeni nazw linkera.

Ustaw właściwości systemu w skryptach inicjujących za pomocą polecenia setprop.

Dodatkowe funkcje

Wybór APEX podczas uruchamiania

Przykład:

Podczas uruchamiania można opcjonalnie aktywować pakiety APEX dostawców. Jeśli określisz nazwę pliku za pomocą właściwości systemu ro.vendor.apex.<apex name>, dla konkretnego <apex name> aktywowany jest tylko pakiet APEX pasujący do nazwy pliku. Moduł APEX z <apex name> jest ignorowany (nie jest aktywowany), jeśli ta właściwość systemu ma wartość none. Za pomocą tej funkcji możesz zainstalować wiele kopii pakietu APEX o tej samej nazwie. Jeśli istnieje kilka wersji tego samego pakietu APEX, powinny one mieć ten sam klucz.

Przykładowe przypadki użycia:

  • Zainstaluj 3 wersje dostawcy APEX HAL Wi-Fi: zespoły ds. kontroli jakości mogą przeprowadzać testy ręczne lub automatyczne, korzystając z jednej wersji, a następnie ponownie uruchamiać urządzenie w innej wersji i ponownie przeprowadzać testy, a potem porównywać wyniki końcowe.
  • Zainstaluj 2 wersje dostawcy APEX HAL aparatu: bieżącąeksperymentalną: testerzy mogą korzystać z wersji eksperymentalnej bez pobierania i instalowania dodatkowego pliku, dzięki czemu mogą łatwo wrócić do poprzedniej wersji.

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

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ły sysprop
    • Służy do zmiany wartości domyślnej ustawionej na urządzeniu, które zostało już uruchomione.
    • Zastępuje wartość bootconfig, jeśli jest obecna.
    • persist.vendor.apex.<apex name>

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

// 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ły sysprop:

$ 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 konfiguracji uruchamiania po flashowaniu (np. za pomocą poleceń fastboot oem), zmiana właściwości konfiguracji uruchamiania w przypadku wielu zainstalowanych pakietów APEX powoduje również 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. Na przykład:

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