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"):truejeś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:
- XML-e deklaracji funkcji
- XML-e funkcji czujników jako wstępnie utworzone pliki w APEX-ie dostawcy HAL czujników
- Pliki konfiguracyjne wejścia
- Konfiguracje ekranu dotykowego jako wstępnie utworzone pliki w APEX-ie dostawcy zawierającym tylko konfigurację
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,*.kli*.kcm) są wyszukiwane w katalogu/etc/usrAPEX-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>
- Służy do ustawiania wartości domyślnej w
- 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";