Suporte ao sistema de build do VNDK

No Android 8.1 e versões mais recentes, o sistema de build tem suporte integrado ao VNDK. Quando o suporte ao VNDK está ativado, o sistema de build verifica as dependências entre módulos, cria uma variante específica do fornecedor para módulos do fornecedor e instala esses módulos automaticamente em diretórios designados.

Exemplo de suporte ao build do VNDK

Neste exemplo, a definição do módulo Android.bp define uma biblioteca chamada libexample. A propriedade vendor_available indica que os módulos do framework e do fornecedor podem depender de libexample:

libexample vendor_available:true e vndk.enabled:true

Figura 1.

O executável /system/bin/foo do framework e o executável /vendor/bin/bar do fornecedor dependem de libexample e têm libexample nas propriedades shared_libs.

Se libexample for usado por módulos de framework e módulos de fornecedores, duas variantes de libexample serão criadas. A variante principal (nomeada libexample) é usada por módulos de framework, e a variante do fornecedor (nomeada libexample.vendor) é usada por módulos do fornecedor. As duas variantes são instaladas em diretórios diferentes:

  • A variante principal é instalada em /system/lib[64]/libexample.so.
  • A variante do fornecedor é instalada no VNDK APEX porque vndk.enabled é true.

Para mais detalhes, consulte Definição do módulo.

Configurar o suporte ao build

Para ativar o suporte total do sistema de build para um dispositivo de produto, adicione BOARD_VNDK_VERSION a BoardConfig.mk:

BOARD_VNDK_VERSION := current

Essa configuração tem um efeito global: quando definida em BoardConfig.mk, todos os módulos são verificados. Como não existe um mecanismo para colocar um módulo infrator na lista de proibições ou na lista de permissões, limpe todas as dependências desnecessárias antes de adicionar BOARD_VNDK_VERSION. É possível testar e compilar um módulo definindo BOARD_VNDK_VERSION nas variáveis de ambiente:

$ BOARD_VNDK_VERSION=current m module_name.vendor

Quando BOARD_VNDK_VERSION está ativado, vários caminhos de pesquisa de cabeçalho globais padrão são removidos. São eles:

  • 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

Se um módulo depender dos cabeçalhos desses diretórios, será necessário especificar (explicitamente) as dependências com header_libs, static_libs e/ou shared_libs.

APEX do VNDK

No Android 10 e versões anteriores, os módulos com vndk.enabled eram instalados em /system/lib[64]/vndk[-sp]-${VER}. No Android 11 e versões mais recentes, as bibliotecas do VNDK são empacotadas em um formato APEX, e o nome do APEX do VNDK é com.android.vndk.v${VER}. Dependendo da configuração do dispositivo, o VNDK APEX é achatado ou desachatado e está disponível no caminho canônico /apex/com.android.vndk.v${VER}.

APEX do VNDK

Figura 2. APEX do VNDK.

Definição do módulo

Para criar o Android com BOARD_VNDK_VERSION, revise a definição do módulo em Android.mk ou Android.bp. Esta seção descreve diferentes tipos de definições de módulo, várias propriedades de módulo relacionadas ao VNDK e as verificações de dependência implementadas no sistema de build.

Módulos do fornecedor

Os módulos do fornecedor são executáveis específicos do fornecedor ou bibliotecas compartilhadas que precisam ser instalados em uma partição do fornecedor. Em arquivos Android.bp, os módulos do fornecedor precisam definir a propriedade do fornecedor ou proprietária como true. Em arquivos Android.mk, os módulos do fornecedor precisam definir LOCAL_VENDOR_MODULE ou LOCAL_PROPRIETARY_MODULE como true.

Se BOARD_VNDK_VERSION for definido, o sistema de build não permitirá dependências entre módulos do fornecedor e módulos do framework e emitirá erros se:

  • um módulo sem vendor:true depende de um módulo com vendor:true ou
  • um módulo com vendor:true depende de um módulo que não seja llndk_library e que não tenha vendor:true nem vendor_available:true.

A verificação de dependência se aplica a header_libs, static_libs e shared_libs em Android.bp e a LOCAL_HEADER_LIBRARIES, LOCAL_STATIC_LIBRARIES e LOCAL_SHARED_LIBRARIES em Android.mk.

LL-NDK

As bibliotecas compartilhadas do LL-NDK são bibliotecas compartilhadas com ABIs estáveis. O framework e os módulos do fornecedor compartilham a mesma implementação e a mais recente. Para cada biblioteca compartilhada do LL-NDK, o cc_library contém uma propriedade llndk com um arquivo de símbolo:

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

O arquivo de símbolo descreve os símbolos visíveis para os módulos do fornecedor. Exemplo:

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

Com base no arquivo de símbolo, o sistema de build gera uma biblioteca compartilhada de rascunho para módulos de fornecedores, que se vinculam a essas bibliotecas quando BOARD_VNDK_VERSION está ativado. Um símbolo só é incluído na biblioteca compartilhada stub se:

  • Não é definido no final da seção com _PRIVATE ou _PLATFORM,
  • Não tem a tag #platform-only.
  • Não tem tags #introduce* ou corresponde ao destino.

VNDK

Em arquivos Android.bp, as definições de módulo cc_library, cc_library_static, cc_library_shared e cc_library_headers oferecem suporte a três propriedades relacionadas ao VNDK: vendor_available, vndk.enabled e vndk.support_system_process.

Se vendor_available ou vndk.enabled for true, duas variantes (core e vendor) poderão ser criadas. A variante principal precisa ser tratada como um módulo de framework, e a variante do fornecedor precisa ser tratada como um módulo do fornecedor. Se alguns módulos do framework dependerem desse módulo, a variante principal será criada. Se alguns módulos do fornecedor dependerem desse módulo, a variante do fornecedor será criada. O sistema de build aplica as seguintes verificações de dependência:

  • A variante principal é sempre exclusiva do framework e inacessível para os módulos do fornecedor.
  • A variante do fornecedor é sempre inacessível aos módulos do framework.
  • Todas as dependências da variante do fornecedor, especificadas em header_libs, static_libs e/ou shared_libs, precisam ser um llndk_library ou um módulo com vendor_available ou vndk.enabled.
  • Se vendor_available for true, a variante do fornecedor será acessível para todos os módulos do fornecedor.
  • Se vendor_available for false, a variante do fornecedor só será acessível a outros módulos do VNDK ou do VNDK-SP (ou seja, módulos com vendor:true não podem vincular módulos vendor_available:false).

O caminho de instalação padrão para cc_library ou cc_library_shared é determinado pelas seguintes regras:

  • A variante principal está instalada em /system/lib[64].
  • O caminho de instalação da variante do fornecedor pode variar:
    • Se vndk.enabled for false, a variante do fornecedor será instalada em /vendor/lib[64].
    • Se vndk.enabled for true, a variante do fornecedor será instalada no VNDK APEX(com.android.vndk.v${VER}).

A tabela abaixo resume como o sistema de build lida com as variantes do fornecedor:

fornecedor_disponível vndk
ativado
vndk
support_same_process
Descrições das variantes do fornecedor
true false false As variantes do fornecedor são VND-ONLY. As bibliotecas compartilhadas são instaladas em /vendor/lib[64].
true Inválido (erro de build)
true false As variantes do fornecedor são VNDK. As bibliotecas compartilhadas são instaladas no APEX do VNDK.
true As variantes do fornecedor são VNDK-SP. As bibliotecas compartilhadas são instaladas no VNDK APEX.

false

false

false

Nenhuma variante do fornecedor. Este módulo é SOMENTE FWK.

true Inválido (erro de build)
true false As variantes do fornecedor são VNDK-Private. As bibliotecas compartilhadas são instaladas no VNDK APEX. Eles não podem ser usados diretamente por módulos do fornecedor.
true As variantes do fornecedor são VNDK-SP-Private. As bibliotecas compartilhadas são instaladas no VNDK APEX. Eles não podem ser usados diretamente por módulos do fornecedor.

Extensões do VNDK

As extensões do VNDK são bibliotecas compartilhadas do VNDK com APIs adicionais. As extensões são instaladas em /vendor/lib[64]/vndk[-sp] (sem o sufixo de versão) e substituem as bibliotecas compartilhadas originais do VNDK no momento da execução.

Definir extensões do VNDK

No Android 9 e versões mais recentes, o Android.bp oferece suporte nativo a extensões VNDK. Para criar uma extensão do VNDK, defina outro módulo com uma propriedade vendor:true e extends:

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

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

Um módulo com as propriedades vendor:true, vndk.enabled:true e extends define a extensão do VNDK:

  • A propriedade extends precisa especificar um nome de biblioteca compartilhada do VNDK de base (ou do VNDK-SP).
  • As extensões do VNDK (ou extensões do VNDK-SP) são nomeadas com base nos nomes do módulo de origem. Por exemplo, o binário de saída de libvndk_ext é libvndk.so em vez de libvndk_ext.so.
  • As extensões do VNDK são instaladas em /vendor/lib[64]/vndk.
  • As extensões do VNDK-SP são instaladas em /vendor/lib[64]/vndk-sp.
  • As bibliotecas compartilhadas de base precisam ter vndk.enabled:true e vendor_available:true.

Uma extensão VNDK-SP precisa ser estendida de uma biblioteca compartilhada VNDK-SP (vndk.support_system_process precisa 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,
    },
}

As extensões do VNDK (ou extensões do VNDK-SP) podem depender de outras bibliotecas compartilhadas do fornecedor:

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

Usar extensões VNDK

Se um módulo de fornecedor depender de outras APIs definidas pelas extensões do VNDK, o módulo precisará especificar o nome da extensão do VNDK na propriedade 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",
    ],
}

Se um módulo do fornecedor depender de extensões do VNDK, essas extensões serão instaladas no /vendor/lib[64]/vndk[-sp] automaticamente. Se um módulo não depender mais de uma extensão do VNDK, adicione uma etapa de limpeza ao CleanSpec.mk para remover a biblioteca compartilhada. Exemplo:

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

Compilação condicional

Esta seção descreve como lidar com as diferenças sutis (por exemplo, adicionar ou remover um recurso de uma das variantes) entre as três bibliotecas compartilhadas de VNDK a seguir:

  • Variante principal (por exemplo, /system/lib[64]/libexample.so)
  • Variante do fornecedor (por exemplo, /apex/com.android.vndk.v${VER}/lib[64]/libexample.so)
  • Extensão do VNDK (por exemplo, /vendor/lib[64]/vndk[-sp]/libexample.so)

Flags condicionais do compilador

O sistema de build do Android define __ANDROID_VNDK__ para variantes do fornecedor e extensões do VNDK por padrão. Você pode proteger o código com os protetores de pré-processador C:

void all() { }

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

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

Além de __ANDROID_VNDK__, diferentes cflags ou cppflags podem ser especificados em Android.bp. O cflags ou cppflags especificado em target.vendor é específico para a variante do fornecedor.

Por exemplo, o Android.bp a seguir define libexample e 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",
    ],
}

Esta é a lista de códigos 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

De acordo com esses dois arquivos, o sistema de build gera bibliotecas compartilhadas com os seguintes símbolos exportados:

Caminho de instalação 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 dos símbolos exportados

O verificador de ABI do VNDK compara a ABI das variantes do fornecedor do VNDK e das extensões do VNDK aos despejos de ABI de referência em prebuilts/abi-dumps/vndk.

  • Os símbolos exportados por variantes de fornecedor do VNDK (por exemplo, /apex/com.android.vndk.v${VER}/lib[64]/libexample.so) precisam ser idênticos aos símbolos definidos em despejos de ABI, e não aos superconjuntos.
  • Os símbolos exportados por extensões do VNDK (por exemplo, /vendor/lib[64]/vndk/libexample.so) precisam ser superconjuntos dos símbolos definidos em despejos de ABI.

Se as variantes do fornecedor do VNDK ou as extensões do VNDK não seguirem os requisitos acima, o verificador de ABI do VNDK emitirá erros de build e interromperá o build.

Excluir arquivos de origem ou bibliotecas compartilhadas das variantes do fornecedor

Para excluir arquivos de origem da variante do fornecedor, adicione-os à propriedade exclude_srcs. Da mesma forma, para garantir que as bibliotecas compartilhadas não sejam vinculadas à variante do fornecedor, adicione essas bibliotecas à propriedade exclude_shared_libs. Exemplo:

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

Neste exemplo, a variante principal de libexample_cond_exclude inclui o código de fwk.c e both.c e depende das bibliotecas compartilhadas libfwk_only e libboth. A variante do fornecedor de libexample_cond_exclude inclui apenas o código de both.c porque fwk.c é excluído pela propriedade exclude_srcs. Da mesma forma, ela depende apenas da biblioteca compartilhada libboth, porque libfwk_only é excluído pela propriedade exclude_shared_libs.

Exportar cabeçalhos de extensões VNDK

Uma extensão do VNDK pode adicionar novas classes ou funções a uma biblioteca compartilhada do VNDK. Sugerimos manter essas declarações em cabeçalhos independentes e evitar a alteração dos cabeçalhos atuais.

Por exemplo, um novo arquivo de cabeçalho include-ext/example/ext/feature_name.h é criado para a extensão libexample_ext do VNDK:

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

No Android.bp a seguir, libexample exporta apenas include, enquanto libexample_ext exporta include e include-ext. Isso garante que feature_name.h não seja incluído incorretamente pelos usuários de 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",
    },
}

Se não for possível separar as extensões para arquivos de cabeçalho independentes, uma alternativa é adicionar proteções #ifdef. No entanto, é preciso que todos os usuários da extensão VNDK adicionem as sinalizações de definição. Você pode definir cc_defaults para adicionar flags de definição a cflags e vincular bibliotecas compartilhadas com shared_libs.

Por exemplo, para adicionar uma nova função de membro Example2::get_b() à extensão libexample2_ext do VNDK, é necessário modificar o arquivo de cabeçalho existente e adicionar uma proteção #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_

Um cc_defaults chamado libexample2_ext_defaults é definido para os usuários 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",
    ],
}

Os usuários de libexample2_ext podem simplesmente incluir libexample2_ext_defaults na propriedade defaults:

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

Pacotes de produtos

No sistema de build do Android, a variável PRODUCT_PACKAGES especifica os executáveis, as bibliotecas compartilhadas ou os pacotes que precisam ser instalados no dispositivo. As dependências transitivas dos módulos especificados também são instaladas implicitamente no dispositivo.

Se BOARD_VNDK_VERSION estiver ativado, os módulos com vendor_available ou vndk.enabled receberão um tratamento especial. Se um módulo de framework depender de um módulo com vendor_available ou vndk.enabled, a variante principal será incluída no conjunto de instalação transitiva. Se um módulo de fornecedor depender de um módulo com vendor_available, a variante do fornecedor será incluída no conjunto de instalação transitiva. No entanto, as variantes do fornecedor de módulos com vndk.enabled são instaladas, mesmo que não sejam usadas por módulos do fornecedor.

Quando as dependências são invisíveis para o sistema de build (por exemplo, bibliotecas compartilhadas que podem ser abertas com dlopen() durante a execução), especifique os nomes dos módulos em PRODUCT_PACKAGES para instalar esses módulos explicitamente.

Se um módulo tiver vendor_available ou vndk.enabled, o nome do módulo representa a variante principal. Para especificar explicitamente a variante do fornecedor em PRODUCT_PACKAGES, anexe um sufixo .vendor ao nome do módulo. Exemplo:

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

Neste exemplo, libexample significa /system/lib[64]/libexample.so e libexample.vendor significa /vendor/lib[64]/libexample.so. Para instalar o /vendor/lib[64]/libexample.so, adicione libexample.vendor a PRODUCT_PACKAGES:

PRODUCT_PACKAGES += libexample.vendor