Compatibilidad con el sistema de compilación del VNDK

En Android 8.1 y versiones posteriores, el sistema de compilación tiene compatibilidad integrada con el VNDK. Cuando se habilita la compatibilidad con el VNDK, el sistema de compilación verifica las dependencias entre los módulos, compila una variante específica del proveedor para los módulos del proveedor y, luego, instala automáticamente esos módulos en los directorios designados.

Ejemplo de compatibilidad con la compilación del VNDK

En este ejemplo, la definición del módulo Android.bp define una biblioteca llamada libexample. La propiedad vendor_available indica que los módulos del framework y los módulos del proveedor pueden depender de libexample:

libexample vendor_available:true y vndk.enabled:true

Se habilitó la compatibilidad con la figura 1.

Tanto el ejecutable del framework /system/bin/foo como el ejecutable del proveedor /vendor/bin/bar dependen de libexample y tienen libexample en sus propiedades shared_libs.

Si libexample se usa en los módulos del framework y en los módulos del proveedor, se compilan dos variantes de libexample. La variante principal (llamada libexample) se usa en los módulos del framework, y la variante del proveedor (llamada libexample.vendor) se usa en los módulos del proveedor. Las dos variantes se instalan en directorios diferentes:

  • La variante principal se instala en /system/lib[64]/libexample.so.
  • La variante del proveedor se instala en el APEX de VNDK porque vndk.enabled es true.

Para obtener más detalles, consulta Definición del módulo.

Configura la compatibilidad con la compilación

Para habilitar la compatibilidad total del sistema de compilación para un dispositivo de producto, agrega BOARD_VNDK_VERSION a BoardConfig.mk:

BOARD_VNDK_VERSION := current

Este parámetro de configuración tiene un efecto global: Cuando se define en BoardConfig.mk, se verifican todos los módulos. Como no hay ningún mecanismo para incluir o excluir de la lista negra un módulo infractor, debes limpiar todas las dependencias innecesarias antes de agregar BOARD_VNDK_VERSION. Puedes probar y compilar un módulo configurando BOARD_VNDK_VERSION en tus variables de entorno:

$ BOARD_VNDK_VERSION=current m module_name.vendor

Cuando se habilita BOARD_VNDK_VERSION, se quitan varias rutas de búsqueda de encabezado global predeterminadas. Por ejemplo:

  • 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

Si un módulo depende de los encabezados de estos directorios, debes especificar (de forma explícita) las dependencias con header_libs, static_libs o shared_libs.

APEX de VNDK

En Android 10 y versiones anteriores, los módulos con vndk.enabled se instalaban en /system/lib[64]/vndk[-sp]-${VER}. En Android 11 y versiones posteriores, las bibliotecas del VNDK se empaquetan en formato APEX, y el nombre del APEX del VNDK es com.android.vndk.v${VER}. Según la configuración del dispositivo, el APEX del VNDK se aplana o no se aplana y está disponible en la ruta canónica /apex/com.android.vndk.v${VER}.

APEX de VNDK

Figura 2: APEX de VNDK.

Definición del módulo

Para compilar Android con BOARD_VNDK_VERSION, debes revisar la definición del módulo en Android.mk o Android.bp. En esta sección, se describen diferentes tipos de definiciones de módulos, varias propiedades de módulos relacionadas con el VNDK y las verificaciones de dependencias implementadas en el sistema de compilación.

Módulos de proveedores

Los módulos del proveedor son bibliotecas compartidas o ejecutables específicos del proveedor que se deben instalar en una partición del proveedor. En los archivos Android.bp, los módulos del proveedor deben establecer la propiedad del proveedor o la propiedad exclusiva en true. En los archivos Android.mk, los módulos del proveedor deben establecer LOCAL_VENDOR_MODULE o LOCAL_PROPRIETARY_MODULE en true.

Si se define BOARD_VNDK_VERSION, el sistema de compilación no permite dependencias entre los módulos del proveedor y los módulos del framework, y emite errores en los siguientes casos:

  • Un módulo sin vendor:true depende de un módulo con vendor:true.
  • Un módulo con vendor:true depende de un módulo que no es llndk_library y que no tiene ni vendor:true ni vendor_available:true.

La verificación de dependencias se aplica a header_libs, static_libs y shared_libs en Android.bp, y a LOCAL_HEADER_LIBRARIES, LOCAL_STATIC_LIBRARIES y LOCAL_SHARED_LIBRARIES en Android.mk.

LL-NDK

Las bibliotecas compartidas del LL-NDK son bibliotecas compartidas con ABIs estables. Tanto los módulos del framework como los del proveedor comparten la misma implementación más reciente. Para cada biblioteca compartida del LL-NDK, el cc_library contiene una propiedad llndk con un archivo de símbolos:

cc_library {
    name: "libvndksupport",
    llndk: {
        symbol_file: "libvndksupport.map.txt",
    },
}

El archivo de símbolos describe los símbolos visibles para los módulos del proveedor. Por ejemplo:

LIBVNDKSUPPORT {
  global:
    android_load_sphal_library; # llndk
    android_unload_sphal_library; # llndk
  local:
    *;
};

Según el archivo de símbolos, el sistema de compilación genera una biblioteca compartida de código auxiliar para los módulos del proveedor, que se vinculan con estas bibliotecas cuando BOARD_VNDK_VERSION está habilitado. Un símbolo se incluye en la biblioteca compartida de código auxiliar solo si cumple con las siguientes condiciones:

  • No está definido en el final de la sección con _PRIVATE o _PLATFORM.
  • No tiene la etiqueta #platform-only.
  • No tiene etiquetas #introduce* o la etiqueta coincide con el objetivo.

VNDK

En los archivos Android.bp, las definiciones de los módulos cc_library, cc_library_static, cc_library_shared y cc_library_headers admiten tres propiedades relacionadas con el VNDK: vendor_available, vndk.enabled y vndk.support_system_process.

Si vendor_available o vndk.enabled es true, se pueden compilar dos variantes (core y vendor). La variante principal debe tratarse como un módulo de framework, y la variante del proveedor, como un módulo del proveedor. Si algunos módulos del framework dependen de este módulo, se compila la variante principal. Si algunos módulos del proveedor dependen de este módulo, se compila la variante del proveedor. El sistema de compilación aplica las siguientes verificaciones de dependencias:

  • La variante principal siempre es solo del framework y es inaccesible para los módulos del proveedor.
  • La variante del proveedor siempre es inaccesible para los módulos del framework.
  • Todas las dependencias de la variante del proveedor, que se especifican en header_libs, static_libs o shared_libs, deben ser un llndk_library o un módulo con vendor_available o vndk.enabled.
  • Si vendor_available es true, la variante del proveedor es accesible para todos los módulos del proveedor.
  • Si vendor_available es false, solo se puede acceder a la variante del proveedor desde otros módulos de VNDK o VNDK-SP (es decir, los módulos con vendor:true no pueden vincular módulos de vendor_available:false).

La ruta de instalación predeterminada para cc_library o cc_library_shared se determina según las siguientes reglas:

  • La variante principal se instala en /system/lib[64].
  • La ruta de instalación de la variante del proveedor puede variar:
    • Si vndk.enabled es false, la variante del proveedor se instala en /vendor/lib[64].
    • Si vndk.enabled es true, la variante del proveedor se instala en el APEX de VNDK(com.android.vndk.v${VER}).

En la siguiente tabla, se resume cómo el sistema de compilación controla las variantes del proveedor:

vendor_available vndk
enabled
vndk
support_system_process
Descripciones de variantes de proveedores
true false false Las variantes del proveedor son VND-ONLY. Las bibliotecas compartidas se instalan en /vendor/lib[64].
true No válido (error de compilación)
true false Las variantes del proveedor son VNDK. Las bibliotecas compartidas se instalan en el APEX del VNDK.
true Las variantes del proveedor son VNDK-SP. Las bibliotecas compartidas se instalan en el APEX de VNDK.

false

false

false

No hay variantes de proveedores. Este módulo es SOLO PARA FWK.

true No válido (error de compilación)
true false Las variantes del proveedor son VNDK-Private. Las bibliotecas compartidas se instalan en el APEX del VNDK. Los módulos del proveedor no deben usarlos directamente.
true Las variantes del proveedor son VNDK-SP-Private. Las bibliotecas compartidas se instalan en el APEX del VNDK. Los módulos del proveedor no deben usarlos directamente.

Extensiones del VNDK

Las extensiones del VNDK son bibliotecas compartidas del VNDK con APIs adicionales. Las extensiones se instalan en /vendor/lib[64]/vndk[-sp] (sin sufijo de versión) y anulan las bibliotecas compartidas originales del VNDK en el tiempo de ejecución.

Cómo definir extensiones del VNDK

En Android 9 y versiones posteriores, Android.bp admite de forma nativa las extensiones de VNDK. Para compilar una extensión del VNDK, define otro módulo con una propiedad vendor:true y una propiedad extends:

cc_library {
    name: "libvndk",
    vendor_available: true,
    vndk: {
        enabled: true,
    },
}

cc_library {
    name: "libvndk_ext",
    vendor: true,
    vndk: {
        enabled: true,
        extends: "libvndk",
    },
}

Un módulo con propiedades vendor:true, vndk.enabled:true y extends define la extensión del VNDK:

  • La propiedad extends debe especificar un nombre de biblioteca compartida base del VNDK (o un nombre de biblioteca compartida del VNDK-SP).
  • Las extensiones del VNDK (o extensiones del VNDK-SP) se denominan según los nombres de los módulos base a partir de los cuales se extienden. Por ejemplo, el archivo binario de salida de libvndk_ext es libvndk.so en lugar de libvndk_ext.so.
  • Las extensiones del VNDK se instalan en /vendor/lib[64]/vndk.
  • Las extensiones de VNDK-SP se instalan en /vendor/lib[64]/vndk-sp.
  • Las bibliotecas compartidas base deben tener vndk.enabled:true y vendor_available:true.

Una extensión de VNDK-SP debe extenderse desde una biblioteca compartida de VNDK-SP (vndk.support_system_process debe ser igual):

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

Las extensiones del VNDK (o extensiones del VNDK-SP) pueden depender de otras bibliotecas compartidas del proveedor:

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

Cómo usar las extensiones del VNDK

Si un módulo del proveedor depende de APIs adicionales definidas por extensiones del VNDK, el módulo debe especificar el nombre de la extensión del VNDK en su propiedad 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",
    ],
}

Si un módulo del proveedor depende de extensiones del VNDK, esas extensiones se instalan automáticamente en /vendor/lib[64]/vndk[-sp]. Si un módulo ya no depende de una extensión del VNDK, agrega un paso de limpieza a CleanSpec.mk para quitar la biblioteca compartida. Por ejemplo:

$(call add-clean-step, rm -rf $(TARGET_OUT_VENDOR)/lib/libvndk.so)

Compilación condicional

En esta sección, se describe cómo abordar las diferencias sutiles (p.ej., agregar o quitar una función de una de las variantes) entre las siguientes tres bibliotecas compartidas del VNDK:

  • Variante principal (p.ej., /system/lib[64]/libexample.so)
  • Variante del proveedor (p.ej., /apex/com.android.vndk.v${VER}/lib[64]/libexample.so)
  • Extensión del VNDK (p.ej., /vendor/lib[64]/vndk[-sp]/libexample.so)

Marcas de compilación condicionales

El sistema de compilación de Android define __ANDROID_VNDK__ para las variantes del proveedor y las extensiones del VNDK de forma predeterminada. Puedes proteger el código con las protecciones del preprocesador de C:

void all() { }

#if !defined(__ANDROID_VNDK__)
void framework_only() { }
#endif

#if defined(__ANDROID_VNDK__)
void vndk_only() { }
#endif

Además de __ANDROID_VNDK__, se pueden especificar diferentes cflags o cppflags en Android.bp. El cflags o cppflags especificado en target.vendor es específico de la variante del proveedor.

Por ejemplo, el siguiente Android.bp define libexample y 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",
    ],
}

Y este es el listado de código de 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

Según estos dos archivos, el sistema de compilación genera bibliotecas compartidas con los siguientes símbolos exportados:

Ruta de instalación Símbolos exportados
/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

Requisitos sobre los símbolos exportados

El verificador de ABI del VNDK compara la ABI de las variantes del VNDK del proveedor y las extensiones del VNDK con los volcados de ABI de referencia en prebuilts/abi-dumps/vndk.

  • Los símbolos exportados por las variantes del proveedor del VNDK (p.ej., /apex/com.android.vndk.v${VER}/lib[64]/libexample.so) deben ser idénticos a los símbolos definidos en los volcados de ABI (no deben ser superconjuntos de estos).
  • Los símbolos exportados por las extensiones del VNDK (p.ej., /vendor/lib[64]/vndk/libexample.so) deben ser superconjuntos de los símbolos definidos en los volcados de ABI.

Si las variantes del proveedor del VNDK o las extensiones del VNDK no cumplen con los requisitos anteriores, el verificador de ABI del VNDK emite errores de compilación y detiene la compilación.

Excluye archivos fuente o bibliotecas compartidas de las variantes del proveedor

Para excluir archivos fuente de la variante del proveedor, agrégalos a la propiedad exclude_srcs. Del mismo modo, para garantizar que las bibliotecas compartidas no se vinculen con la variante del proveedor, agrega esas bibliotecas a la propiedad exclude_shared_libs. Por ejemplo:

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"],
        },
    },
}

En este ejemplo, la variante principal de libexample_cond_exclude incluye el código de fwk.c y both.c, y depende de las bibliotecas compartidas libfwk_only y libboth. La variante del proveedor de libexample_cond_exclude solo incluye el código de both.c porque la propiedad fwk.c excluye exclude_srcs. Del mismo modo, solo depende de la biblioteca compartida libboth porque la propiedad exclude_shared_libs excluye libfwk_only.

Cómo exportar encabezados desde extensiones del VNDK

Una extensión del VNDK puede agregar clases o funciones nuevas a una biblioteca compartida del VNDK. Se sugiere mantener esas declaraciones en encabezados independientes y evitar cambiar los encabezados existentes.

Por ejemplo, se crea un nuevo archivo de encabezado include-ext/example/ext/feature_name.h para la extensión libexample_ext del VNDK:

  • Android.bp
  • include-ext/example/ext/feature_name.h
  • include/example/example.h
  • src/example.c
  • src/ext/feature_name.c

En el siguiente Android.bp, libexample solo exporta include, mientras que libexample_ext exporta include y include-ext. Esto garantiza que los usuarios de libexample no incluirán feature_name.h de forma incorrecta:

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",
    },
}

Si no es posible separar las extensiones en archivos de encabezado independientes, una alternativa es agregar protecciones #ifdef. Sin embargo, asegúrate de que todos los usuarios de la extensión del VNDK agreguen las marcas de definición. Puedes definir cc_defaults para agregar marcas de definición a cflags y vincular bibliotecas compartidas con shared_libs.

Por ejemplo, para agregar una nueva función miembro Example2::get_b() a la extensión del VNDK libexample2_ext, debes modificar el archivo de encabezado existente y agregar un elemento de protección #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_

Se define un cc_defaults llamado libexample2_ext_defaults para los usuarios de 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",
    ],
}

Los usuarios de libexample2_ext pueden simplemente incluir libexample2_ext_defaults en su propiedad defaults:

cc_binary {
    name: "example2_user_executable",
    defaults: ["libexample2_ext_defaults"],
    vendor: true,
}

Paquetes de productos

En el sistema de compilación de Android, la variable PRODUCT_PACKAGES especifica los ejecutables, las bibliotecas compartidas o los paquetes que se deben instalar en el dispositivo. Las dependencias transitivas de los módulos especificados también se instalan de forma implícita en el dispositivo.

Si BOARD_VNDK_VERSION está habilitado, los módulos con vendor_available o vndk.enabled reciben un tratamiento especial. Si un módulo de framework depende de un módulo con vendor_available o vndk.enabled, la variante principal se incluye en el conjunto de instalación transitiva. Si un módulo del proveedor depende de un módulo con vendor_available, la variante del proveedor se incluye en el conjunto de instalación transitivo. Sin embargo, las variantes de proveedores de módulos con vndk.enabled se instalan independientemente de si los módulos de proveedores las usan o no.

Cuando las dependencias son invisibles para el sistema de compilación (p.ej., bibliotecas compartidas que se pueden abrir con dlopen() en el tiempo de ejecución), debes especificar los nombres de los módulos en PRODUCT_PACKAGES para instalar esos módulos de forma explícita.

Si un módulo tiene vendor_available o vndk.enabled, el nombre del módulo representa su variante principal. Para especificar de forma explícita la variante del proveedor en PRODUCT_PACKAGES, agrega un sufijo .vendor al nombre del módulo. Por ejemplo:

cc_library {
    name: "libexample",
    srcs: ["example.c"],
    vendor_available: true,
}

En este ejemplo, libexample significa /system/lib[64]/libexample.so y libexample.vendor significa /vendor/lib[64]/libexample.so. Para instalar /vendor/lib[64]/libexample.so, agrega libexample.vendor a PRODUCT_PACKAGES:

PRODUCT_PACKAGES += libexample.vendor