Поддержка системы сборки VNDK

В Android 8.1 и более поздних версий система сборки поддерживает VNDK. Когда поддержка VNDK включена, система сборки проверяет зависимости между модулями, создает вариант для модулей поставщика и автоматически устанавливает эти модули в назначенные каталоги.

Пример поддержки сборки VNDK

В этом примере определение модуля Android.bp задает библиотеку с названием libexample. Свойство vendor_available указывает, что модули фреймворка и модули поставщика могут зависеть от libexample:

libexample vendor_available:true и vndk.enabled:true

Включена поддержка рисунка 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.

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

VNDK APEX

Рисунок 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.

false

false

false

Нет вариантов поставщиков. Этот модуль предназначен только для фреймворка.

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