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.
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 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_lib
i 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++.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 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:
- Pliki XML deklaracji funkcji
- Funkcje czujników w plikach 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 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
i*.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ą i 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>
- Służy do ustawiania wartości domyślnej w
- 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";