Aby pakować i instalować moduły systemu operacyjnego Android na niższym poziomie, możesz używać formatu pliku APEX. Umożliwia niezależne kompilowanie i instalowanie komponentów, takich jak natywne usługi i biblioteki, implementacje HAL, oprogramowanie układowe, pliki konfiguracji itp.
Dostawca APEX jest instalowany automatycznie przez system kompilacji w partycji /vendor
i aktywowany w czasie wykonywania przez apexd
, tak jak APEX w innych partycjach.
Przypadki użycia
Modułowe obrazy dostawców
APEX umożliwiają naturalne łączenie i modułowanie implementacji funkcji w obrazach dostawcy.
Gdy obrazy dostawcy są tworzone jako kombinacja niezależnie utworzonych APEX dostawcy, producenci urządzeń mogą łatwo wybrać konkretne implementacje dostawcy, które chcą mieć na swoim urządzeniu. Producenci mogą nawet utworzyć nowy pakiet APEX, jeśli żaden z dostępnych nie spełnia ich potrzeb lub mają zupełnie nowy niestandardowy sprzęt.
Na przykład producent OEM może stworzyć urządzenie z implementacją AOSP APEX dla Wi-Fi, implementacją SoC Bluetooth APEX i niestandardową implementacją APEX dla telefonii.
Bez APEX dostawcy implementacja z tak wieloma zależnościami między komponentami dostawcy wymaga starannej koordynacji i śledzenia. Dzięki temu, że wszystkie komponenty (w tym pliki konfiguracyjne i dodatkowe biblioteki) są zapakowane w APEX z wyraźnie zdefiniowanymi interfejsami w dowolnym punkcie komunikacji między funkcjami, różne komponenty stają się wymienne.
Iteracja dewelopera
APEX dostawcy pomagają deweloperom szybciej ulepszać moduły dostawcy, ponieważ umożliwiają łączenie całej implementacji funkcji, takiej jak HAL Wi-Fi, w ramach APEX dostawcy. Deweloperzy mogą tworzyć i przesyłać pojedyncze wersje APEX dostawcy, aby testować zmiany, zamiast odtwarzać całą zawartość obrazu dostawcy.
Upraszcza i przyspiesza on cykl iteracji dla programistów, którzy pracują głównie nad jedną funkcją i chcą ją ulepszać.
Naturalne grupowanie obszarów funkcji w APEX upraszcza proces tworzenia, przesyłania i testowania zmian dotyczących danego obszaru funkcji. Na przykład ponowne zainstalowanie pakietu APEX spowoduje automatyczne zaktualizowanie wszystkich bibliotek i plików konfiguracji zawartych w pakiecie.
Umieszczanie obszaru funkcji w APEX upraszcza też debugowanie i powracanie do poprzedniego stanu, gdy urządzenie działa nieprawidłowo. Jeśli na przykład funkcja telefonii działa źle w nowej wersji, deweloperzy mogą spróbować zainstalować na urządzeniu starsze wdrożenie APEX (bez konieczności flashowania pełnej wersji) i sprawdzić, czy prawidłowe działanie zostało przywrócone.
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
Informacje ogólne o APEX, w tym wymagania dotyczące urządzenia, szczegóły formatu pliku i sposób instalacji, znajdziesz na głównej stronie Formatu pliku APEX.
W Android.bp
ustawienie właściwości vendor: true
powoduje, że moduł APEX staje się dostawcą APEX.
apex {
..
vendor: true,
..
}
pliki binarne i biblioteki udostępnione.
Pakiet APEX zawiera zależność warunkową w swojej ładowni, chyba że ma stabilne interfejsy.
Stabilne natywne interfejsy dla zależności APEX dostawcy obejmują cc_library
z bibliotekami stubs
i LLNDK. Te zależności są wykluczane z opakowania, a zależności są rejestrowane w pliku manifestu APEX. Plik manifestu jest przetwarzany przez linkerconfig
, aby zewnętrzne natywne zależności były dostępne w czasie wykonywania.
W tym fragmencie kodu plik APEX zawiera zarówno plik binarny (my_service
), jak i niestabilne zależności (pliki *.so
).
apex {
..
vendor: true,
binaries: ["my_service"],
..
}
W tym fragmencie kodu APEX zawiera współdzieloną bibliotekę my_standalone_lib
i wszystkie jej niestabilne zależności (jak opisano powyżej).
apex {
..
vendor: true,
native_shared_libs: ["my_standalone_lib"],
..
}
Pomniejszanie APEX
APEX może się powiększać, ponieważ zawiera niestabilne zależności. Zalecamy stosowanie łączenia stałego. Typowe biblioteki, takie jak libc++.so
i libbase.so
, można połączyć statycznie z plikami binarnymi HAL. Inną opcją jest użycie zależności, aby zapewnić stabilny interfejs. Zależność nie będzie uwzględniona w pakiecie APEX.
Implementacje HAL
Aby zdefiniować implementację HAL, podaj odpowiednie pliki binarne i biblioteki w Apex dostawcy, na przykład:
Aby w pełni ująć implementację HAL, APEX powinien też określać odpowiednie fragmenty i skrypty inicjalizacji VINTF.
Fragmenty VINTF
Fragmenty VINTF mogą być wyświetlane z apex dostawcy, jeśli fragmenty znajdują się w etc/vintf
apexa.
Użyj właściwości prebuilts
, aby umieścić fragmenty VINTF w APEX.
apex {
..
vendor: true,
prebuilts: ["fragment.xml"],
..
}
prebuilt_etc {
name: "fragment.xml",
src: "fragment.xml",
sub_dir: "vintf",
}
Interfejsy zapytań API
Gdy fragmenty VINTF są dodawane do APEX, użyj interfejsów API libbinder_ndk
, aby uzyskać mapowanie interfejsów HAL i nazwy 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 przelotowego HAL.
Skrypty inicjalizacji
APEX może zawierać skrypty inicjalizacji na 2 sposoby: (A) w ramach gotowego pliku tekstowego w pakiecie APEX lub (B) w zwykłym pliku inicjalizacji w pliku /vendor/etc
. Możesz ustawić obie opcje w tym samym Apex.
Skrypt inicjujący w Apex:
prebuilt_etc {
name: "myinit.rc",
src: "myinit.rc"
}
apex {
..
vendor: true,
prebuilts: ["myinit.rc"],
..
}
Scenariusze inicjalizacji w plikach APEX dostawcy mogą zawierać definicje service
i dyrektywy on <property or event>
.
Upewnij się, że definicja service
wskazuje binarne w tym samym pliku 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
...
Zachowaj ostrożność podczas stosowania dyrektyw on
. Skrypty inicjalizacji w APEX są analizowane i wykonywane po aktywowaniu APEX, więc niektórych zdarzeń i właściwości nie można używać. Użyj apex.all.ready=true
, aby jak najszybciej wywołać działania.
Wczytywanie APEXów może używać on init
, ale nie on early-init
.
Oprogramowanie układowe
Przykład:
Wstaw oprogramowanie układowe do dostawcy APEX za pomocą typu modułu prebuilt_firmware
w następujący 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 katalogi /apex/*/etc/firmware
w celu znalezienia modułów oprogramowania.
file_contexts
w APEX powinien odpowiednio etykietować wszystkie wpisy w natywnym oprogramowaniu, aby te pliki były dostępne dla ueventd
w czasie wykonywania. Zwykle wystarczająca jest etykieta vendor_file
. Na przykład:
(/.*)? u:object_r:vendor_file:s0
moduły jądra,
Umieść moduły jądra w APEX dostawcy jako wstępnie utworzone moduły, wykonując te czynności:
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
w APEX powinien prawidłowo oznaczać wpisy ładunku modułu jądra. Na przykład:
/etc/modules(/.*)? u:object_r:vendor_kernel_modules:s0
Moduł jądra musi być zainstalowany w wyraźny sposób. Ten przykładowy skrypt inicjalizacji 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:
Umieść nakładki zasobów w czasie wykonywania 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 konfiguracji
APEX dostawcy obsługuje różne inne pliki konfiguracji, które zwykle znajdują się w partycji dostawcy jako wstępnie utworzone w APEX dostawcy. Dodawane są kolejne pliki.
Przykłady:
- Pliki XML deklaracji funkcji
- Czujniki mają pliki XML jako wstępnie utworzone w bibliotece HAL dostawcy APEX
- Pliki konfiguracji wejścia
- Konfiguracje ekranu dotykowego jako wstępnie utworzone w APEX dostawcy tylko z konfiguracją
Bootstrap Vendor APEXes
Niektóre usługi HAL, takie jak keymint
, powinny być dostępne przed aktywacją APEX. Te HALe zwykle ustawiają early_hal
w definicji usługi w skrypcie init. Innym przykładem jest klasa animation
, która zwykle jest uruchamiana wcześniej niż zdarzenie post-fs-data
. Jeśli taka wczesna usługa HAL jest spakowana w APEX dostawcy, ustaw wartość "vendorBootstrap": true
w pliku manifestu APEX, aby można było ją aktywować wcześniej. Pamiętaj, że bootstrap APEX można aktywować tylko z gotowej lokalizacji, takiej jak /vendor/apex
, a nie z /data/apex
.
Właściwości systemowe
Oto właściwości systemu, które framework odczytuje, aby obsługiwać dostawców Apex:
input_device.config_file.apex=<apex name>
– jeśli jest ustawiony, pliki konfiguracji danych wejściowych (*.idc
,*.kl
i*.kcm
) są wyszukiwane w katalogu/etc/usr
w APEX.ro.vulkan.apex=<apex name>
– gdy jest ustawiony, sterownik Vulkan jest wczytywany z APEX. Ponieważ sterownik Vulkan jest używany przez wczesne wersje HAL, uczyń widocznym bootstrap APEX i skonfiguruj ten ładownik nazw.
Ustaw właściwości systemu w skryptach init za pomocą polecenia setprop
.
Dodatkowe funkcje
Wybór APEX podczas uruchamiania
Przykład:
Opcjonalnie można aktywować dostawcę APEX podczas uruchamiania.
Jeśli podasz nazwę pliku za pomocą właściwości systemowej ro.vendor.apex.<apex name>
, dla określonego <apex name>
zostanie aktywowany tylko Apex pasujący do tej nazwy.
APEX z wartością <apex name>
jest ignorowany (nieaktywowany), jeśli ta właściwość systemu ma wartość none
. Za pomocą tej funkcji możesz zainstalować wiele kopii Apex o tej samej nazwie. Jeśli masz kilka wersji tego samego Apex, powinny one mieć ten sam klucz.
Przykładowe zastosowania:
- Zainstaluj 3 wersje pakietu HAL dla Wi-Fi od dostawcy APEX: zespoły ds. kontroli jakości mogą uruchamiać ręczne lub automatyczne testy za pomocą jednej wersji, a następnie uruchomić ponownie urządzenie i uruchomić testy w innej wersji, a następnie porównać wyniki.
- Zainstaluj 2 wersje dostawcy interfejsu HAL aparatu APEX:aktualną i eksperymentalną. Deweloperzy korzystający z wersji dogfood mogą używać wersji eksperymentalnej bez pobierania i instalowania dodatkowego pliku, dzięki czemu mogą łatwo wrócić do wersji stabilnej.
Podczas uruchamiania apexd
szuka właściwości sysprop w określonym formacie, aby aktywować odpowiednią wersję APEX.
Oczekiwane formaty klucza usługi:
- 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
- Stały sysprop
- Służy do zmiany wartości domyślnej ustawionej na urządzeniu, które jest już uruchomione.
- Zastępuje wartość bootconfig, jeśli jest podana.
persist.vendor.apex.<apex name>
Wartość właściwości powinna być nazwą pliku APEX, który ma zostać włączony, 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,
..
}
Domyślna wersja powinna być również skonfigurowana za pomocą bootconfig w pliku 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ń wersję, która została aktywowana, ustawiając trwałą właściwość 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 bootconfig po przeflashowaniu (np. za pomocą poleceń fastboot
oem
), zmiana właściwości bootconfig dla zainstalowanego na wielu urządzeniach Apexa 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. Na przykład:
launch_cvd --noresume \
--extra_bootconfig_args "androidboot.vendor.apex.com.oem.camera.hal:=com.oem.camera.hal.my_apex_experimental";