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

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

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

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

libexamplevendor_available:true и vndk.enabled:true

Рисунок 1. Поддержка VNDK включена

Как исполняемый файл платформы /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. ВНДК APEX

Определение модуля

Чтобы собрать Android с BOARD_VNDK_VERSION , вам необходимо пересмотреть определение модуля в Android.mk или Android.bp . В этом разделе описываются различные типы определений модулей, некоторые свойства модулей, связанные с VNDK, а также проверки зависимостей, реализованные в системе сборки.

Вендорские модули

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

  • Базовый вариант всегда предназначен только для платформы и недоступен для модулей поставщиков.
  • Вариант поставщика всегда недоступен для модулей платформы.
  • Все зависимости варианта поставщика, указанные в 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_same_process
Описания вариантов поставщиков
true false false Варианты поставщиков — VND-ONLY . Общие библиотеки устанавливаются в /vendor/lib[64] .
true Недействительно (ошибка сборки)
true false Варианты поставщика: VNDK . Общие библиотеки устанавливаются в VNDK APEX.
true Варианты поставщика: ВНДК-СП . Общие библиотеки устанавливаются в VNDK APEX.

false

false

false

Нет вариантов поставщиков. Этот модуль предназначен ТОЛЬКО для FWK .

true Недействительно (ошибка сборки)
true false Варианты поставщиков: VNDK-Private . Общие библиотеки устанавливаются в VNDK APEX. Они не должны напрямую использоваться модулями поставщика.
true Варианты поставщиков: VNDK-SP-Private . Общие библиотеки устанавливаются в VNDK APEX. Они не должны напрямую использоваться модулями поставщика.

Расширения ВНДК

Расширения 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/имя_функции.h
  • включить/пример/example.h
  • источник/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_

cc_defaults с именем libexample2_ext_defaults определен для пользователей libexample2_ext :

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