Você pode usar o formato de arquivo APEX para empacotar e instalar módulos do SO Android de nível inferior. Ele permite a construção e instalação independente de componentes como serviços e bibliotecas nativas, implementações de HAL, firmware, arquivos de configuração, etc.
Os APEXes do fornecedor são instalados pelo sistema de compilação automaticamente na partição /vendor
e ativados em tempo de execução pelo apexd
assim como os APEXes em outras partições.
Casos de uso
Modularização de imagens de fornecedores
Os APEXes facilitam um agrupamento natural e modularização de implementações de recursos em imagens de fornecedores.
Quando as imagens de fornecedores são criadas como uma combinação de APEXs de fornecedores criados de forma independente, os fabricantes de dispositivos podem escolher facilmente as implementações de fornecedores específicas desejadas em seus dispositivos. Os fabricantes podem até criar um novo APEX de fornecedor se nenhum dos APEX fornecidos atender às suas necessidades ou se eles tiverem um novo hardware personalizado.
Por exemplo, um OEM pode optar por compor seu dispositivo com o APEX de implementação wifi AOSP, o APEX de implementação de Bluetooth SoC e um APEX de implementação de telefonia OEM personalizado.
Sem APEXs de fornecedores, uma implementação com tantas dependências entre componentes de fornecedores requer coordenação e acompanhamento cuidadosos. Ao agrupar todos os componentes (incluindo arquivos de configuração e bibliotecas extras) em APEXes com interfaces claramente definidas em qualquer ponto de comunicação entre recursos, os diferentes componentes se tornam intercambiáveis.
Iteração do desenvolvedor
Os APEXs de fornecedores ajudam os desenvolvedores a iterar mais rapidamente enquanto desenvolvem módulos de fornecedores agrupando toda uma implementação de recurso, como o wifi HAL, dentro de um APEX de fornecedor. Os desenvolvedores podem então criar e enviar individualmente o APEX do fornecedor para testar as alterações, em vez de reconstruir toda a imagem do fornecedor.
Isso simplifica e acelera o ciclo de iteração do desenvolvedor para desenvolvedores que trabalham principalmente em uma área de recurso e desejam iterar apenas nessa área de recurso.
O agrupamento natural de uma área de recurso em um APEX também simplifica o processo de criação, envio e teste de alterações para essa área de recurso. Por exemplo, a reinstalação de um APEX atualiza automaticamente qualquer biblioteca ou arquivos de configuração incluídos no APEX.
Agrupar uma área de recurso em um APEX também simplifica a depuração ou a reversão quando um mau comportamento do dispositivo é observado. Por exemplo, se a telefonia estiver funcionando mal em uma nova compilação, os desenvolvedores podem tentar instalar um APEX de implementação de telefonia mais antigo em um dispositivo (sem precisar atualizar uma compilação completa) e verificar se o bom comportamento é restaurado.
Fluxo de trabalho de exemplo:
# 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
Fundamentos
Consulte a página principal do formato de arquivo do APEX para obter 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 APEX de fornecedor.
apex {
..
vendor: true,
..
}
Binários e bibliotecas compartilhadas
Um APEX inclui dependências transitivas dentro da carga útil do APEX, a menos que tenham interfaces estáveis.
As interfaces nativas estáveis para dependências do APEX do fornecedor incluem cc_library
com stubs
, ndk_library
ou llndk_library
. Essas dependências são excluídas do empacotamento e as dependências são registradas no manifesto do APEX. O manifesto é processado pelo linkerconfig
para que as dependências nativas externas estejam disponíveis em tempo de execução.
Ao contrário dos APEXes na partição /system
, os APEXs do fornecedor geralmente estão vinculados a uma versão específica do VNDK. As bibliotecas VNDK garantem a estabilidade da ABI na versão, portanto, podemos tratar as bibliotecas VNDK como estáveis e reduzir o tamanho dos APEXes de fornecedores excluindo-os dos APEXes usando a propriedade use_vndk_as_stable
.
No trecho abaixo, o APEX conterá o binário ( my_service
) e suas dependências não estáveis ( arquivos *.so
). Ele não conterá bibliotecas VNDK, mesmo quando my_service
for compilado com bibliotecas VNDK como libbase
. Em vez disso, em tempo de execução, my_service
usará libbase
das bibliotecas VNDK fornecidas pelo sistema.
apex {
..
vendor: true,
use_vndk_as_stable: true,
binaries: ["my_service"],
..
}
No trecho abaixo, o APEX conterá a biblioteca compartilhada my_standalone_lib
e qualquer uma de suas dependências não estáveis (conforme descrito acima).
apex {
..
vendor: true,
use_vndk_as_stable: true,
native_shared_libs: ["my_standalone_lib"],
..
}
Implementações HAL
Para definir uma implementação de HAL, forneça os binários e bibliotecas correspondentes dentro de um APEX de fornecedor semelhante aos exemplos a seguir:
Para encapsular totalmente a implementação HAL, o APEX também deve especificar quaisquer fragmentos VINTF relevantes e scripts de inicialização.
fragmentos VINTF
Os fragmentos VINTF são instalados em /vendor/etc/vintf
, em vez de contidos no APEX.
Use a propriedade padrão vintf_fragments
:
apex {
..
vendor: true,
vintf_fragments: ["fragment.xml"],
..
}
Scripts de inicialização
Os APEXes podem incluir scripts de inicialização de duas maneiras: (A) um arquivo de texto pré-construído dentro da carga útil do APEX ou (B) um script de inicialização regular em /vendor/etc
. Você pode definir ambos para o mesmo APEX.
Script de inicialização no APEX:
prebuilt_etc {
name: "myinit.rc",
src: "myinit.rc"
}
apex {
..
vendor: true,
prebuilts: ["myinit.rc"],
..
}
Scripts de inicialização dentro de APEXes só podem ter definições de service
, não on <property>
. Se o seu serviço precisar depender de algumas condições, você precisará instalar o script init em /vendor
.
Script de inicialização em /vendor
:
apex {
..
vendor: true,
init_rc: ["myinit.in.vendor.rc"],
..
}
Quaisquer referências ao caminho binário em um script de inicialização precisam refletir o caminho dentro do APEX. Você pode codificar o caminho no arquivo de origem do script init ou substituí-lo de um arquivo de origem existente usando um genrule .
Firmware
Exemplo:
Incorpore o firmware em um APEX de fornecedor com o tipo de módulo prebuilt_firmware
, conforme a seguir.
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. ueventd
verifica os diretórios /apex/*/etc/firmware
para encontrar módulos de firmware.
O file_contexts
do APEX deve rotular corretamente todas as entradas de carga útil de firmware para garantir que esses arquivos sejam acessíveis pelo ueventd
em tempo de execução; normalmente, o rótulo vendor_file
é suficiente. Por exemplo:
(/.*)? u:object_r:vendor_file:s0
Módulos do kernel
Incorpore módulos de kernel em um APEX de fornecedor como módulos pré-construídos, conforme 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 deve rotular corretamente todas as entradas de carga útil do módulo do kernel. Por exemplo:
/etc/modules(/.*)? u:object_r:vendor_kernel_modules:s0
Os módulos do kernel devem ser instalados explicitamente. O seguinte exemplo de script de inicialização 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 tempo de execução em um APEX de 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 do fornecedor suportam vários outros arquivos de configuração normalmente encontrados na partição do fornecedor como pré-compilados dentro dos APEXes do fornecedor, e mais estão sendo adicionados.
Exemplos:
- XMLs de declaração de recurso
- Os sensores apresentam XMLs como pré-construídos em um sensor HAL do fornecedor APEX
- Arquivos de configuração de entrada
- Configurações de tela sensível ao toque como pré-construídas em um APEX de fornecedor somente de configuração
Recursos extras de desenvolvimento
Seleção do APEX na inicialização
Exemplo:
Os desenvolvedores também podem instalar várias versões de APEXs de fornecedores que compartilham o mesmo nome e chave do APEX e, em seguida, escolher qual versão é ativada durante cada inicialização usando sysprops persistentes. Para determinados casos de uso do desenvolvedor, isso pode ser mais simples do que instalar uma nova cópia do APEX usando adb install
.
Exemplos de casos de uso:
- Instale 3 versões do APEX do fornecedor wifi HAL: as equipes de controle de qualidade podem executar testes manuais ou automatizados usando uma versão, reiniciar em outra versão e executar novamente os testes e comparar os resultados finais.
- Instale 2 versões da câmera HAL do fornecedor APEX, atual e experimental : Os dogfooders podem usar a versão experimental sem baixar e instalar um arquivo adicional, para que possam trocar facilmente.
Durante a inicialização, apexd
procura sysprops seguindo um formato específico para ativar a versão correta do APEX.
Os formatos esperados para a chave de propriedade são:
- Bootconfig
- Usado para definir o valor padrão, em
BoardConfig.mk
. -
androidboot.vendor.apex.<apex name>
- Usado para definir o valor padrão, em
- sysprop persistente
- Usado para alterar o valor padrão, definido em um dispositivo já inicializado.
- Substitui o valor bootconfig se estiver presente.
-
persist.vendor.apex.<apex name>
O valor da propriedade deve ser o nome do arquivo do APEX que deve ser ativado.
A única alteração nas definições do APEX Android.bp
é o campo multi_install_skip_symbol_files
, que deve ser true
para todas as definições de APEX multiinstaladas não padrão:
// Default version.
apex {
name: "com.oem.camera.hal.my_apex_default",
apex_name: "com.oem.camera.hal",
vendor: true,
..
}
// Non-default version.
apex {
name: "com.oem.camera.hal.my_apex_experimental",
apex_name: "com.oem.camera.hal",
vendor: true,
multi_install_skip_symbol_files: true,
..
}
A versão padrão também deve ser configurada usando bootconfig em 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
Depois que o dispositivo for inicializado, 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 suportar a atualização do bootconfig após a atualização (como por meio de comandos fastboot oem
), a alteração da propriedade bootconfig para o APEX multiinstalado também altera a versão ativada na inicialização.
Para dispositivos de referência virtual baseados em Cuttlefish , você pode usar o comando --extra_bootconfig_args
para definir a propriedade bootconfig diretamente durante a inicialização. Por exemplo:
launch_cvd --noresume \
--extra_bootconfig_args "androidboot.vendor.apex.com.oem.camera.hal:=com.oem.camera.hal.my_apex_experimental";