OEMs e fornecedores de SoC que desejam implementar atualizações de sistema A/B devem garantir que seu carregador de inicialização implemente o HAL boot_control e passe os parâmetros corretos para o kernel.
Implementando o HAL de controle de inicialização
Os carregadores de inicialização com capacidade A/B devem implementar o HAL boot_control
em hardware/libhardware/include/hardware/boot_control.h
. Você pode testar implementações usando o utilitário system/extras/bootctl
e system/extras/tests/bootloader/
.
Você também deve implementar a máquina de estado mostrada abaixo:
Configurando o núcleo
Para implementar atualizações do sistema A/B:
- Escolha a seguinte série de patches do kernel (se necessário):
- Se inicializar sem ramdisk e usar "boot as recovery", escolha android-review.googlesource.com/#/c/158491/ .
- Para configurar o dm-verity sem ramdisk, selecione android-review.googlesource.com/#/q/status:merged+project:kernel/common+branch:android-3.18+topic:A_B_Changes_3.18 .
- Certifique-se de que os argumentos da linha de comando do kernel contenham os seguintes argumentos extras:
skip_initramfs rootwait ro init=/init root="/dev/dm-0 dm=system none ro,0 1 android-verity <public-key-id> <path-to-system-partition>"
<public-key-id>
é o ID da chave pública usada para verificar a assinatura da tabela verity (para obter detalhes, consulte dm-verity ) . - Adicione o certificado .X509 que contém a chave pública ao chaveiro do sistema:
- Copie o certificado .X509 formatado no formato
.der
para a raiz do diretório dokernel
. Se o certificado .X509 estiver formatado como um arquivo.pem
, use o seguinte comandoopenssl
para converter do formato.pem
para.der
:openssl x509 -in <x509-pem-certificate> -outform der -out <x509-der-certificate>
- Crie o
zImage
para incluir o certificado como parte do chaveiro do sistema. Para verificar, verifique a entradaprocfs
(requer queKEYS_CONFIG_DEBUG_PROC_KEYS
esteja habilitado):angler:/# cat /proc/keys 1c8a217e I------ 1 perm 1f010000 0 0 asymmetri Android: 7e4333f9bba00adfe0ede979e28ed1920492b40f: X509.RSA 0492b40f [] 2d454e3e I------ 1 perm 1f030000 0 0 keyring .system_keyring: 1/4
A inclusão bem-sucedida do certificado .X509 indica a presença da chave pública no chaveiro do sistema (o realce indica o ID da chave pública). - Substitua o espaço por
#
e passe-o como<public-key-id>
na linha de comando do kernel. Por exemplo, passeAndroid:#7e4333f9bba00adfe0ede979e28ed1920492b40f
no lugar de<public-key-id>
.
- Copie o certificado .X509 formatado no formato
Como configurar variáveis de compilação
Os carregadores de inicialização com capacidade A/B devem atender aos seguintes critérios de variável de compilação:
Deve definir para o destino A/B |
/device/google/marlin/+/android-7.1.0_r1/device-common.mk . Opcionalmente, você pode conduzir a etapa dex2oat pós-instalação (mas pré-reinicialização) descrita em Compilando . |
---|---|
Fortemente recomendado para alvo A/B |
|
Não é possível definir para o destino A/B |
|
Opcional para compilações de depuração | PRODUCT_PACKAGES_DEBUG += update_engine_client |
Configurando partições (slots)
Os dispositivos A/B não precisam de uma partição de recuperação ou partição de cache porque o Android não usa mais essas partições. A partição de dados agora é usada para o pacote OTA baixado e o código da imagem de recuperação está na partição de inicialização. Todas as partições que são A/B-ed devem ser nomeadas da seguinte forma (os slots são sempre nomeados a
, b
, etc.): boot_a
, boot_b
, system_a
, system_b
, vendor_a
, vendor_b
.
Cache
Para atualizações não A/B, a partição de cache foi usada para armazenar pacotes OTA baixados e para armazenar blocos temporariamente durante a aplicação de atualizações. Nunca houve uma boa maneira de dimensionar a partição de cache: quão grande ela precisava depender de quais atualizações você queria aplicar. O pior caso seria uma partição de cache tão grande quanto a imagem do sistema. Com atualizações A/B não há necessidade de stash blocks (porque você está sempre gravando em uma partição que não está sendo usada no momento) e com streaming A/B não há necessidade de baixar todo o pacote OTA antes de aplicá-lo.
Recuperação
O disco RAM de recuperação agora está contido no arquivo boot.img
. Ao entrar em recuperação, o bootloader não pode colocar a opção skip_initramfs
na linha de comando do kernel.
Para atualizações não A/B, a partição de recuperação contém o código usado para aplicar as atualizações. As atualizações A/B são aplicadas pelo update_engine
em execução na imagem normal do sistema inicializado. Ainda há um modo de recuperação usado para implementar a redefinição de dados de fábrica e o sideload de pacotes de atualização (de onde veio o nome "recuperação"). O código e os dados para o modo de recuperação são armazenados na partição de inicialização normal em um ramdisk; para inicializar na imagem do sistema, o bootloader diz ao kernel para pular o ramdisk (caso contrário, o dispositivo inicializa no modo de recuperação. O modo de recuperação é pequeno (e grande parte dele já estava na partição de inicialização), então a partição de inicialização não aumenta no tamanho.
Fstab
O argumento slotselect
deve estar na linha para as partições A/B-ed. Por exemplo:
<path-to-block-device>/vendor /vendor ext4 ro wait,verify=<path-to-block-device>/metadata,slotselect
Nenhuma partição deve ser nomeada como vendor
. Em vez disso, a partição vendor_a
ou vendor_b
será selecionada e montada no ponto de montagem /vendor
.
Argumentos de slot do kernel
O sufixo do slot atual deve ser passado por meio de um nó específico da árvore de dispositivos (DT) ( /firmware/android/slot_suffix
) ou pela linha de comando do kernel androidboot.slot_suffix
ou argumento bootconfig.
Por padrão, o fastboot pisca o slot atual em um dispositivo A/B. Se o pacote de atualização também contiver imagens para o outro slot não atual, o fastboot também exibirá essas imagens. As opções disponíveis incluem:
-
--slot SLOT
. Substitua o comportamento padrão e solicite que o fastboot atualize o slot que é passado como um argumento. -
--set-active [ SLOT ]
. Defina o slot como ativo. Se nenhum argumento opcional for especificado, o slot atual será definido como ativo. -
fastboot --help
. Obtenha detalhes sobre os comandos.
Se o bootloader implementar o fastboot, ele deve suportar o comando set_active <slot>
que define o slot ativo atual para o slot fornecido (isso também deve limpar o sinalizador não inicializável desse slot e redefinir a contagem de tentativas para os valores padrão). O bootloader também deve suportar as seguintes variáveis:
-
has-slot:<partition-base-name-without-suffix>
. Retorna “sim” se a partição fornecida suportar slots, “não” caso contrário. -
current-slot
. Retorna o sufixo do slot que será inicializado a partir do próximo. -
slot-count
. Retorna um número inteiro que representa o número de slots disponíveis. Atualmente, dois slots são suportados, portanto, esse valor é2
. -
slot-successful:<slot-suffix>
. Retorna "sim" se o slot especificado foi marcado como inicializando com sucesso, "não" caso contrário. -
slot-unbootable:<slot-suffix>
. Retorna "sim" se o slot especificado estiver marcado como não inicializável, "não" caso contrário. -
slot-retry-count
. Número de tentativas restantes para tentar inicializar o slot fornecido.
Para visualizar todas as variáveis, execute fastboot getvar all
.
Gerando pacotes OTA
As ferramentas do pacote OTA seguem os mesmos comandos que os comandos para dispositivos não A/B. O arquivo target_files.zip
deve ser gerado definindo as variáveis de construção para o destino A/B. As ferramentas de pacote OTA identificam e geram pacotes automaticamente no formato do atualizador A/B.
Exemplos:
- Para gerar um OTA completo:
./build/make/tools/releasetools/ota_from_target_files \ dist_output/tardis-target_files.zip \ ota_update.zip
- Para gerar um OTA incremental:
./build/make/tools/releasetools/ota_from_target_files \ -i PREVIOUS-tardis-target_files.zip \ dist_output/tardis-target_files.zip \ incremental_ota_update.zip
Configurando partições
O update_engine
pode atualizar qualquer par de partições A/B definidas no mesmo disco. Um par de partições tem um prefixo comum (como system
ou boot
) e sufixo por slot (como _a
). A lista de partições para as quais o gerador de carga útil define uma atualização é configurada pela variável make AB_OTA_PARTITIONS
.
Por exemplo, se um par de partições bootloader_a
e booloader_b
estiver incluído ( _a
e _b
são os sufixos do slot), você poderá atualizar essas partições especificando o seguinte na configuração do produto ou da placa:
AB_OTA_PARTITIONS := \ boot \ system \ bootloader
Todas as partições atualizadas pelo update_engine
não devem ser modificadas pelo resto do sistema. Durante as atualizações incrementais ou delta , os dados binários do slot atual são usados para gerar os dados no novo slot. Qualquer modificação pode fazer com que os dados do novo slot falhe na verificação durante o processo de atualização e, portanto, falhe na atualização.
Configurando a pós-instalação
Você pode configurar a etapa de pós-instalação de maneira diferente para cada partição atualizada usando um conjunto de pares de valores-chave. Para executar um programa localizado em /system/usr/bin/postinst
em uma nova imagem, especifique o caminho relativo à raiz do sistema de arquivos na partição do sistema.
Por exemplo, usr/bin/postinst
é system/usr/bin/postinst
(se não estiver usando um disco RAM). Além disso, especifique o tipo de sistema de arquivos a ser passado para a chamada de sistema mount(2)
. Adicione o seguinte aos arquivos .mk
do produto ou dispositivo (se aplicável):
AB_OTA_POSTINSTALL_CONFIG += \ RUN_POSTINSTALL_system=true \ POSTINSTALL_PATH_system=usr/bin/postinst \ FILESYSTEM_TYPE_system=ext4
Compilando
Por motivos de segurança, system_server
não pode usar compilação just-in-time (JIT) . Isso significa que você deve compilar com antecedência os arquivos odex para system_server
e suas dependências no mínimo; qualquer outra coisa é opcional.
Para compilar aplicativos em segundo plano, você deve adicionar o seguinte à configuração do dispositivo do produto (no device.mk do produto):
- Inclua os componentes nativos na compilação para garantir que o script de compilação e os binários sejam compilados e incluídos na imagem do sistema.
# A/B OTA dexopt package PRODUCT_PACKAGES += otapreopt_script
- Conecte o script de compilação ao
update_engine
de forma que seja executado como uma etapa de pós-instalação.# A/B OTA dexopt update_engine hookup AB_OTA_POSTINSTALL_CONFIG += \ RUN_POSTINSTALL_system=true \ POSTINSTALL_PATH_system=system/bin/otapreopt_script \ FILESYSTEM_TYPE_system=ext4 \ POSTINSTALL_OPTIONAL_system=true
Para obter ajuda para instalar os arquivos pré-optados na segunda partição do sistema não utilizada, consulte Primeira instalação de inicialização dos arquivos DEX_PREOPT .