Вы можете использовать формат файлов APEX для упаковки и установки модулей ОС Android более низкого уровня. Он позволяет независимо создавать и устанавливать такие компоненты, как встроенные сервисы и библиотеки, реализации HAL, встроенное ПО, файлы конфигурации и т. д.
APEX-файлы поставщика автоматически устанавливаются системой сборки в раздел /vendor и активируются во время выполнения с помощью apexd, как и APEX-файлы в других разделах.
Примеры использования
Модульность изображений поставщиков
APEX-файлы позволяют естественным образом объединять и разделять реализации функций в образах поставщиков.
Когда образы поставщиков создаются как комбинация независимо созданных APEX-файлов поставщиков, производители устройств могут легко выбирать конкретные реализации поставщиков, которые они хотят использовать на своих устройствах. Производители могут даже создать новый APEX поставщика, если ни один из предоставленных APEX не подходит для их нужд или если у них совершенно новое пользовательское оборудование.
Например, OEM может выбрать для своего устройства APEX-модуль Wi-Fi из AOSP, APEX-модуль Bluetooth из SoC и собственный APEX-модуль телефонии.
Без APEX-файлов поставщика реализация с таким количеством зависимостей между компонентами поставщика требует тщательной координации и отслеживания. Упаковав все компоненты (включая файлы конфигурации и дополнительные библиотеки) в APEX-файлы с четко определенными интерфейсами, можно в любой момент заменить один компонент другим.
Итерация разработчика
APEX-файлы поставщиков помогают разработчикам быстрее создавать модули поставщиков, поскольку в них можно объединять реализацию целых функций, например HAL Wi-Fi. После этого разработчики могут создавать и по отдельности отправлять пакеты 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 поставщика включают 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.
Например, пакет APEX com.android.foo может определять сервис с названием foo-service.
on foo-service /apex/com.android.foo/bin/foo
...
Будьте осторожны при использовании директив on. Поскольку скрипты инициализации в APEX-пакетах анализируются и выполняются после активации APEX-пакетов, некоторые события или свойства нельзя использовать. Используйте apex.all.ready=true, чтобы активировать действия как можно раньше.
Загрузочные 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
..
}
В каталоге <apex name>/etc/firmware APEX установлено prebuilt_firmware модулей. 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
Модули ядра должны быть установлены явным образом. В примере ниже показан скрипт инициализации в разделе поставщика, в котором установка выполняется с помощью 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-файлов поставщиков, и их количество постоянно увеличивается.
Примеры
- XML-файлы декларации функций
- XML-файлы функций датчиков как предварительно созданные в APEX поставщика HAL датчиков
- Входные файлы конфигурации
- Конфигурации сенсорного экрана в виде встроенных объектов в APEX-файле поставщика, содержащем только конфигурацию
Системные APEX-файлы
Некоторые сервисы 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/usrAPEX.ro.vulkan.apex=<apex name>– если задано, драйвер Vulkan загружается из APEX. Поскольку драйвер Vulkan используется ранними уровнями HAL, создайте загрузочный APEX и настройте видимость пространства имен компоновщика.
Задайте системные свойства в скриптах инициализации с помощью команды setprop.
Дополнительные функции
Выбор APEX при загрузке
Пример
APEX-пакеты поставщика можно активировать во время загрузки.
Если вы укажете название файла, используя системное свойство ro.vendor.apex.<apex name>, для определенного <apex name> будет активирован только APEX, соответствующий названию файла.
Если для этого системного свойства задано значение none, пакет APEX с <apex name> игнорируется (не активируется). Вы можете использовать эту функцию, чтобы установить несколько копий APEX с одним и тем же названием. Если у одного и того же пакета APEX несколько версий, у них должен быть один и тот же ключ.
Примеры использования
- Установите три версии APEX поставщика HAL Wi-Fi. Специалисты по контролю качества могут выполнить ручное или автоматическое тестирование с одной версией, затем перезагрузить устройство с другой версией, повторить тестирование и сравнить результаты.
- Установите две версии APEX поставщика HAL камеры:текущую и экспериментальную. Участники программы Dogfood смогут использовать экспериментальную версию без скачивания и установки дополнительного файла, а также легко возвращаться к текущей версии.
При загрузке apexd ищет sysprops в определенном формате, чтобы активировать нужную версию APEX.
Допустимые форматы ключа свойства:
- Bootconfig:
- Используется для установки значения по умолчанию в
BoardConfig.mk. androidboot.vendor.apex.<apex name>(экспортируется какro.boot.vendor.apex.<apex name>).
- Используется для установки значения по умолчанию в
- Динамическая конфигурация ранней загрузки (
init_dev_config):- Рекомендуемый механизм активации и отключения APEX-пакетов на уровне SKU (доступен в Android 17 и более поздних версий).
- Динамически задает значение
ro.boot.vendor.apex.<apex name>при загрузке до запускаapexd-bootstrap. Если задать для свойства значениеnone, активация APEX будет отключена. - Подробнее о конфигурации устройства ранней загрузки (
init_dev_config)…
- Постоянное системное свойство (устарело):
persist.vendor.apex.<apex name>- Функция для разработчиков. Поскольку
apexdактивирует предустановленные APEX-файлы до монтирования раздела/data, постоянные системные свойства, хранящиеся в/data, нельзя использовать для раннего выбора APEX-файлов в рабочих сборках.
Значением свойства должно быть имя файла 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 для выбора APEX
Чтобы указать, какой APEX использовать во время сборки, настройте версию по умолчанию с помощью 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
Если устройство поддерживает обновление 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";
Использовать init_dev_config для выбора APEX
Чтобы динамически выбирать или отключать APEX-файлы поставщика во время выполнения в Android 17 и более поздних версиях, реализуйте инструмент поставщика, выполняемый сервисом init_dev_config.
Инструмент запускается во время early-init перед apexd-bootstrap и задает свойства ro.boot.vendor.apex.* с помощью rustutils::system_properties.
Если задать в качестве значения свойства имя файла APEX, будет выбрана определенная версия для активации. Если задать значение none, можно явно отключить активацию APEX, например на SKU, где отсутствуют аппаратные периферийные устройства.
use rustutils::system_properties;
fn main() {
// Select an APEX version based on detected SKU, or disable it altogether:
let hal_apex = match sku_type {
1 => "com.oem.camera.hal.my_apex_default",
2 => "com.oem.camera.hal.my_apex_experimental",
_ => "none",
};
system_properties::write(
"ro.boot.vendor.apex.com.oem.camera.hal",
hal_apex,
).expect("Failed to set vendor APEX selection property");
}
Использовать постоянные системные свойства для разработки (устарело)
Для разработки и отладки после загрузки устройства вы можете изменить активированную версию, задав постоянное системное свойство:
$ adb root;
$ adb shell setprop \
persist.vendor.apex.com.oem.camera.hal \
com.oem.camera.hal.my_apex_experimental;
$ adb reboot;