Os OEMs e fornecedores de SoC que quiserem implementar atualizações do sistema A/B precisam garantir que o carregador de inicialização implemente o HAL boot_control e transmita os parâmetros corretos ao kernel.
Implementar a HAL de controle de inicialização
Os carregadores de inicialização compatíveis com A/B precisam implementar a HAL boot_control
em
hardware/libhardware/include/hardware/boot_control.h
. É possível testar implementações usando a
utilidade
system/extras/bootctl
e
system/extras/tests/bootloader/
.
Você também precisa implementar a máquina de estado mostrada abaixo:

Configurar o kernel
Para implementar atualizações do sistema A/B:
-
Selecione as seguintes séries de patches do kernel (se necessário):
- Se você estiver inicializando sem ramdisk e usando "inicializar como recuperação", faça o cherrypick android-review.googlesource.com/#/c/158491/.
- Para configurar o dm-verity sem ramdisk, faça o cherrypick android-review.googlesource.com/#/q/status:merged+project:kernel/common+branch:android-3.18+topic:A_B_Changes_3.18.
-
Verifique se os argumentos da linha de comando do kernel contêm os seguintes argumentos extras:
... em que o valorskip_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 de integridade. Para mais detalhes, consulte dm-verity. -
Adicione o certificado .X509 que contém a chave pública ao keyring do sistema:
-
Copie o certificado .X509 formatado no formato
.der
para a raiz do diretóriokernel
. Se o certificado .X509 estiver formatado como um arquivo.pem
, use o seguinte comandoopenssl
para converter de.pem
para o formato.der
:openssl x509 -in <x509-pem-certificate> -outform der -out <x509-der-certificate>
-
Crie o
zImage
para incluir o certificado como parte do keyring do sistema. Para verificar,confira a entradaprocfs
. Isso exige queKEYS_CONFIG_DEBUG_PROC_KEYS
esteja ativado: A inclusão bem-sucedida do certificado .X509 indica a presença da chave pública no keyring do sistema. O destaque indica o ID da chave pública.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
-
Substitua o espaço por
#
e transmita como<public-key-id>
na linha de comando do kernel. Por exemplo, transmitaAndroid:#7e4333f9bba00adfe0ede979e28ed1920492b40f
em vez de<public-key-id>
.
-
Copie o certificado .X509 formatado no formato
Definir variáveis de build
Os carregadores de inicialização compatíveis com A/B precisam atender aos seguintes critérios de variáveis de build:
Precisa ser definido para o destino de teste A/B |
/device/google/marlin/+/android-7.1.0_r1/device-common.mk . Você pode realizar a etapa dex2oat pós-instalação (mas pré-reinicialização) descrita em
Compilação.
|
---|---|
Altamente recomendado para a meta de teste A/B |
|
Não é possível definir para o destino de teste A/B |
|
Opcional para builds de depuração | PRODUCT_PACKAGES_DEBUG += update_engine_client |
Definir partições (slots)
Os dispositivos A/B não precisam de uma partição de recuperação ou 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 testadas com A/B precisam ser nomeadas da seguinte forma (os slots sempre são 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 era 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: o tamanho necessário dependia das atualizações que você queria aplicar. O pior caso seria uma partição de cache tão grande quanto a imagem do sistema. Com as atualizações A/B, não é necessário armazenar blocos (porque você sempre grava em uma partição que não está sendo usada no momento) e com o streaming A/B, não é necessário 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 no modo de
recuperação, o carregador de inicialização 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 atualizações. As atualizações A/B
são aplicadas pelo update_engine
em execução na imagem do sistema inicializado regularmente.
Ainda há um modo de recuperação usado para implementar a redefinição de dados de fábrica e o sideloading de pacotes de atualização (de onde veio o nome "recuperação"). O código e os dados do modo de recuperação
são armazenados na partição de inicialização regular em um ramdisk. Para inicializar a imagem do sistema, o
bootloader informa ao kernel para ignorar o ramdisk. Caso contrário, o dispositivo é inicializado 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 de tamanho.
Fstab
O argumento slotselect
precisa estar na linha das partições
testadas com A/B. Exemplo:
<path-to-block-device>/vendor /vendor ext4 ro wait,verify=<path-to-block-device>/metadata,slotselect
Nenhuma partição pode ser chamada de 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 precisa ser transmitido por um nó específico da árvore de dispositivos (DT)
(/firmware/android/slot_suffix
) ou pela
linha de comando do kernel androidboot.slot_suffix
ou pelo argumento bootconfig.
Por padrão, o fastboot atualiza 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 vai atualizar essas imagens. As opções disponíveis incluem:
-
--slot SLOT
. Substitua o comportamento padrão e solicite ao fastboot que faça o flash do slot transmitido 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
. Confira detalhes sobre os comandos.
Se o carregador de inicialização implementar o fastboot, ele precisará oferecer suporte ao comando
set_active <slot>
, que define o slot ativo atual como o slot especificado. Isso
também precisa limpar a flag não inicializável para esse slot e redefinir a contagem de novas tentativas para os valores
padrão. O carregador de inicialização também precisa ser compatível com as seguintes variáveis:
-
has-slot:<partition-base-name-without-suffix>
. Retorna "yes" se a partição especificada for compatível com espaços e "no" caso contrário. current-slot
. Retorna o sufixo do slot que será inicializado em seguida.-
slot-count
. Retorna um número inteiro que representa o número de slots disponíveis. No momento, há dois slots disponíveis, então esse valor é2
. -
slot-successful:<slot-suffix>
. Retorna "yes" se o slot especificado foi marcado como inicializado com sucesso. Caso contrário, retorna "no". -
slot-unbootable:<slot-suffix>
. Retorna "yes" se o slot especificado estiver marcado como não inicializável. Caso contrário, retorna "no". -
slot-retry-count:<slot-suffix>
. Número de novas tentativas restantes para inicializar o slot especificado.
Para conferir todas as variáveis, execute
fastboot getvar all
.
Gerar pacotes OTA
As ferramentas de pacote OTA seguem os mesmos comandos dos
dispositivos não A/B. O arquivo target_files.zip
precisa ser gerado definindo as variáveis de build para o destino do teste A/B. As ferramentas de pacote OTA identificam e geram automaticamente pacotes no formato do atualizador A/B.
Exemplos:
-
Para gerar uma OTA completa:
./build/make/tools/releasetools/ota_from_target_files \ dist_output/tardis-target_files.zip \ ota_update.zip
-
Para gerar uma 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
Configurar 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 um sufixo por slot (como _a
). A lista de partições para as quais o gerador de payload define uma atualização é configurada pela variável AB_OTA_PARTITIONS
make.
Por exemplo, se um par de partições bootloader_a
e booloader_b
for incluído (_a
e _b
são os sufixos de 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 por update_engine
não podem ser modificadas pelo restante do sistema. Durante 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 novos dados de slot
não sejam verificados durante o processo de atualização e, portanto, a atualização falhe.
Configurar após a instalação
É possível configurar a etapa de pós-instalação de maneira diferente para cada partição atualizada usando um conjunto de
pares de chave-valor. 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 você não estiver usando um disco RAM). Além disso, especifique o tipo de sistema de arquivos a ser transmitido 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
Compilar apps
Os apps podem ser compilados em segundo plano antes da reinicialização com a nova imagem do sistema. Para compilar apps em segundo plano, adicione o seguinte à configuração do dispositivo do produto (em device.mk do produto):
-
Inclua os componentes nativos no build 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
para que ele seja executado como uma etapa 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 ajuda na instalação dos arquivos pré-otimizados na segunda partição do sistema não utilizada, consulte Instalação na primeira inicialização dos arquivos DEX_PREOPT.