Поддержка системы сборки 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 .

ВНДК АПЕКС

В 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 — как модуль vendor. Если некоторые модули фреймворка зависят от этого модуля, собирается вариант core. Если некоторые модули vendor зависят от этого модуля, собирается вариант vendor. Система сборки обеспечивает следующие проверки зависимостей:

  • Основной вариант всегда доступен только фреймворку и недоступен для модулей поставщика.
  • Вариант поставщика всегда недоступен для модулей фреймворка.
  • Все зависимости варианта поставщика, которые указаны в header_libs , static_libs и/или shared_libs , должны быть либо llndk_library , либо модулем с vendor_available или vndk.enabled .
  • Если vendor_available имеет true , вариант поставщика доступен всем модулям поставщика.
  • Если vendor_available равно false , то вариант vendor доступен только другим модулям 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} ).

В таблице ниже показано, как система сборки обрабатывает варианты поставщиков:

поставщик_доступен вндк
включено
вндк
поддержка_системного_процесса
Описания вариантов поставщиков
true false false Варианты поставщика доступны только для VND . Общие библиотеки устанавливаются в /vendor/lib[64] .
true Недействительно (ошибка сборки)
true false Варианты поставщика: VNDK . Общие библиотеки устанавливаются в VNDK APEX.
true Вариант поставщика — VNDK-SP . Общие библиотеки устанавливаются в VNDK APEX.

false

false

false

Вариантов поставщика нет. Этот модуль предназначен ТОЛЬКО для 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_extlibvndk.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 . Указанные в target.vendor cflags или cppflags зависят от версии поставщика.

Например, следующий 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 не соответствуют вышеуказанным требованиям, средство проверки VNDK ABI выдает ошибки сборки и останавливает сборку.

Исключить исходные файлы или общие библиотеки из вариантов поставщика

Чтобы исключить исходные файлы из варианта поставщика, добавьте их в свойство 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/имя_функции.h
  • включить/пример/пример.h
  • src/example.c
  • src/ext/имя_функции.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 :

#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