Google is committed to advancing racial equity for Black communities. See how.
Esta página foi traduzida pela API Cloud Translation.
Switch to English

Implementando atualizações A / B

OEMs e fornecedores de SoC que desejam implementar atualizações de sistema A / B devem garantir que seu bootloader implemente o boot_control HAL 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:

Figura 1. Máquina de estado do bootloader

Configurando o kernel

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

  1. Escolha a seguinte série de patch do kernel (se necessário):
  2. 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>"
    ... onde o valor <public-key-id> é o ID da chave pública usada para verificar a assinatura da tabela de verificação (para detalhes, consulte dm-verity ).
  3. Adicione o certificado .X509 contendo a chave pública ao chaveiro do sistema:
    1. Copie o certificado .X509 formatado no formato .der para a raiz do diretório do kernel . Se o certificado .X509 é formatado como uma .pem arquivo, use o seguinte openssl comando converter de .pem para .der formato:
      openssl x509 -in <x509-pem-certificate> -outform der -out <x509-der-certificate>
    2. Crie o zImage para incluir o certificado como parte do chaveiro do sistema. Para verificar, verifique a entrada procfs (requer que KEYS_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
      Inclusão bem-sucedida do certificado .X509 indica a presença da chave pública no chaveiro do sistema (destaque denota o ID da chave pública).
    3. Substitua o espaço por # e passe-o como <public-key-id> na linha de comando do kernel. Por exemplo, passe Android:#7e4333f9bba00adfe0ede979e28ed1920492b40f no lugar de <public-key-id> .

Definir variáveis ​​de construção

Carregadores de inicialização com capacidade A / B devem atender aos seguintes critérios de variável de compilação:

Deve definir para alvo A / B
  • AB_OTA_UPDATER := true
  • AB_OTA_PARTITIONS := \
    boot \
    system \
    vendor
    e outras partições atualizadas por meio de update_engine (rádio, bootloader, etc.)
  • PRODUCT_PACKAGES += \
    update_engine \
    update_verifier
Para obter um exemplo, consulte /device/google/marlin/+/android-7.1.0_r1/device-common.mk . Você pode opcionalmente conduzir a etapa dex2oat pós-instalação (mas pré-reinicialização) descrita em Compilando .
Fortemente recomendado para alvo A / B
  • Defina TARGET_NO_RECOVERY := true
  • Defina BOARD_USES_RECOVERY_AS_BOOT := true
  • Não defina BOARD_RECOVERYIMAGE_PARTITION_SIZE
Não é possível definir para alvo A / B
  • BOARD_CACHEIMAGE_PARTITION_SIZE
  • BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE
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 como segue (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 esconder blocos temporariamente durante a aplicação de atualizações. Nunca houve uma boa maneira de dimensionar a partição do cache: o tamanho necessário dependia 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 as atualizações A / B, não há necessidade de esconder blocos (porque você está sempre gravando em uma partição que não é usada atualmente) e com o streaming A / B não há necessidade de baixar o pacote OTA inteiro antes de aplicá-lo.

Recuperação

O disco RAM de recuperação agora está contido no arquivo boot.img . Ao entrar na 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 as atualizações. As atualizações A / B são aplicadas por update_engine em execução na imagem do sistema inicializado regular. Ainda existe um modo de recuperação usado para implementar a redefinição dos 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 carregador de inicialização 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 de kernel

O sufixo do slot atual deve ser passado por um nó da árvore de dispositivo específico (DT) ( /firmware/android/slot_suffix ) ou por meio do argumento da linha de comando androidboot.slot_suffix .

Por padrão, o fastboot atualiza o slot atual em um dispositivo A / B. Se o pacote de atualização também contém imagens para o outro slot não atual, o fastboot atualiza essas imagens também. 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 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 de 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 deve oferecer suporte às 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, então este valor é 2 .
  • slot-successful:<slot-suffix> . Retorna "sim" se o slot fornecido foi marcado como inicializando com sucesso, "não" caso contrário.
  • slot-unbootable:<slot-suffix> . Retorna “sim” se o slot fornecido está marcado como não inicializável, “não” caso contrário.
  • slot-retry-count . Número de novas tentativas restantes para tentar inicializar o slot fornecido.

Para ver 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 para o 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 AB_OTA_PARTITIONS make.

Por exemplo, se um par de partições bootloader_a e booloader_b estão incluídas ( _a e _b são os sufixos caça-níqueis), você pode atualizar essas partições, especificando o seguinte na configuração do produto ou placa:

AB_OTA_PARTITIONS := \
  boot \
  system \
  bootloader

Todas as partições atualizadas por 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 novos dados do slot falhem na verificação durante o processo de atualização e, portanto, falhem 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 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 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 a compilação just-in-time (JIT) . Isso significa que você deve compilar com antecedência 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):

  1. Inclua os componentes nativos na construçã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
    
  2. Conecte o script de compilação ao update_engine 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 na instalação dos arquivos pré-selecionados na segunda partição do sistema não usada, consulte Primeira instalação de inicialização dos arquivos DEX_PREOPT .