В 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/include -
frameworks/native/include -
frameworks/native/opengl/include -
hardware/libhardware/include -
hardware/libhardware_legacy/include -
hardware/ril/include -
libnativehelper/include -
libnativehelper/include_deprecated -
system/core/include -
system/media/audio/include
Если модуль зависит от заголовочных файлов из этих каталогов, необходимо явно указать зависимости с помощью header_libs , static_libs и/или shared_libs .
ВНДК АПЕКС
В 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 — это разделяемые библиотеки со стабильными 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*, или тег соответствует целевому объекту.
ВНДК
В файлах 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 , могут быть собраны два варианта ( core и vendor ). Вариант core следует рассматривать как модуль фреймворка, а вариант vendor — как модуль стороннего разработчика. Если от этого модуля зависят некоторые модули фреймворка, собирается вариант core. Если от этого модуля зависят некоторые модули стороннего разработчика, собирается вариант vendor. Система сборки обеспечивает следующие проверки зависимостей:
- Базовый вариант всегда является исключительно фреймворком и недоступен для модулей сторонних разработчиков.
- Вариант от стороннего поставщика всегда недоступен для модулей фреймворка.
- Все зависимости варианта vendor, указанные в
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}).
- Если
В таблице ниже приведено краткое описание того, как система сборки обрабатывает варианты от разных поставщиков:
| доступный поставщик | вндк включено | вндк support_system_process | Описание вариантов поставщика |
|---|---|---|---|
true | false | false | Варианты от поставщика — ТОЛЬКО VND . Разделяемые библиотеки установлены в /vendor/lib[64] . |
true | Неверный (ошибка сборки) | ||
true | false | Используемые варианты от поставщика — VNDK . Разделяемые библиотеки устанавливаются в VNDK APEX. | |
true | Варианты от поставщика — VNDK-SP . Разделяемые библиотеки устанавливаются в VNDK APEX. | ||
| | | Вариантов от разных производителей нет. Этот модуль предназначен ТОЛЬКО для FWK . |
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 . Это гарантирует, что feature_name.h не будет некорректно включен пользователями libexample :
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 добавляют флаги определения. Вы можете определить cc_defaults , чтобы добавить флаги определения в cflags и связать разделяемые библиотеки с shared_libs .
Например, чтобы добавить новую функцию-член Example2::get_b() в расширение VNDK libexample2_ext , необходимо изменить существующий заголовочный файл и добавить директиву #ifdef guard:
#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