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

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

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

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

libexample vendor_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. АПЕКС ВНДК

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

Чтобы собрать 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 файл Android.bp содержит определение модуля llndk_library :

llndk_library {
    name: "libvndksupport",
    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 . Общие библиотеки устанавливаются в /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 с дополнительными 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

Требования к экспортируемым символам

Средство проверки VNDK ABI сравнивает 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. Рекомендуется хранить эти объявления в независимых заголовках и избегать изменения существующих заголовков.

Например, для расширения libexample_ext создается новый заголовочный файл include-ext/example/ext/feature_name.h :

  • Android.bp
  • include-ext/example/ext/feature_name.h
  • включить/пример/example.h
  • источник/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() в расширение 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