АПЕКС поставщика

Формат файла APEX можно использовать для упаковки и установки низкоуровневых модулей ОС Android. Он позволяет выполнять независимую сборку и установку компонентов, таких как собственные службы и библиотеки, реализации HAL, прошивки, файлы конфигурации и т. д.

APEX-файлы поставщиков устанавливаются системой сборки автоматически в раздел /vendor и активируются во время выполнения с помощью apexd , как и APEX-файлы в других разделах.

Варианты использования

Модуляризация изображений поставщиков

APEX облегчают естественное объединение и модуляризацию реализаций функций в образах поставщиков.

Когда образы поставщиков создаются как комбинация независимо созданных APEX-ов поставщиков, производители устройств могут легко выбирать конкретные реализации поставщиков, которые требуются для их устройства. Производители могут даже создать новый APEX-ов поставщиков, если ни один из предоставленных APEX-ов не соответствует их потребностям или у них есть совершенно новое пользовательское оборудование.

Например, OEM-производитель может выбрать для своего устройства реализацию Wi-Fi APEX от AOSP, реализацию Bluetooth APEX от SoC и пользовательскую реализацию телефонии APEX от OEM.

Без APEX-ов поставщиков реализация с таким количеством зависимостей между компонентами поставщиков требует тщательной координации и отслеживания. Оборачивая все компоненты (включая файлы конфигурации и дополнительные библиотеки) в APEX-ы с четко определенными интерфейсами в любой точке межфункциональной коммуникации, различные компоненты становятся взаимозаменяемыми.

Итерация разработчика

APEX-ы поставщиков помогают разработчикам быстрее итерировать при разработке модулей поставщиков, объединяя целую реализацию функции, например, HAL Wi-Fi, внутри APEX-ов поставщиков. Затем разработчики могут создавать и индивидуально запускать APEX-ов поставщиков для тестирования изменений вместо того, чтобы перестраивать весь образ поставщика.

Это упрощает и ускоряет цикл итераций для разработчиков, которые в основном работают в одной функциональной области и хотят выполнять итерации только в этой функциональной области.

Естественное объединение области функций в APEX также упрощает процесс сборки, отправки и тестирования изменений для этой области функций. Например, переустановка APEX автоматически обновляет любую связанную библиотеку или файлы конфигурации, которые включает APEX.

Объединение области функций в APEX также упрощает отладку или возврат к предыдущему состоянию при обнаружении плохого поведения устройства. Например, если телефония работает плохо в новой сборке, то разработчики могут попробовать установить на устройство более старую реализацию телефонии APEX (без необходимости прошивать полную сборку) и посмотреть, восстановится ли хорошее поведение.

Пример рабочего процесса:

# 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 ...

Примеры

Основы

Общую информацию об APEX, включая требования к устройствам, сведения о формате файла и этапы установки, см. на главной странице «Формат файла APEX».

В Android.bp установка свойства vendor: true делает модуль APEX поставщиком APEX.

apex {
  ..
  vendor: true,
  ..
}

Двоичные файлы и общие библиотеки

APEX включает транзитивные зависимости внутри полезной нагрузки APEX, если только они не имеют стабильных интерфейсов.

Стабильные собственные интерфейсы для зависимостей поставщика APEX включают cc_library с stubs и библиотеки LLNDK. Эти зависимости исключаются из упаковки, а зависимости записываются в манифест APEX. Манифест обрабатывается linkerconfig , так что внешние собственные зависимости доступны во время выполнения.

В следующем фрагменте APEX содержит как двоичный файл ( my_service ), так и его нестабильные зависимости (файлы *.so ).

apex {
  ..
  vendor: true,
  binaries: ["my_service"],
  ..
}

В следующем фрагменте APEX содержит общую библиотеку my_standalone_lib и все ее нестабильные зависимости (как описано выше).

apex {
  ..
  vendor: true,
  native_shared_libs: ["my_standalone_lib"],
  ..
}

Сделать APEX меньше

APEX может стать больше, поскольку он связывает нестабильные зависимости. Мы рекомендуем использовать статическую компоновку. Распространенные библиотеки, такие как libc++.so и libbase.so могут быть статически связаны с двоичными файлами HAL. Создание зависимости для предоставления стабильного интерфейса может быть другим вариантом. Зависимость не будет связана в APEX.

Реализации HAL

Чтобы определить реализацию HAL, предоставьте соответствующие двоичные файлы и библиотеки внутри APEX поставщика, как в следующих примерах:

Для полной инкапсуляции реализации HAL APEX также должен указывать все соответствующие фрагменты VINTF и сценарии инициализации.

Фрагменты VINTF

Фрагменты VINTF могут обслуживаться поставщиком APEX, если фрагменты расположены в etc/vintf APEX.

Используйте свойство prebuilts для встраивания фрагментов VINTF в APEX.

apex {
  ..
  vendor: true,
  prebuilts: ["fragment.xml"],
  ..
}

prebuilt_etc {
  name: "fragment.xml",
  src: "fragment.xml",
  sub_dir: "vintf",
}

Запрос API

При добавлении фрагментов VINTF в APEX используйте API-интерфейсы libbinder_ndk для получения сопоставлений интерфейсов HAL и имен APEX.

  • AServiceManager_isUpdatableViaApex("com.android.foo.IFoo/default") : true , если экземпляр HAL определен в APEX.
  • AServiceManager_getUpdatableApexName("com.android.foo.IFoo/default", ...) : получает имя APEX, которое определяет экземпляр HAL.
  • AServiceManager_openDeclaredPassthroughHal("mapper", "instance", ...) : используйте это для открытия сквозного HAL.

Скрипты инициализации

APEX могут включать сценарии инициализации двумя способами: (A) предварительно созданный текстовый файл в полезной нагрузке APEX или (B) обычный сценарий инициализации в /vendor/etc . Вы можете установить оба варианта для одного и того же APEX.

Скрипт инициализации в APEX:

prebuilt_etc {
  name: "myinit.rc",
  src: "myinit.rc"
}

apex {
  ..
  vendor: true,
  prebuilts: ["myinit.rc"],
  ..
}

Скрипты инициализации в APEX-файлах поставщиков могут содержать определения service и директивы on <property or event> .

Убедитесь, что определение service указывает на двоичный файл в том же APEX. Например, com.android.foo APEX может определять службу с именем foo-service .

on foo-service /apex/com.android.foo/bin/foo
  ...

Будьте осторожны при использовании директив on . Поскольку скрипты init в APEX анализируются и выполняются после активации APEX, некоторые события или свойства не могут быть использованы. Используйте apex.all.ready=true для запуска действий как можно раньше. Bootstrap APEX может использовать on init , но не on early-init .

Прошивка

Пример:

Встройте прошивку в APEX поставщика с типом модуля prebuilt_firmware , как показано ниже.

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 устанавливаются в каталоге <apex name>/etc/firmware сервера APEX. ueventd сканирует каталоги /apex/*/etc/firmware для поиска модулей прошивки.

file_contexts APEX должен правильно маркировать все записи полезной нагрузки прошивки, чтобы гарантировать, что эти файлы доступны ueventd во время выполнения; обычно достаточно метки vendor_file . Например:

(/.*)? u:object_r:vendor_file:s0

Модули ядра

Встраивайте модули ядра в APEX поставщика как предварительно созданные модули, как указано ниже.

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 должен правильно маркировать все записи полезной нагрузки модуля ядра. Например:

/etc/modules(/.*)? u:object_r:vendor_kernel_modules:s0

Модули ядра должны быть установлены явно. Следующий пример скрипта init в разделе поставщика показывает установку через insmod :

my_init.rc :

on early-boot
  insmod /apex/myapex/etc/modules/my.ko
  ..

Наложения ресурсов времени выполнения

Пример:

Встраивайте наложения ресурсов времени выполнения в APEX поставщика с помощью свойства rros .

runtime_resource_overlay {
    name: "my_rro",
    soc_specific: true,
}


apex {
  ..
  vendor: true,
  rros: ["my_rro"],  // installed inside APEX as /overlay/my_rro.apk
  ..
}

Другие файлы конфигурации

APEX-файлы поставщиков поддерживают различные другие файлы конфигурации, которые обычно находятся в разделе поставщика в качестве предварительно созданных файлов внутри APEX-файлов поставщиков, и в настоящее время добавляются новые файлы.

Примеры:

Bootstrap Vendor APEXes

Некоторые службы HAL, такие как keymint должны быть доступны до активации APEX. Эти HAL обычно устанавливают early_hal в своем определении службы в скрипте инициализации. Другим примером является класс animation , который обычно запускается раньше события post-fs-data . Когда такая ранняя служба HAL упакована в APEX поставщика, установите apex "vendorBootstrap": true в его манифесте APEX, чтобы ее можно было активировать раньше. Обратите внимание, что APEX начальной загрузки можно активировать только из предварительно созданного расположения, например /vendor/apex , а не из /data/apex .

Свойства системы

Ниже приведены системные свойства, которые считывает фреймворк для поддержки APEX-ов поставщиков:

  • input_device.config_file.apex=<apex name> — если установлено, файлы конфигурации ввода ( *.idc , *.kl и *.kcm ) ищутся в каталоге /etc/usr APEX.
  • ro.vulkan.apex=<apex name> - если установлено, драйвер Vulkan загружается из APEX. Поскольку драйвер Vulkan используется ранними HAL, сделайте APEX Bootstrap APEX и настройте это пространство имен компоновщика видимым.

Задайте свойства системы в сценариях инициализации с помощью команды setprop .

Дополнительные возможности

Выбор APEX при загрузке

Пример:

APEX-ы поставщиков могут быть опционально активированы во время загрузки. Если указать имя файла с помощью системного свойства ro.vendor.apex.<apex name> , для определенного <apex name> активируется только APEX, соответствующий имени файла. APEX с <apex name> игнорируется (не активируется), если это системное свойство установлено в none . Эту функцию можно использовать для установки нескольких копий APEX с одинаковым именем. Если существует несколько версий одного и того же APEX, они должны использовать один и тот же ключ.

Примеры вариантов использования:

  • Установите 3 версии поставщика Wi-Fi HAL APEX: команды по контролю качества могут запустить ручное или автоматизированное тестирование, используя одну версию, затем перезагрузиться в другую версию и повторно запустить тесты, а затем сравнить окончательные результаты.
  • Установите 2 версии поставщика HAL камеры APEX, текущую и экспериментальную : участники тестирования могут использовать экспериментальную версию без загрузки и установки дополнительного файла, поэтому они могут легко переключиться обратно.

Во время загрузки apexd ищет системные свойства в определенном формате, чтобы активировать нужную версию APEX.

Ожидаемые форматы для ключа свойства:

  • Конфигурация загрузки
    • Используется для установки значения по умолчанию в BoardConfig.mk .
    • androidboot.vendor.apex.<apex name>
  • Постоянный sysprop
    • Используется для изменения значения по умолчанию, установленного на уже загруженном устройстве.
    • Переопределяет значение bootconfig, если оно присутствует.
    • persist.vendor.apex.<apex name>

Значением свойства должно быть имя файла APEX, который необходимо активировать, или none для отключения 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,
  ..
}

Версию по умолчанию также следует настроить с помощью bootconfig в 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

После загрузки устройства измените активированную версию, установив постоянный sysprop:

$ adb root;
$ adb shell setprop \
    persist.vendor.apex.com.oem.camera.hal \
    com.oem.camera.hal.my_apex_experimental;
$ adb reboot;

Если устройство поддерживает обновление bootconfig после прошивки (например, с помощью команд fastboot oem ), то изменение свойства bootconfig для многоустановленного APEX также изменяет версию, активируемую при загрузке.

Для виртуальных эталонных устройств на основе Cuttlefish можно использовать команду --extra_bootconfig_args для установки свойства bootconfig непосредственно во время запуска. Например:

launch_cvd --noresume \
  --extra_bootconfig_args "androidboot.vendor.apex.com.oem.camera.hal:=com.oem.camera.hal.my_apex_experimental";