Do pakowania i instalowania modułów systemu operacyjnego Android niższego poziomu możesz używać formatu pliku APEX. Umożliwia to niezależne tworzenie i instalowanie komponentów, takich jak usługi i biblioteki natywne, implementacje HAL, oprogramowanie układowe, pliki konfiguracyjne itp.
Moduły APEX dostawcy są instalowane przez system kompilacji automatycznie w /vendorpartycji i aktywowane w czasie działania przez apexd, tak jak moduły APEX 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 udostępnionych 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 APEX-em niestandardowej implementacji telefonii OEM.
Bez pakietó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
APEX-y dostawców pomagają deweloperom szybciej iterować podczas tworzenia modułów dostawców, ponieważ zawierają całą implementację funkcji, np. HAL Wi-Fi. Deweloperzy mogą następnie tworzyć i indywidualnie przesyłać pakiet APEX dostawcy w celu przetestowania zmian, 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 pakiet APEX upraszcza też proces tworzenia, wdrażania i testowania zmian w tym obszarze. Na przykład ponowna instalacja pakietu APEX automatycznie aktualizuje wszystkie dołączone biblioteki lub pliki konfiguracyjne, które zawiera pakiet APEX.
Dołączenie obszaru funkcji do pakietu APEX upraszcza też debugowanie lub przywracanie, gdy zaobserwowane zostanie nieprawidłowe działanie 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 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, w tym wymagania dotyczące urządzenia, 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ę 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_library z stubs 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 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 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 się powiększyć, ponieważ zawiera niestabilne zależności. Zalecamy używanie linkowania statycznego. Typowe biblioteki, takie jak libc++.so i libbase.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 API zapytań
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"):truejeś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
Pliki APEX mogą zawierać skrypty inicjujące na 2 sposoby: (A) w postaci gotowego 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 wczytywane początkowo mogą używać on init, ale nie on early-init.
Oprogramowanie układowe
Przykład:
Osadź oprogramowanie układowe w pakiecie 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
pakietu 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. 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. 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:
- Pliki XML deklaracji funkcji
- Funkcje czujników są dostępne w postaci plików XML jako wstępnie skompilowane moduły APEX dostawcy HAL czujnika.
- Pliki konfiguracji wejścia
- Konfiguracje ekranu dotykowego jako wstępnie skompilowane moduły APEX dostawcy tylko z konfiguracją
Wczytywanie początkowe pakietów APEX dostawcy
Niektóre usługi HAL, takie jak keymint, powinny być dostępne przed aktywowaniem pakietów APEX. Te warstwy HAL zwykle ustawiają early_hal w definicji usługi w skrypcie inicjującym. Innym przykładem są zajęcia animation, które zwykle rozpoczynają się wcześniej niż zdarzenie post-fs-data. Jeśli taki wczesny interfejs HAL jest spakowany w pakiet APEX dostawcy, oznacz go w pliku APEX Manifest jako "vendorBootstrap": true, aby można go było aktywować wcześniej. 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,*.kli*.kcm) są wyszukiwane w katalogu/etc/usrpakietu 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 APEX rozruchowy 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> zostanie aktywowany tylko pakiet APEX pasujący do nazwy pliku.
Jeśli ta właściwość systemu ma wartość none, APEX z <apex name> jest ignorowany (nie jest aktywowany). 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 w jednej wersji, a następnie ponownie uruchamiać urządzenie w innej wersji i ponownie przeprowadzać testy, a potem porównywać wyniki.
- Zainstaluj 2 wersje dostawcy APEX HAL aparatu: bieżącą i eksperymentalną: testerzy mogą używać 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>(wyeksportowano jakoro.boot.vendor.apex.<apex name>).
- Służy do ustawiania wartości domyślnej w
- Dynamiczna konfiguracja wczesnego rozruchu (
init_dev_config):- Zalecany mechanizm produkcyjny do aktywowania i wyłączania poszczególnych pakietów APEX (dostępny w Androidzie 17 i nowszych wersjach).
- Ustawia
ro.boot.vendor.apex.<apex name>dynamicznie podczas uruchamiania, zanim zostanie uruchomiony procesapexd-bootstrap. Ustawienie wartości właściwości nanonewyłącza aktywację APEX. - Szczegóły architektury znajdziesz w sekcji Konfiguracja urządzenia wczesnego rozruchu (
init_dev_config).
- Trwały sysprop (wycofany):
persist.vendor.apex.<apex name>- Funkcja dostępna tylko na potrzeby programowania. Ponieważ
apexdaktywuje wstępnie zainstalowane pakiety APEX przed zamontowaniem partycji/data, trwałe właściwości systemowe przechowywane w/datanie mogą być używane do wczesnego wyboru pakietów APEX w kompilacjach produkcyjnych.
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,
..
}
Używanie bootconfig do wyboru APEX
Aby zadeklarować, którego pakietu APEX użyć w czasie kompilacji, skonfiguruj wersję domyślną 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
Jeśli urządzenie obsługuje aktualizowanie konfiguracji rozruchu po flashowaniu (np. za pomocą poleceń fastboot oem), zmiana właściwości konfiguracji rozruchu w przypadku wielokrotnie zainstalowanego pakietu 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. Przykład:
launch_cvd --noresume \
--extra_bootconfig_args "androidboot.vendor.apex.com.oem.camera.hal:=com.oem.camera.hal.my_apex_experimental";
Używanie funkcji init_dev_config do wyboru pakietu APEX
W Androidzie 17 i nowszych wersjach, aby dynamicznie wybierać lub wyłączać pakiety APEX dostawcy w czasie działania, zaimplementuj narzędzie dostawcy wykonywane przez usługę init_dev_config.
Narzędzie działa w trakcie early-init przed apexd-bootstrap i ustawia właściwości ro.boot.vendor.apex.* za pomocą rustutils::system_properties.
Ustawienie właściwości na nazwę pliku APEX powoduje wybranie konkretnej wersji do aktywacji, a ustawienie wartości na none może służyć do wyraźnego wyłączenia aktywacji APEX, np. w przypadku SKU, w których nie ma urządzeń peryferyjnych.
use rustutils::system_properties;
fn main() {
// Select an APEX version based on detected SKU, or disable it altogether:
let hal_apex = match sku_type {
1 => "com.oem.camera.hal.my_apex_default",
2 => "com.oem.camera.hal.my_apex_experimental",
_ => "none",
};
system_properties::write(
"ro.boot.vendor.apex.com.oem.camera.hal",
hal_apex,
).expect("Failed to set vendor APEX selection property");
}
Używanie trwałych właściwości systemu na potrzeby programowania (wycofane)
Na potrzeby programowania i debugowania po uruchomieniu urządzenia możesz zmienić 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;