В Android 8.1 и более поздних версий система сборки поддерживает VNDK. Когда поддержка VNDK включена, система сборки проверяет зависимости между модулями, создает вариант для модулей поставщика и автоматически устанавливает эти модули в назначенные каталоги.
Пример поддержки сборки VNDK
В этом примере определение модуля Android.bp задает библиотеку с названием libexample. Свойство vendor_available указывает, что модули фреймворка и модули поставщика могут зависеть от libexample:
Включена поддержка рисунка 1.
И исполняемый файл фреймворка /system/bin/foo, и исполняемый файл поставщика /vendor/bin/bar зависят от libexample и имеют libexample в своих свойствах shared_libs.
Если libexample используется как модулями фреймворка, так и модулями поставщика, создаются два варианта libexample. Основной вариант (названный в честь libexample) используется модулями фреймворка, а вариант поставщика (названный в честь libexample.vendor) – модулями поставщика. Обе версии устанавливаются в разные каталоги:
- Основной вариант установлен в папку
/system/lib[64]/libexample.so. - Вариант поставщика устанавливается в VNDK APEX, потому что для
vndk.enabledзадано значениеtrue.
Подробнее о модуле…
Настроить поддержку сборки
Чтобы включить полную поддержку системы сборки для устройства, добавьте BOARD_VNDK_VERSION в BoardConfig.mk:
BOARD_VNDK_VERSION := current
Эта настройка имеет глобальное действие: если она определена в BoardConfig.mk, проверяются все модули. Поскольку нет механизма, позволяющего добавить в черный или белый список проблемный модуль, перед добавлением BOARD_VNDK_VERSION необходимо удалить все ненужные зависимости. Вы можете протестировать и скомпилировать модуль, задав BOARD_VNDK_VERSION в переменных среды:
$ BOARD_VNDK_VERSION=current m module_name.vendor
Если параметр BOARD_VNDK_VERSION включен, несколько путей поиска заголовков по умолчанию удаляются. Вот некоторые из них:
frameworks/av/includeframeworks/native/includeframeworks/native/opengl/includehardware/libhardware/includehardware/libhardware_legacy/includehardware/ril/includelibnativehelper/includelibnativehelper/include_deprecatedsystem/core/includesystem/media/audio/include
Если модуль зависит от заголовков из этих каталогов, необходимо явно указать зависимости с помощью header_libs, static_libs и/или shared_libs.
VNDK APEX
В Android 10 и более ранних версиях модули с vndk.enabled устанавливались в /system/lib[64]/vndk[-sp]-${VER}. В Android 11 и более поздних версиях библиотеки VNDK упакованы в формат APEX, а название VNDK APEX – com.android.vndk.v${VER}. В зависимости от конфигурации устройства VNDK APEX может быть сжатым или несжатым и доступен по каноническому пути /apex/com.android.vndk.v${VER}.

Рисунок 2. VNDK APEX.
Определение модуля
Чтобы создать Android с помощью BOARD_VNDK_VERSION, необходимо изменить определение модуля в файле Android.mk или Android.bp. В этом разделе описаны различные типы определений модулей, несколько свойств модулей, связанных с VNDK, и проверки зависимостей, реализованные в системе сборки.
Модули поставщиков
Модули поставщика – это исполняемые файлы или общие библиотеки, которые необходимо установить в раздел поставщика. В файлах Android.bp модули поставщиков должны задавать для свойства vendor или proprietary значение true.
В файлах Android.mk модули поставщиков должны задавать для LOCAL_VENDOR_MODULE или LOCAL_PROPRIETARY_MODULE значение true.
Если определена переменная BOARD_VNDK_VERSION, система сборки запрещает зависимости между модулями поставщика и модулями фреймворка и выдает ошибки, если:
- модуль без
vendor:trueзависит от модуля сvendor:true; - модуль с
vendor:trueзависит от модуля, не являющегосяllndk_library, в котором нет ниvendor:true, ниvendor_available:true.
Проверка зависимостей применяется к приложениям "header_libs", "static_libs" и "shared_libs" в режиме "Android.bp", а также к приложениям "LOCAL_HEADER_LIBRARIES", "LOCAL_STATIC_LIBRARIES" и "LOCAL_SHARED_LIBRARIES" в режиме "Android.mk".
LL-NDK
Общие библиотеки LL-NDK – это библиотеки со стабильными ABI. В обоих модулях используется одна и та же последняя реализация. Для каждой общей библиотеки LL-NDK в файле cc_library есть свойство llndk с файлом символов:
cc_library { name: "libvndksupport", llndk: { symbol_file: "libvndksupport.map.txt", }, }
Файл символов содержит описание символов, видимых для модулей поставщика. Пример:
LIBVNDKSUPPORT { global: android_load_sphal_library; # llndk android_unload_sphal_library; # llndk local: *; };
На основе файла символов система сборки создает заглушку общей библиотеки для модулей поставщика, которые связываются с этими библиотеками, когда включен параметр BOARD_VNDK_VERSION. Символ включается в общую библиотеку заглушек, только если:
- не определено в конце раздела с помощью
_PRIVATEили_PLATFORM; - Нет тега
#platform-onlyи - Не содержит тегов
#introduce*или тег совпадает с целевым.
VNDK
В файлах Android.bp определения модулей cc_library, cc_library_static, cc_library_shared и cc_library_headers поддерживают три свойства, связанные с VNDK: vendor_available, vndk.enabled и vndk.support_system_process.
Если vendor_available или vndk.enabled < true, могут быть созданы два варианта (основной и поставщика). Основной вариант следует рассматривать как модуль фреймворка, а вариант поставщика – как модуль поставщика. Если какие-либо модули фреймворка зависят от этого модуля, будет создан основной вариант. Если некоторые модули поставщика зависят от этого модуля, создается вариант поставщика. Система сборки выполняет следующие проверки зависимостей:
- Основной вариант всегда содержит только фреймворк и недоступен для модулей поставщика.
- Вариант поставщика всегда недоступен для модулей фреймворка.
- Все зависимости варианта поставщика, указанные в
header_libs,static_libsи/илиshared_libs, должны быть либоllndk_library, либо модулем сvendor_availableилиvndk.enabled. - Если значение атрибута
vendor_available–true, вариант поставщика доступен всем модулям поставщика. - Если
vendor_available–false, вариант поставщика доступен только другим модулям VNDK или VNDK-SP (то есть модули сvendor:trueне могут связываться с модулямиvendor_available:false).
Путь установки по умолчанию для cc_library или cc_library_shared определяется по следующим правилам:
- Основной вариант устанавливается в
/system/lib[64]. - Путь установки варианта поставщика может быть разным:
- Если для
vndk.enabledзадано значениеfalse, вариант поставщика устанавливается в/vendor/lib[64]. - Если
vndk.enabled–true, вариант поставщика устанавливается в VNDK APEX(com.android.vndk.v${VER}).
- Если для
В таблице ниже показано, как система сборки обрабатывает варианты поставщика.
| vendor_available | vndk enabled |
vndk support_system_process |
Описание вариантов поставщика |
|---|---|---|---|
true |
false |
false |
Варианты поставщика имеют значение VND-ONLY. Общие библиотеки устанавливаются в /vendor/lib[64]. |
true |
Недействительный (ошибка сборки) | ||
true |
false |
Варианты поставщика – это VNDK. Общие библиотеки устанавливаются в VNDK APEX. | |
true |
Варианты поставщика – VNDK-SP. Общие библиотеки устанавливаются в VNDK APEX. | ||
|
|
|
Нет вариантов поставщиков. Этот модуль предназначен только для фреймворка. |
true |
Недействительный (ошибка сборки) | ||
true |
false |
Варианты поставщика – VNDK-Private. Общие библиотеки устанавливаются в VNDK APEX. Их нельзя использовать напрямую в модулях поставщика. | |
true |
Варианты поставщика – VNDK-SP-Private. Общие библиотеки устанавливаются в VNDK APEX. Их нельзя использовать напрямую в модулях поставщика. |
Расширения VNDK
Расширения VNDK – это общие библиотеки VNDK с дополнительными API. Расширения устанавливаются в /vendor/lib[64]/vndk[-sp] (без суффикса версии) и во время выполнения переопределяют исходные общие библиотеки VNDK.
Как определить расширения VNDK
В Android 9 и более поздних версиях Android.bp поддерживает расширения VNDK. Чтобы создать расширение VNDK, определите другой модуль со свойствами vendor:true и extends:
cc_library { name: "libvndk", vendor_available: true, vndk: { enabled: true, }, } cc_library { name: "libvndk_ext", vendor: true, vndk: { enabled: true, extends: "libvndk", }, }
Модуль со свойствами vendor:true, vndk.enabled:true и extends определяет расширение VNDK:
- Свойство
extendsдолжно содержать название базовой общей библиотеки VNDK (или общей библиотеки VNDK-SP). - Расширения VNDK (или VNDK-SP) называются так же, как базовые модули, которые они расширяют. Например, двоичный код для
libvndk_ext–libvndk.so, а неlibvndk_ext.so. - Расширения VNDK устанавливаются в
/vendor/lib[64]/vndk. - Расширения VNDK-SP устанавливаются в каталог
/vendor/lib[64]/vndk-sp. - В базовых общих библиотеках должны быть и
vndk.enabled:true, иvendor_available:true.
Расширение VNDK-SP должно быть создано на основе общей библиотеки VNDK-SP (значения vndk.support_system_process должны быть одинаковыми):
cc_library { name: "libvndk_sp", vendor_available: true, vndk: { enabled: true, support_system_process: true, }, } cc_library { name: "libvndk_sp_ext", vendor: true, vndk: { enabled: true, extends: "libvndk_sp", support_system_process: true, }, }
Расширения VNDK (или VNDK-SP) могут зависеть от других общих библиотек поставщика:
cc_library { name: "libvndk", vendor_available: true, vndk: { enabled: true, }, } cc_library { name: "libvndk_ext", vendor: true, vndk: { enabled: true, extends: "libvndk", }, shared_libs: [ "libvendor", ], } cc_library { name: "libvendor", vendor: true, }
Используйте расширения VNDK
Если модуль поставщика зависит от дополнительных API, определенных расширениями VNDK, модуль должен указать название расширения VNDK в своем свойстве shared_libs:
// A vendor shared library example cc_library { name: "libvendor", vendor: true, shared_libs: [ "libvndk_ext", ], } // A vendor executable example cc_binary { name: "vendor-example", vendor: true, shared_libs: [ "libvndk_ext", ], }
Если модуль поставщика зависит от расширений VNDK, эти расширения VNDK автоматически устанавливаются в /vendor/lib[64]/vndk[-sp]. Если модуль больше не зависит от расширения VNDK, добавьте в CleanSpec.mk шаг очистки, чтобы удалить общую библиотеку. Пример:
$(call add-clean-step, rm -rf $(TARGET_OUT_VENDOR)/lib/libvndk.so)
Условная компиляция
В этом разделе описывается, как работать с незначительными различиями (например, добавлением или удалением функции из одного из вариантов) между следующими тремя общими библиотеками VNDK:
- Основной вариант (например,
/system/lib[64]/libexample.so). - Вариант поставщика (например,
/apex/com.android.vndk.v${VER}/lib[64]/libexample.so) - Расширение VNDK (например,
/vendor/lib[64]/vndk[-sp]/libexample.so)
Условные флаги компилятора
Система сборки Android по умолчанию определяет __ANDROID_VNDK__ для вариантов поставщика и расширений VNDK. Вы можете защитить код с помощью директив препроцессора C:
void all() { }
#if !defined(__ANDROID_VNDK__)
void framework_only() { }
#endif
#if defined(__ANDROID_VNDK__)
void vndk_only() { }
#endif
В дополнение к __ANDROID_VNDK__ в Android.bp можно указать другие значения cflags или cppflags. Значение cflags или cppflags, указанное в target.vendor, относится к определенному варианту поставщика.
Например, следующий код Android.bp определяет параметры libexample и libexample_ext:
cc_library { name: "libexample", srcs: ["src/example.c"], vendor_available: true, vndk: { enabled: true, }, target: { vendor: { cflags: ["-DLIBEXAMPLE_ENABLE_VNDK=1"], }, }, } cc_library { name: "libexample_ext", srcs: ["src/example.c"], vendor: true, vndk: { enabled: true, extends: "libexample", }, cflags: [ "-DLIBEXAMPLE_ENABLE_VNDK=1", "-DLIBEXAMPLE_ENABLE_VNDK_EXT=1", ], }
Вот код src/example.c:
void all() { }
#if !defined(LIBEXAMPLE_ENABLE_VNDK)
void framework_only() { }
#endif
#if defined(LIBEXAMPLE_ENABLE_VNDK)
void vndk() { }
#endif
#if defined(LIBEXAMPLE_ENABLE_VNDK_EXT)
void vndk_ext() { }
#endifНа основе этих двух файлов система сборки генерирует общие библиотеки со следующими экспортированными символами:
| Путь установки | Экспортированные символы |
|---|---|
/system/lib[64]/libexample.so |
all, framework_only |
/apex/com.android.vndk.v${VER}/lib[64]/libexample.so |
all, vndk |
/vendor/lib[64]/vndk/libexample.so |
all, vndk, vndk_ext |
Требования к экспортируемым символам
Инструмент проверки ABI VNDK сравнивает ABI вариантов поставщиков VNDK и расширений VNDK с эталонными дампами ABI в prebuilts/abi-dumps/vndk.
- Символы, экспортируемые вариантами VNDK для поставщиков (например,
/apex/com.android.vndk.v${VER}/lib[64]/libexample.so), должны быть идентичны символам, определенным в дампах ABI (а не их надмножествами). - Символы, экспортируемые расширениями VNDK (например,
/vendor/lib[64]/vndk/libexample.so), должны быть супермножествами символов, определенных в дампах ABI.
Если варианты VNDK или расширения VNDK не соответствуют указанным выше требованиям, средство проверки ABI VNDK выдает ошибки сборки и останавливает ее.
Как исключить исходные файлы или общие библиотеки из вариантов поставщика
Чтобы исключить исходные файлы из варианта поставщика, добавьте их в свойство exclude_srcs. Чтобы общие библиотеки не были связаны с вариантом поставщика, добавьте их в свойство exclude_shared_libs. Пример:
cc_library { name: "libexample_cond_exclude", srcs: ["fwk.c", "both.c"], shared_libs: ["libfwk_only", "libboth"], vendor_available: true, target: { vendor: { exclude_srcs: ["fwk.c"], exclude_shared_libs: ["libfwk_only"], }, }, }
В этом примере основной вариант libexample_cond_exclude включает код из fwk.c и both.c и зависит от общих библиотек libfwk_only и libboth. Вариант libexample_cond_exclude для поставщика содержит только код из both.c, поскольку fwk.c исключен из-за свойства exclude_srcs. Аналогично, он зависит только от общей библиотеки libboth, поскольку библиотека libfwk_only исключена свойством exclude_shared_libs.
Экспорт заголовков из расширений VNDK
Расширение VNDK может добавлять новые классы или новые функции в общую библиотеку VNDK. Рекомендуется хранить эти декларации в отдельных заголовках и не изменять существующие заголовки.
Например, для расширения VNDK libexample_ext создается новый заголовочный файл include-ext/example/ext/feature_name.h:
- Android.bp
- include-ext/example/ext/feature_name.h
- include/example/example.h
- src/example.c
- src/ext/feature_name.c
В следующем примере кода Android.bp, libexample экспортирует только include, а libexample_ext – include и include-ext. Это гарантирует, что пользователи libexample не будут ошибочно включать feature_name.h:
cc_library { name: "libexample", srcs: ["src/example.c"], export_include_dirs: ["include"], vendor_available: true, vndk: { enabled: true, }, } cc_library { name: "libexample_ext", srcs: [ "src/example.c", "src/ext/feature_name.c", ], export_include_dirs: [ "include", "include-ext", ], vendor: true, vndk: { enabled: true, extends: "libexample", }, }
Если разделить расширения на независимые заголовочные файлы невозможно, можно добавить защиту #ifdef. Однако убедитесь, что все пользователи расширения VNDK добавляют флаги define. Вы можете определить cc_defaults, чтобы добавить флаги определения в cflags и связать общие библиотеки с shared_libs.
Например, чтобы добавить новую функцию-член Example2::get_b() в расширение VNDK libexample2_ext, нужно изменить существующий заголовочный файл и добавить защиту #ifdef:
#ifndef LIBEXAMPLE2_EXAMPLE_H_ #define LIBEXAMPLE2_EXAMPLE_H_ class Example2 { public: Example2(); void get_a(); #ifdef LIBEXAMPLE2_ENABLE_VNDK_EXT void get_b(); #endif private: void *impl_; }; #endif // LIBEXAMPLE2_EXAMPLE_H_
Для пользователей libexample2_ext определен объект cc_defaults с названием libexample2_ext_defaults:
cc_library { name: "libexample2", srcs: ["src/example2.cpp"], export_include_dirs: ["include"], vendor_available: true, vndk: { enabled: true, }, } cc_library { name: "libexample2_ext", srcs: ["src/example2.cpp"], export_include_dirs: ["include"], vendor: true, vndk: { enabled: true, extends: "libexample2", }, cflags: [ "-DLIBEXAMPLE2_ENABLE_VNDK_EXT=1", ], } cc_defaults { name: "libexample2_ext_defaults", shared_libs: [ "libexample2_ext", ], cflags: [ "-DLIBEXAMPLE2_ENABLE_VNDK_EXT=1", ], }
Пользователи libexample2_ext могут просто включить libexample2_ext_defaults в свойство defaults:
cc_binary {
name: "example2_user_executable",
defaults: ["libexample2_ext_defaults"],
vendor: true,
}Пакеты продуктов
В системе сборки Android переменная PRODUCT_PACKAGES указывает исполняемые файлы, общие библиотеки или пакеты, которые должны быть установлены на устройстве. Транзитивные зависимости указанных модулей также неявно устанавливаются на устройство.
Если BOARD_VNDK_VERSION включен, модули с vendor_available или vndk.enabled обрабатываются особым образом. Если модуль фреймворка зависит от модуля с vendor_available или vndk.enabled, то основной вариант включается в набор транзитивной установки. Если модуль поставщика зависит от модуля с vendor_available, вариант поставщика включается в транзитивный набор для установки. Однако варианты модулей поставщика с vndk.enabled устанавливаются независимо от того, используются ли они модулями поставщика.
Если зависимости не видны системе сборки (например, общие библиотеки, которые могут быть открыты с помощью dlopen() во время выполнения), вам следует указать названия модулей в PRODUCT_PACKAGES, чтобы установить эти модули явным образом.
Если в модуле есть vendor_available или vndk.enabled, название модуля обозначает его основной вариант. Чтобы явно указать вариант поставщика в PRODUCT_PACKAGES, добавьте суффикс .vendor к названию модуля. Пример:
cc_library { name: "libexample", srcs: ["example.c"], vendor_available: true, }
В этом примере libexample обозначает /system/lib[64]/libexample.so, а libexample.vendor – /vendor/lib[64]/libexample.so. Чтобы установить /vendor/lib[64]/libexample.so, добавьте libexample.vendor в PRODUCT_PACKAGES:
PRODUCT_PACKAGES += libexample.vendor