Dostawca – APEX

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_libi 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") : truejeś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:

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*.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ą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>
  • 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";