Fornecedor APEX

É possível usar o formato de arquivo APEX para empacotar e instalar módulos do SO Android de nível inferior. Ela permite criar e a instalação de componentes como bibliotecas e serviços nativos, implementações, firmware, arquivos de configuração etc.

Os APEXs do fornecedor são instalados automaticamente pelo sistema de build no /vendor e ativado em tempo de execução por apexd, assim como os APEXs em outros partições diferentes.

Casos de uso

Modularização de imagens do fornecedor

Os APEXes facilitam o agrupamento e a modularização naturais das implementações de recursos nas imagens do fornecedor.

Quando as imagens são criadas com a combinação de fornecedores independentes APEXs, os fabricantes de dispositivos podem escolher facilmente as implementações de fornecedores que queriam nos dispositivos deles. Os fabricantes podem até criar um novo APEX do fornecedor se nenhum dos APEXs fornecidos atender às necessidades deles ou se eles tiverem um hardware personalizado novo.

Por exemplo, um OEM pode escolher compor o dispositivo com o APEX de implementação de Wi-Fi do AOSP, o APEX de implementação de Bluetooth do SoC e um APEX de implementação de telefonia OEM personalizado.

Sem APEXs de fornecedor, uma implementação com tantas dependências entre os componentes do fornecedor requerem coordenação e rastreamento cuidadosos. Ao encapsular todos os elementos (incluindo arquivos de configuração e bibliotecas extras) em APEXs com interfaces claramente definidas em qualquer ponto da comunicação entre recursos, o componentes diferentes se tornam intercambiáveis.

Iteração do desenvolvedor

Os APEXes do fornecedor ajudam os desenvolvedores a iterar mais rapidamente ao desenvolver módulos do fornecedor, agrupando uma implementação de recurso inteira, como o HAL do Wi-Fi, dentro de um APEX do fornecedor. Os desenvolvedores podem criar e enviar individualmente o APEX do fornecedor para teste em vez de recriar toda a imagem do fornecedor.

Isso simplifica e acelera o ciclo de iteração dos desenvolvedores que trabalhem principalmente em uma área de recursos e queiram iterar apenas nesse recurso. área

O agrupamento natural de uma área de recurso em um APEX também simplifica o processo de criar, enviar e testar alterações para essa área de recursos. Por exemplo: a reinstalação de um APEX atualiza automaticamente todas as bibliotecas agrupadas ou arquivos de configuração que o APEX inclui.

O agrupamento de uma área de recurso em um APEX também simplifica a depuração ou reversão quando um comportamento incorreto do dispositivo é observado. Por exemplo, se a telefonia estiver funcionando mal em um novo build, os desenvolvedores poderão tentar instalar um APEX de implementação de telefonia mais antigo em um dispositivo (sem precisar atualizar um build completo) e verificar se o comportamento correto é restaurado.

Exemplo de fluxo de trabalho:

# Build the entire device and flash. OR, obtain an already-flashed device.
source build/envsetup.sh && lunch oem_device-userdebug
m
fastboot flashall -w

# Test the device.
... testing ...

# Check previous behavior using a vendor APEX from one week ago, downloaded from
# your continuous integration build.
... download command ...
adb install <path to downloaded APEX>
adb reboot
... testing ...

# Edit and rebuild just the APEX to change and test behavior.
... edit APEX source contents ...
m <apex module name>
adb install out/<path to built APEX>
adb reboot
... testing ...

Exemplos

Noções básicas

Consulte a página principal Formato de arquivo APEX para informações genéricas do APEX, incluindo requisitos do dispositivo, detalhes do formato de arquivo e etapas de instalação.

Em Android.bp, definir a propriedade vendor: true torna um módulo APEX um fornecedora APEX.

apex {
  ..
  vendor: true,
  ..
}

Binários e bibliotecas compartilhadas

Um APEX inclui dependências transitivas dentro do payload do APEX, a menos que tenha interfaces estáveis.

As interfaces nativas estáveis para dependências APEX do fornecedor incluem cc_library com Bibliotecas stubs e LLNDK. Essas dependências são excluídas da embalagem e são registradas no manifesto APEX. O manifesto é processada por linkerconfig para que as dependências nativas externas sejam disponível no ambiente de execução.

No snippet a seguir, o APEX contém o binário (my_service) e os dependências não estáveis (arquivos *.so).

apex {
  ..
  vendor: true,
  binaries: ["my_service"],
  ..
}

No snippet a seguir, o APEX contém a biblioteca compartilhada my_standalone_libe qualquer uma das dependências não estáveis (conforme descrito acima).

apex {
  ..
  vendor: true,
  native_shared_libs: ["my_standalone_lib"],
  ..
}

Tornar o APEX menor

O APEX pode ficar maior porque agrupa dependências não estáveis. Recomendamos usando vinculação estática. Bibliotecas comuns, como libc++.so e libbase.so, podem ser vinculadas de forma estática aos binários HAL. Criar uma dependência para fornecer uma interface estável pode ser outra opção. A dependência não será empacotada no APEX.

Implementações do HAL

Para definir uma implementação do HAL, forneça os binários e as bibliotecas correspondentes dentro de um APEX do fornecedor semelhante aos exemplos a seguir:

Para encapsular totalmente a implementação da HAL, o APEX também deve especificar quaisquer fragmentos VINTF e scripts init relevantes.

Fragmentos VINTF

Os fragmentos VINTF podem ser veiculados de um APEX do fornecedor quando estão localizados em etc/vintf do APEX.

Use a propriedade prebuilts para incorporar os fragmentos VINTF ao APEX.

apex {
  ..
  vendor: true,
  prebuilts: ["fragment.xml"],
  ..
}

prebuilt_etc {
  name: "fragment.xml",
  src: "fragment.xml",
  sub_dir: "vintf",
}

APIs de consulta

Quando fragmentos VINTF são adicionados ao APEX, use APIs libbinder_ndk para receber os mapeamentos de interfaces HAL e nomes APEX.

  • AServiceManager_isUpdatableViaApex("com.android.foo.IFoo/default") : true se a instância da HAL for definida em APEX.
  • AServiceManager_getUpdatableApexName("com.android.foo.IFoo/default", ...): pega o nome do APEX que define a instância do HAL.
  • AServiceManager_openDeclaredPassthroughHal("mapper", "instance", ...) : use-o para abrir uma HAL de passagem.

Scripts de inicialização

Os APEXs podem incluir scripts init de duas formas: (A) por meio de um arquivo de texto pré-criado na Payload APEX ou (B) um script init normal em /vendor/etc. É possível definir para o mesmo APEX.

Script de inicialização no APEX:

prebuilt_etc {
  name: "myinit.rc",
  src: "myinit.rc"
}

apex {
  ..
  vendor: true,
  prebuilts: ["myinit.rc"],
  ..
}

Os scripts de inicialização em APEXes de fornecedores podem ter definições de service e diretivas de on <property or event>.

A definição service precisa apontar um binário no mesmo APEX. Por exemplo, o com.android.foo APEX pode definir um serviço chamado foo-service.

on foo-service /apex/com.android.foo/bin/foo
  ...

Tenha cuidado ao usar diretivas on. Como os scripts de inicialização em APEXes são analisados e executados após a ativação dos APEXes, alguns eventos ou propriedades não podem ser usados. Use apex.all.ready=true para disparar ações o mais cedo possível. APEXs de inicialização podem usar on init, mas não on early-init.

Firmware

Exemplo:

Incorpore firmware em um APEX do fornecedor com o tipo de módulo prebuilt_firmware, conforme segue.

prebuilt_firmware {
  name: "my.bin",
  src: "path_to_prebuilt_firmware",
  vendor: true,
}

apex {
  ..
  vendor: true,
  prebuilts: ["my.bin"],  // installed inside APEX as /etc/firmware/my.bin
  ..
}

Os módulos prebuilt_firmware são instalados no diretório <apex name>/etc/firmware do APEX. O ueventd verifica os diretórios /apex/*/etc/firmware para encontrar módulos de firmware.

O file_contexts do APEX precisa rotular todas as entradas de payload de firmware corretamente para garantir que esses arquivos sejam acessíveis por ueventd no momento da execução; normalmente, o rótulo vendor_file é suficiente. Exemplo:

(/.*)? u:object_r:vendor_file:s0

Módulos do kernel

Incorpore módulos do kernel em um APEX de fornecedor como módulos pré-criados, como mostrado a seguir.

prebuilt_etc {
  name: "my.ko",
  src: "my.ko",
  vendor: true,
  sub_dir: "modules"
}

apex {
  ..
  vendor: true,
  prebuilts: ["my.ko"],  // installed inside APEX as /etc/modules/my.ko
  ..
}

O file_contexts do APEX precisa rotular todas as entradas de payload do módulo do kernel corretamente. Exemplo:

/etc/modules(/.*)? u:object_r:vendor_kernel_modules:s0

Os módulos do kernel precisam ser instalados explicitamente. O exemplo de script init abaixo na partição do fornecedor mostra a instalação via insmod:

my_init.rc:

on early-boot
  insmod /apex/myapex/etc/modules/my.ko
  ..

Sobreposições de recursos de tempo de execução

Exemplo:

Incorpore sobreposições de recursos de execução em um APEX do fornecedor usando a propriedade rros.

runtime_resource_overlay {
    name: "my_rro",
    soc_specific: true,
}


apex {
  ..
  vendor: true,
  rros: ["my_rro"],  // installed inside APEX as /overlay/my_rro.apk
  ..
}

Outros arquivos de configuração

Os APEXs de fornecedores oferecem suporte a vários outros arquivos de configuração normalmente encontrados no fornecedor. como pré-criados dentro de APEXs de fornecedores e muitos outros estão sendo adicionados.

Exemplos:

Inicializar APEXs de fornecedores

Alguns serviços da HAL, como keymint, devem estar disponíveis antes que os APEXs sejam ativado. Essas HALs geralmente definem early_hal na definição do serviço na classe script init. Outro exemplo é a classe animation, que geralmente é iniciada antes do evento post-fs-data. Quando esse serviço HAL inicial é empacotado no APEX do fornecedor, faça com que o apex "vendorBootstrap": true no manifesto APEX seja ativado mais cedo. Os APEXes de inicialização só podem ser ativados no local pré-criado, como /vendor/apex, e não em /data/apex.

Propriedades do sistema

Essas são as propriedades do sistema que o framework lê para oferecer suporte ao APEXs:

  • input_device.config_file.apex=<apex name>: quando definido, a entrada arquivos de configuração (*.idc, *.kl e *.kcm) são pesquisados em /etc/usr do APEX.
  • ro.vulkan.apex=<apex name>: quando definido, o driver Vulkan é carregado do APEX. Como o driver Vulkan é usado por HALs iniciais, torne o APEX Bootstrap APEX e configure esse namespace de linker para ficar visível.

Defina as propriedades do sistema em scripts de inicialização usando o comando setprop.

Recursos extras de desenvolvimento

Seleção do APEX na inicialização

Exemplo:

Os desenvolvedores também podem instalar várias versões de APEXes de fornecedores que compartilham o mesmo nome e chave APEX e, em seguida, escolher qual versão será ativada durante cada inicialização usando sysprops persistentes. Para alguns casos de uso de desenvolvedores, isso pode ser mais simples do que instalar uma nova cópia do APEX usando adb install.

Exemplos de casos de uso:

  • Instalar três versões da APEX, fornecedora de HAL de Wi-Fi:as equipes de controle de qualidade podem fazer a execução manual. ou automatizados usando uma versão, depois reinicializar para outra versão e executar os testes novamente e comparar os resultados finais.
  • Instale duas versões do APEX do fornecedor do HAL da câmera, atual e experimental. Os dogfooders podem usar a versão experimental sem fazer o download e instalar um arquivo extra, para que possam alternar facilmente.

Durante a inicialização, o apexd procura sysprops seguindo um formato específico para ativar a versão correta do APEX.

Os formatos esperados da chave de propriedade são:

  • Configuração de inicialização
    • Usado para definir o valor padrão em BoardConfig.mk.
    • androidboot.vendor.apex.<apex name>
  • sysprop persistente
    • Usado para mudar o valor padrão, definido em um dispositivo já inicializado.
    • Substitui o valor de bootconfig, se presente.
    • persist.vendor.apex.<apex name>

O valor da propriedade deve ser o nome do arquivo APEX, que deve ser ativado.

// Default version.
apex {
  name: "com.oem.camera.hal.my_apex_default",
  vendor: true,
  ..
}

// Non-default version.
apex {
  name: "com.oem.camera.hal.my_apex_experimental",
  vendor: true,
  ..
}

A versão padrão também deve ser configurada com o bootconfig BoardConfig.mk:

# Example for APEX "com.oem.camera.hal" with the default above:
BOARD_BOOTCONFIG += \
    androidboot.vendor.apex.com.oem.camera.hal=com.oem.camera.hal.my_apex_default

Após a inicialização do dispositivo, altere a versão ativada configurando o sysprop persistente:

$ adb root;
$ adb shell setprop \
    persist.vendor.apex.com.oem.camera.hal \
    com.oem.camera.hal.my_apex_experimental;
$ adb reboot;

Se o dispositivo for compatível com a atualização do bootconfig após a atualização (por exemplo, com os comandos fastboot oem), mudar a propriedade bootconfig dos multiinstalados O APEX também altera a versão ativada na inicialização.

Para dispositivos de referência virtual baseados no Cuttlefish, use o comando --extra_bootconfig_args para definir a propriedade bootconfig durante a inicialização. Exemplo:

launch_cvd --noresume \
  --extra_bootconfig_args "androidboot.vendor.apex.com.oem.camera.hal:=com.oem.camera.hal.my_apex_experimental";