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
Punkty APEX ułatwiają naturalne grupowanie i modularyzację wdrożeń funkcji w obrazach dostawców.
Gdy obrazy dostawcy są tworzone jako kombinacja niezależnie utworzonych przez dostawcę APEX-ów, 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 punktów APEX dostawcy wdrożenie tak wielu zależności między komponentami dostawcy wymaga dokładnej koordynacji i śledzenia. Dzięki temu, że wszystkie komponenty (w tym pliki konfiguracji 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 przetestować 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 instalowanie pakietu APEX powoduje automatyczne zaktualizowanie wszystkich pakietów biblioteki lub plików konfiguracji, które zawiera.
Wiązanie obszaru funkcji w APEX upraszcza też debugowanie lub wycofywanie zmian w przypadku zaobserwowanego niewłaściwego działania urządzenia. Jeśli na przykład usługi telefoniczne w nowej wersji nie działają prawidłowo, deweloperzy mogą zainstalować na urządzeniu starszą wersję APEX (bez konieczności aktualizowania pełnej kompilacji) i sprawdzić, czy wszystko wróci do normy.
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
sprawia, że moduł APEX staje się APEX dostawcy.
apex {
..
vendor: true,
..
}
pliki binarne i biblioteki udostępnione.
Pakiet APEX zawiera zależności tranzytowe w swojej łańcuchce, chyba że mają 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 ich nazwy są zapisywane w pliku manifestu 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 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ólną bibliotekę
my_standalone_lib
i wszystkie jej niestabilne zależności (jak opisano powyżej).
apex {
..
vendor: true,
native_shared_libs: ["my_standalone_lib"],
..
}
Pomniejsz APEX
APEX może się powiększać, ponieważ zawiera niestabilne zależności. Zalecamy stosowanie łączenia stałego. Popularne biblioteki, takie jak libc++.so
i libbase.so
, można statycznie połączyć 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 również określać odpowiednie fragmenty VINTF i skrypty inicjalizacji.
Fragmenty VINTF
Fragmenty VINTF mogą być wyświetlane z APEX dostawcy, gdy fragmenty znajdują się w regionie etc/vintf
obszaru APEX.
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 API zapytań
Po dodaniu fragmentów VINTF do APEX użyj interfejsów API libbinder_ndk
, aby pobrać 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 przelotowego HAL.
Skrypty init
APEX może zawierać skrypty inicjalizacji na 2 sposoby: (A) w ramach wstępnie utworzonego pliku tekstowego w pakiecie danych APEX lub (B) w zwykłym pliku inicjalizacji w pliku /vendor/etc
. Możesz ustawić oba te parametry w ramach tego samego 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 inicjowane w regionach APEX są analizowane i uruchamiane po aktywacji tych punktów, więc niektórych zdarzeń lub właściwości nie można używać. Używaj apex.all.ready=true
, by uruchamiać działania jak najszybciej.
Punkty APEX podczas wczytywania mogą używać on init
, ale nie on early-init
.
Oprogramowanie układowe
Przykład:
Wstaw oprogramowanie układowe do dostawcy APEX z typem 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
..
}
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
. 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 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
w 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ł 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
Punkty APEX dostawcy obsługują różne inne pliki konfiguracyjne, które zwykle można znaleźć w partycji dostawcy jako gotowe elementy w APEX dostawcy.
Przykłady:
- Pliki XML deklaracji funkcji
- Czujniki mają pliki XML jako wstępnie utworzone w HAL dostawcy czujnika APEX.
- Pliki konfiguracji wejścia
- Konfiguracje ekranu dotykowego jako wstępnie utworzone w APEX dostawcy tylko z konfiguracją
Wczytywanie dostawcy APEX
Niektóre usługi HAL, takie jak keymint
, powinny być dostępne przed aktywacją APEX. Te HAL zwykle ustawiają early_hal
w definicji usługi w skrypcie początkowym. Innym przykładem jest klasa animation
, która zwykle rozpoczyna się wcześniej niż zdarzenie post-fs-data
. Jeśli taka wczesna usługa HAL jest spakowana w pakiet APEX dostawcy, ustaw wartość apex "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
w 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, uczyń widocznym interfejs Bootstrap APEX i skonfiguruj ten ładownik nazw przestrzeni.
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ż zainstalować wiele wersji punktów APEX dostawców, które mają taką samą nazwę i ten sam klucz, a następnie za pomocą trwałych sysprop za pomocą trwałych sysprop za pomocą wybierać wersji aktywowanej podczas każdego uruchamiania wybierać wersję, która ma być aktywowana. W niektórych przypadkach deweloperskich może to być prostsze niż zainstalowanie nowej kopii raportu APEX za pomocą adb install
.
Przykładowe zastosowania:
- Zainstaluj 3 wersje interfejsu 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 inną wersję i ponownie uruchomić testy, a następnie porównać wyniki końcowe.
- Zainstaluj 2 wersje dostawcy APEX dla interfejsu HAL aparatu: aktualną i eksperymentalną: użytkownicy wersji testowej 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
- Trwał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ą tej 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,
..
}
Wersję domyślną należy też skonfigurować za pomocą konfiguracji rozruchowej 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ń 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 też zmianę wersji aktywowanej podczas uruchamiania.
W przypadku wirtualnych urządzeń referencyjnych opartych na modelu Cuttlefish możesz użyć polecenia --extra_bootconfig_args
, aby ustawić właściwość startconfig 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";