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.
Przykłady zastosowań
Modułowe obrazy dostawców
APEX umożliwiają naturalne grupowanie 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ą APEX dla Bluetootha w systemie SoC oraz niestandardową implementacją APEX dla telefonii.
Bez APEX dostawcy wdrożenie 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 w jednym pakiecie całej implementacji funkcji, np. interfejsu HAL dla Wi-Fi, w ramach APEX dostawcy. Deweloperzy mogą tworzyć i przesyłać poszczególne wersje APEX dostawcy, aby testować zmiany, zamiast odtwarzać cały obraz 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 w danym obszarze funkcji. Na przykład ponowne instalowanie pakietu APEX powoduje automatyczne zaktualizowanie wszystkich bibliotek i plików konfiguracyjnych 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 pliku 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ści tranzytowe w swojej ładowni, chyba że mają one 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 VINTF i skrypty inicjalizacji.
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 init
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 /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 plik binarny 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 inicjujące 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 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
..
}
prebuilt_firmware
moduły są instalowane w <apex name>/etc/firmware
katalogu 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
. 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ć wszystkie wpisy ładunku modułu jądra. Przykład:
/etc/modules(/.*)? u:object_r:vendor_kernel_modules:s0
Moduł jądra musi być zainstalowany w sposób jawny. 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 systemowe, 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
Apex.ro.vulkan.apex=<apex name>
– gdy jest ustawiony, sterownik Vulkan jest wczytywany z APEX. Ponieważ sterownik Vulkan jest używany przez wczesne interfejsy HAL, spraw, aby widoczny był bootstrap APEX i aby skonfigurować przestrzeń nazw linkera.
Ustaw właściwości systemu w skryptach init za pomocą polecenia setprop
.
Dodatkowe funkcje programistyczne
Wybór APEX podczas uruchamiania
Przykład:
Deweloperzy mogą też instalować wiele wersji APEX dostawcy, które mają tę samą nazwę i klucz APEX, a następnie wybierać, która wersja ma być aktywowana podczas każdego rozruchu za pomocą trwałych właściwości systemowych. W przypadku niektórych zastosowań może to być prostsze niż zainstalowanie nowej kopii APEX za pomocą adb install
.
Przykładowe zastosowania:
- Zainstaluj 3 wersje pakietu HAL dostawcy Wi-Fi APEX: zespoły ds. kontroli jakości mogą uruchamiać ręczne lub automatyczne testy za pomocą jednej wersji, a potem uruchomić ponownie inną wersję i ponownie uruchomić testy, a następnie porównać wyniki końcowe.
- Zainstaluj 2 wersje dostawcy interfejsu HAL aparatu APEX:aktualną i eksperymentalną. Użytkownicy wersji dogfood mogą korzystać z wersji eksperymentalnej bez konieczności pobierania i instalowania dodatkowego pliku, dzięki czemu mogą łatwo wrócić do poprzedniej wersji.
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ścią właściwości powinien być plik APEX, który należy aktywować.
// 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ę aktywowaną, 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 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";