É possível usar o formato de arquivo APEX para empacotar e instalar módulos de nível inferior do SO Android. Ele permite a criação e instalação independentes de componentes como serviços e bibliotecas nativos, implementações de HAL, firmware, arquivos de configuração etc.
Os APEXs do fornecedor são instalados automaticamente pelo sistema de build na partição /vendor e ativados no tempo de execução pelo apexd, assim como os APEXs em outras partições.
Casos de uso
Modularização de imagens do fornecedor
Os APEXs facilitam o agrupamento e a modularização naturais das implementações de recursos em imagens do fornecedor.
Quando as imagens do fornecedor são criadas como uma combinação de APEXs do fornecedor criados de forma independente, os fabricantes de dispositivos podem escolher facilmente as implementações específicas do fornecedor que querem no dispositivo. Os fabricantes podem até mesmo criar um novo APEX do fornecedor se nenhum dos APEXs fornecidos atender às necessidades deles ou se eles tiverem um novo hardware personalizado.
Por exemplo, um OEM pode optar por 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 os APEXs do fornecedor, uma implementação com tantas dependências entre os componentes do fornecedor exige coordenação e acompanhamento cuidadosos. Ao agrupar todos os componentes (incluindo arquivos de configuração e bibliotecas extras) em APEXs 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 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 de Wi-Fi, em um APEX do fornecedor. Os desenvolvedores podem criar e enviar individualmente o APEX do fornecedor para testar as mudanças, em vez de recriar toda a imagem do fornecedor.
Isso simplifica e acelera o ciclo de iteração do desenvolvedor para aqueles que trabalham principalmente em uma área de recursos e querem iterar apenas nessa área.
O agrupamento natural de uma área de recursos em um APEX também simplifica o processo de criação, envio e teste de mudanças para essa área. Por exemplo, a reinstalação de um APEX atualiza automaticamente qualquer biblioteca ou arquivo de configuração agrupado que o APEX inclua.
O agrupamento de uma área de recursos em um APEX também simplifica a depuração ou a reversão quando um comportamento inadequado do dispositivo é observado. Por exemplo, se a telefonia estiver funcionando mal em uma nova versão, os desenvolvedores poderão tentar instalar um APEX de implementação de telefonia mais antigo em um dispositivo (sem precisar fazer o flash de uma versão completa) e verificar se o bom comportamento é 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 sobre APEX , incluindo requisitos do dispositivo, detalhes do formato de arquivo e etapas de instalação.
Em Android.bp, definir a propriedade vendor: true transforma um módulo APEX em um APEX do fornecedor.
apex {
..
vendor: true,
..
}
Binários e bibliotecas compartilhadas
Um APEX inclui dependências transitivas no payload, a menos que tenham interfaces estáveis.
As interfaces nativas estáveis para dependências de APEX do fornecedor incluem cc_library com stubs e bibliotecas LLNDK. Essas dependências são excluídas do pacote, 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 no tempo de execução.
No snippet a seguir, o APEX contém o binário (my_service) e as dependências não estáveis (*.so arquivos).
apex {
..
vendor: true,
binaries: ["my_service"],
..
}
No snippet a seguir, o APEX contém a biblioteca compartilhada my_standalone_lib e qualquer uma das dependências não estáveis (conforme descrito acima).
apex {
..
vendor: true,
native_shared_libs: ["my_standalone_lib"],
..
}
Diminuir o APEX
O APEX pode aumentar de tamanho porque agrupa dependências não estáveis. Recomendamos o uso de vinculação estática. Bibliotecas comuns, como libc++.so e libbase.so, podem ser vinculadas estaticamente a binários HAL. Tornar uma dependência para fornecer uma interface estável pode ser outra opção. A dependência não será agrupada no APEX.
Implementações de HAL
Para definir uma implementação de HAL, forneça os binários e bibliotecas correspondentes em um APEX do fornecedor semelhante aos exemplos a seguir:
Para encapsular totalmente a implementação de HAL, o APEX também precisa especificar todos os fragmentos VINTF e scripts de inicialização 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 no APEX.
apex {
..
vendor: true,
prebuilts: ["fragment.xml"],
..
}
prebuilt_etc {
name: "fragment.xml",
src: "fragment.xml",
sub_dir: "vintf",
}
Consultar APIs
Quando os fragmentos VINTF são adicionados ao APEX, use as APIs libbinder_ndk para receber os mapeamentos de interfaces HAL e nomes APEX.
AServiceManager_isUpdatableViaApex("com.android.foo.IFoo/default"):truese a instância HAL estiver definida no APEX.AServiceManager_getUpdatableApexName("com.android.foo.IFoo/default", ...): recebe o nome do APEX que define a instância HAL.AServiceManager_openDeclaredPassthroughHal("mapper", "instance", ...): use isso para abrir um HAL de passagem.
Scripts de inicialização
Os APEXs podem incluir scripts de inicialização de duas maneiras: (A) um arquivo de texto pré-criado no payload do APEX ou (B) um script de inicialização normal em /vendor/etc. É possível definir os dois 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 APEXs do fornecedor podem ter service definições e
on <property or event> diretivas.
Verifique se uma definição de service aponta para um binário no mesmo APEX.
Por exemplo, o APEX com.android.foo 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 APEXs são analisados e executados depois que os APEXs são ativados, alguns eventos ou propriedades não podem ser usados. Use apex.all.ready=true para acionar ações o mais cedo possível.
Os APEXs de inicialização podem usar on init, mas não
on early-init.
Firmware
Exemplo:
Incorpore o firmware em um APEX do fornecedor com o tipo de módulo prebuilt_firmware, conforme mostrado abaixo.
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.
ueventd
O file_contexts do APEX precisa rotular corretamente todas as entradas de payload de firmware para garantir que esses arquivos sejam acessíveis pelo ueventd no 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 do kernel em um APEX do fornecedor como módulos pré-criados, conforme mostrado abaixo.
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 corretamente todas as entradas de payload do módulo do kernel. Por exemplo:
/etc/modules(/.*)? u:object_r:vendor_kernel_modules:s0
Os módulos do kernel precisam ser instalados explicitamente. O exemplo de script de inicialização a seguir 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 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 do fornecedor oferecem suporte a vários outros arquivos de configuração normalmente encontrados na partição do fornecedor como pré-criados dentro dos APEXs do fornecedor, e mais estão sendo adicionados.
Exemplos:
- XMLs de declaração de recursos
- XMLs de recursos de sensores como pré-criados em um APEX do fornecedor HAL de sensores
- Arquivos de configuração de entrada
- Configurações de tela sensível ao toque como pré-criadas em um APEX do fornecedor somente de configuração
APEXs de inicialização do fornecedor
Alguns serviços HAL, como keymint, precisam estar disponíveis antes que os APEXs sejam ativados. Esses HALs geralmente definem early_hal na definição de serviço no script de inicialização. Outro exemplo é a classe animation, que normalmente é iniciada antes do evento post-fs-data. Quando um serviço HAL inicial é
empacotado no APEX do fornecedor, defina o APEX "vendorBootstrap": true no manifesto do APEX
para que ele possa ser ativado mais cedo. Os APEXs de inicialização só podem ser ativados do local pré-criado, como /vendor/apex, não de /data/apex.
Propriedades do sistema
Estas são as propriedades do sistema que o framework lê para oferecer suporte a APEXs do fornecedor:
input_device.config_file.apex=<apex name>- quando definido, os arquivos de configuração de entrada (*.idc,*.kle*.kcm) são pesquisados no diretório/etc/usrdo APEX.ro.vulkan.apex=<apex name>- quando definido, o driver Vulkan é carregado do APEX. Como o driver Vulkan é usado por HALs iniciais, crie o APEX de inicialização e configure esse namespace do vinculador visível.
Defina as propriedades do sistema em scripts de inicialização usando setprop
comando.
Recursos extras
Seleção de APEX na inicialização
Exemplo:
Os APEXs do fornecedor podem ser ativados opcionalmente durante a inicialização.
Se você especificar um nome de arquivo usando a propriedade do sistema
ro.vendor.apex.<apex name>, apenas o APEX correspondente ao nome do arquivo será
ativado para o <apex name>.
O APEX com <apex name> será ignorado (não ativado) se essa propriedade do sistema
estiver definida como none. É possível usar esse recurso para instalar várias cópias de um APEX com o mesmo nome. Se houver várias versões do mesmo APEX, elas precisarão compartilhar a mesma chave.
Exemplos de casos de uso:
- Instalar três versões do APEX do fornecedor HAL de Wi-Fi:as equipes de QA podem executar testes manuais ou automatizados usando uma versão, reiniciar em outra versão e executar os testes novamente e, em seguida, comparar os resultados finais.
- Instalar duas versões do APEX do fornecedor HAL de câmera, atual e experimental: os usuários de teste podem usar a versão experimental sem fazer o download e instalar um arquivo adicional, 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 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 mudar o valor padrão, definido em um dispositivo já inicializado.
- Substitui o valor do bootconfig, se presente.
persist.vendor.apex.<apex name>
O valor da propriedade precisa ser o nome do arquivo do APEX que será ativado ou none para desativar o APEX.
// 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 precisa ser configurada usando o 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, mude a versão ativada definindo 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 oferecer suporte à atualização do bootconfig após o flash (por exemplo, usando comandos fastboot
oem), a mudança da propriedade do bootconfig para o APEX multi-instalado
também mudará a versão ativada na inicialização.
Para dispositivos de referência virtual baseados no Cuttlefish,
é possível usar o comando --extra_bootconfig_args para definir a propriedade do 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";