Implementar atualizações A/B

OEMs e fornecedores de SoC que quiserem implementar atualizações do sistema A/B precisam garantir o carregador de inicialização implementa a HAL boot_control e passa os parâmetros corretos para a grão

Implementar a HAL de controle de inicialização

Os carregadores de inicialização com recursos A/B precisam implementar a HAL boot_control em hardware/libhardware/include/hardware/boot_control.h. É possível testar implementações usando o system/extras/bootctl concessionária de serviço público e system/extras/tests/bootloader/.

Também é necessário implementar a máquina de estado mostrada abaixo:

Figura 1. Máquina de estado do carregador de inicialização

Configurar o kernel

Para implementar atualizações do sistema A/B:

  1. Selecione as seguintes séries de patches de kernel (se necessário):
  2. Verifique se os argumentos de linha de comando do kernel contêm 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>"
    ... em que o valor <public-key-id> é o ID da chave pública usada para verificar a assinatura da tabela de verdade (para mais detalhes, consulte dm-verity).
  3. Adicione o certificado .X509 que contém a chave pública ao keyring do sistema:
    1. Copie o certificado .X509 formatado no formato .der para a raiz do kernel. Se o certificado .X509 estiver formatado como um .pem, use o seguinte comando openssl para converter do Formato de .pem a .der:
      openssl x509 -in <x509-pem-certificate> -outform der -out <x509-der-certificate>
    2. Crie o zImage para incluir o certificado como parte do keyring do sistema. Para verificar,confira a entrada procfs (requer KEYS_CONFIG_DEBUG_PROC_KEYS para ativar):
      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 keyring do sistema (destaque indica o ID da chave pública).
    3. Substitua o espaço por # e transmita-o como <public-key-id> na linha de comando do kernel. Por exemplo, transmita Android:#7e4333f9bba00adfe0ede979e28ed1920492b40f no lugar de <public-key-id>.

Definir variáveis de build

Os carregadores de inicialização com recursos A/B precisam atender aos seguintes critérios de variáveis de build:

Precisa definir para a meta A/B
  • AB_OTA_UPDATER := true
  • AB_OTA_PARTITIONS := \ e
      boot \ e
      system \ e

    de   vendor e outras partições atualizadas por meio de update_engine (rádio, carregador de inicialização, etc.)
  • PRODUCT_PACKAGES += \
      update_engine \
      update_verifier
. Para ver um exemplo, consulte /device/google/marlin/+/android-7.1.0_r1/device-common.mk. Você pode realizar a etapa de dex2oat pós-instalação (mas antes da reinicialização) descrita em Compilação.
Recomendado para a meta A/B
  • Definir TARGET_NO_RECOVERY := true
  • Definir BOARD_USES_RECOVERY_AS_BOOT := true
  • Não definir BOARD_RECOVERYIMAGE_PARTITION_SIZE
Não foi possível definir para a meta A/B
  • BOARD_CACHEIMAGE_PARTITION_SIZE
  • BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE
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 a o código da imagem de recuperação está na partição de inicialização. Todas as partições A/B-ed devem ser nomeadas da seguinte maneira (os slots sempre são chamados de a, b etc.): boot_a, boot_b, system_a, system_b e vendor_a. vendor_b.

Cache

Para atualizações não A/B, a partição de cache era usada para armazenar os pacotes OTA baixados e para bloquear temporariamente ao aplicar atualizações. Nunca havia uma boa maneira de dimensionar o cache partição: o tamanho precisava depender das atualizações que você queria aplicar. As piores nesse caso seria uma partição de cache do tamanho da imagem do sistema. Com as atualizações A/B, você não precisa para armazenar blocos (porque você está sempre gravando em uma partição que não é usada atualmente) e com o streaming A/B, não será necessário fazer o download do pacote OTA inteiro antes de aplicá-lo.

Recuperação

O disco RAM de recuperação agora está contido no arquivo boot.img. Ao entrar em de recuperação, o carregador de inicialização não pode colocar a opção skip_initramfs a 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. A/B as atualizações são aplicadas por update_engine em execução na imagem inicial do sistema normal. Ainda há um modo de recuperação usado para implementar a redefinição para configuração original e o sideload de atualização (que é de onde veio o nome “recovery”. O código e os dados do modo de recuperação é armazenado na partição de inicialização normal em um ramdisk; para inicializar a imagem do sistema, o carregador de inicialização instrui o kernel a pular o ramdisk (caso contrário, o dispositivo inicializa na etapa de recuperação modo O modo de recuperação é pequeno (e grande parte dele já estava na partição de inicialização), portanto, partição não aumenta de tamanho.

Fstab

O argumento slotselect precisa estar na linha do argumento A/B partições diferentes. Exemplo:

<path-to-block-device>/vendor  /vendor  ext4  ro
wait,verify=<path-to-block-device>/metadata,slotselect

Nenhuma partição pode ser nomeada como vendor. Em vez disso, particione vendor_a ou vendor_b será selecionado e montado no ponto de montagem /vendor.

Argumentos de slot do kernel

O sufixo de slot atual precisa ser transmitido por um nó da árvore de dispositivos (DT) específico (/firmware/android/slot_suffix) ou pelo Linha de comando do kernel androidboot.slot_suffix ou 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 atualiza essas imagens. As opções disponíveis incluem:

  • --slot SLOT. Modifique o comportamento padrão e solicite que o fastboot atualize o slot do slot um argumento.
  • --set-active [SLOT]. Defina o slot como ativo. Se não houver argumento opcional for especificado, o espaço atual será definido como ativo.
  • fastboot --help: Veja detalhes sobre os comandos.

Se o carregador de inicialização implementar o fastboot, ele precisará ser compatível com o comando set_active <slot> que define o slot ativo atual para o slot especificado (essa também precisará limpar a sinalização de "não inicializável" desse slot e redefinir a contagem de tentativas para o 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 o suporte a slots. Caso contrário, retorna o valor "no".
  • current-slot: Retorna o sufixo de 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, há suporte para dois slots, então esse valor é 2.
  • slot-successful:<slot-suffix>. Retorna "yes" se o espaço fornecido foi marcado como "Inicialização bem-sucedida", "não" caso contrário.
  • slot-unbootable:<slot-suffix>. Retorna "yes" se o espaço fornecido estiver marcado como não inicializável, "não" caso contrário.
  • slot-retry-count:<slot-suffix>. Número de novas tentativas restantes para tentar inicializar o slot especificado.

Para conferir todas as variáveis, execute fastboot getvar all:

Gerar pacotes OTA

As ferramentas de pacotes OTA seguem os mesmos comandos das para dispositivos não A/B. O arquivo target_files.zip precisa ser gerado por definindo as variáveis de build para o destino A/B. As ferramentas de pacotes OTA identificam automaticamente e gerar pacotes 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
    

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 o sufixo por slot (como _a). A lista de partições para as quais o payload generator define que uma atualização é configurada pela variável make do AB_OTA_PARTITIONS.

Por exemplo, se um par de partições bootloader_a e booloader_b estão incluídos (_a e _b são os slots sufixos), atualize essas partições especificando o seguinte no produto ou na placa configuração:

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 usada para gerar os dados no novo slot. Qualquer modificação pode fazer com que os novos dados de espaço falhar na verificação durante o processo de atualização e, portanto, falhar na atualização.

Configurar pós-instalação

É possível configurar a etapa pós-instalação de forma 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 não for usando um disco de RAM). Além disso, especifique o tipo do sistema de arquivos a passar para o mount(2). Adicione o seguinte ao produto ou dispositivo Arquivos .mk (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 em segundo plano, adicione o seguinte à configuração do dispositivo do produto (na device.mk do produto):

  1. 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
    
  2. Conecte o script de compilação a update_engine, que é executado como um 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 receber ajuda com a instalação dos arquivos pré-aprovados na segunda partição do sistema não utilizada, consulte Primeira instalação de inicialização dos arquivos DEX_PREOPT.