Atualizações A / B perfeitas no Android: como funcionam

imagem



Olá. Na SberDevices, nossa equipe está envolvida no desenvolvimento de vários hardwares e firmware para eles baseados em AOSP.



A partir do Android 8 (alguns fornecedores a partir do 7.1), o sistema tem um novo mecanismo para rolar atualizações OTA, o assim chamado. Atualizações A / B OTA contínuas - atualizações contínuas. Nesta postagem, descreverei os princípios gerais de seu funcionamento, considerarei o mecanismo do ponto de vista do desenvolvedor e também o compararei com a abordagem antiga (chamaremos de baseada em recuperação) para aplicar atualizações. Todos os itens a seguir serão verdadeiros apenas para AOSP puro, uma vez que a implementação específica depende do fornecedor.



OTA baseado em recuperação



As atualizações para Android são entregues na forma de um arquivo zip com atualizações baseadas em bloco. Na época do KitKat, era apenas um conjunto de arquivos copiados para o dispositivo pelo script incluído. Não vou me alongar sobre este modo em detalhes, vou descrever brevemente os princípios básicos de seu funcionamento:



  • O arquivo zip é baixado pelo sistema para o dispositivo;
  • o sistema reinicia no modo de recuperação ;
  • verificar a compatibilidade da atualização com o dispositivo, sua assinatura;
  • se tudo estiver OK, o script de atualização do arquivo zip é executado ;
  • durante a atualização, o dispositivo pode reiniciar várias vezes (por exemplo, para atualizar a árvore de dispositivos );
  • se tudo correr bem, inicialize no novo firmware.


Quais são as desvantagens deste esquema?



  • A necessidade de reservar uma quantidade suficiente de memória interna para o arquivo OTA. A seção / cache é usada para isso . Alguns fornecedores usam / data , mas isso é raro. Como resultado, o usuário fica com menos espaço (sim, os aplicativos ainda podem usar espaço na partição / cache , mas com algumas restrições).
  • Reinicializar e aplicar a atualização leva tempo, o que pode ser crítico para alguns tipos de dispositivos, por exemplo, para Smart TVs.
  • A interrupção do processo de atualização pode resultar em um loop de inicialização .
  • Não há como reverter para a versão antiga do firmware.


Essa inconveniência permite que você ignore o método de atualização contínua. Vamos ver como isso funciona.



Seamless A / B OTA



Principais componentes e mecanismos necessários para implementar atualizações A / B contínuas :



  • marcação de slot de memória flash; 
  • interação com o carregador, gerenciando o estado dos slots ;
  • daemon do sistema update_engine ;
  • gerando um arquivo zip com uma atualização. Este aspecto não será considerado neste artigo.


Slots



O princípio básico do A / B OTA é o  slot . Todas as partições que precisam ser atualizadas (podem ser quaisquer partições, não apenas as do sistema) devem estar em duas cópias ou, caso contrário, em slots. No Android realização é suportado por duas ranhuras, que são chamados respectivamente Um e B . O sistema inicializa e funciona a partir do slot atual, o segundo é usado apenas no momento da atualização. Um sufixo com o nome do slot é adicionado ao nome da seção.



Abaixo está uma tabela que compara as duas opções para organizar partições em um dispositivo. Todas as partições com fenda são marcadas com a opção de montagem  slotelect para que o sistema possa selecionar o slot correto. Dependendo de onde eles são descritos, isso poderia ser fstab

imagem



ou dts .



Mudanças na tabela de partição



  • B / cache não é mais necessário. Agora a atualização pode ser salva em / data ou imediatamente em um slot inativo (mais sobre isso abaixo). 
  • A seção de recuperação também não é mais usada. Porém,  o modo de  recuperação  ainda existe, é necessário, por exemplo, redefinir o dispositivo para as configurações de fábrica (isso pode levar a uma equipe de  resgate ). Ou para os chamados. atualização manual ( sideload ) via adbO ramdisk de recuperação  agora está dentro da  partição de inicialização , o kernel é compartilhado.
  • (android/recovery) cmdline ‑ skip_initramfs.


À primeira vista, parece que tal esquema não é o ideal, uma vez que é necessário alocar o dobro de espaço para o sistema. Mas nos livramos do  / cache , o que significa que já salvamos muita memória. Assim, o sistema vai demorar um pouco mais do que a opção de recuperação .



A principal vantagem das atualizações A / B é a capacidade de  transmitir o firmware. É ele que garante atualizações perfeitas e transparentes para o usuário: para atualizar o dispositivo, basta reiniciar em um novo slot. Neste modo, não há necessidade de baixar o arquivo zip com antecedência, ocupando espaço em / data . Em vez disso, o sistema grava imediatamente blocos de dados de um arquivo especialmente preparado ( carga útil, veja abaixo) em cada seção de um slot inativo. Do ponto de vista da implementação, não faz diferença se baixamos a atualização com antecedência ou a transmitimos imediatamente para o slot.



Os slots têm os seguintes estados:



  • ativo - slot ativo, o sistema será carregado a partir dele na próxima reinicialização;
  • inicializável - a atualização foi flasheada com sucesso no slot, foi validada, as somas hash combinadas, etc.;
  • bem - sucedido - o sistema foi capaz de inicializar com sucesso no novo slot;
  • não inicializável - o slot está danificado. O sistema sempre marca o slot como não inicializável antes de iniciar o processo de atualização.


Ambos os slots podem ser  inicializáveis  e bemsucedidos , mas apenas um está ativo .



O algoritmo do bootloader ao escolher um slot:

imagem

  • O carregador de inicialização detecta que há um ou mais  slots inicializáveis .
  • O slot ativo (ou o slot com a prioridade mais alta) é selecionado a partir deles.
  • Se o sistema for inicializado com sucesso, o slot será marcado como  bem  - sucedidoativo .
  • Caso contrário, o slot será marcado como não inicializável e o sistema será reinicializado.




Alterar os estados do slot durante a atualização:

imagem



Pré-requisitos para Seamless A / B.



boot_control



Para oferecer suporte às atualizações A / B, o fornecedor deve implementar uma interface HAL especial - boot_control . Ele permite que você altere os estados dos slots e obtenha informações sobre eles. Para trabalho externo (por exemplo, via adb shell ), o utilitário é usado - bootctl . A interface é usada como meio de comunicação entre o SO e o bootloader.



update_engine



O principal componente de todo o circuito A / B. Lida com download, atualizações de streaming, verificação de assinaturas e muito mais. Altera os estados do slot por meio de boot_control . Permite que você controle o processo de atualização do dispositivo: pausar, retomar, cancelar.

O componente veio para o Android do ChromeOS, onde já está em uso há algum tempo. AOSP mantém update_engine como um conjunto de sideload estático . Isso é o que é usado na recuperação , porque este modo não suporta links dinâmicos.



O processo deste componente pode ser dividido nas seguintes etapas:



  • carregando a atualização no slot. Você pode baixá-lo de um pacote de atualização baixado anteriormente ou diretamente da Internet via http / https. Durante o download, a assinatura é verificada, a chave pública já está no dispositivo (/system/etc/update_engine/update-payload-key.pub.pem);
  • verificação da atualização baixada e comparação de somas hash;
  • execução de scripts pós-instalação


Estrutura do service pack:



2009-01-01 00:00:00 .....          360          360  META-INF/com/android/metadata
2009-01-01 00:00:00 .....          107          107  care_map.txt
2009-01-01 00:00:00 .....    384690699    384690699  payload.bin
2009-01-01 00:00:00 .....          154          154  payload_properties.txt
2009-01-01 00:00:00 .....         1675          943  META-INF/com/android/otacert


  • care_map.txt - usado por update_verifier (veja abaixo);
  • payload_properties.txt - contém hashes e tamanhos de dados dentro da carga útil ;
  • payload.bin - pacote de atualização, contém blocos de todas as seções, metadados , assinatura.


update_engine_client



Cliente para gerenciar o daemon update_engine . Pode ser chamado diretamente pelo fornecedor para aplicar a atualização.



update_verifier



Utilitário para verificar a integridade do sistema na primeira inicialização (slot com a bandeira  ativa , mas ainda sem  sucesso ). O controle de integridade é implementado usando o módulo de kernel dm-verity . Se a verificação for bem-sucedida, o utilitário marcará o slot atual como  bem-sucedido . Caso contrário, o sistema será reinicializado no slot antigo. Apenas os blocos especificados no arquivo care_map.txt são verificados .



UpdateEngineApi



Existe uma API Java para implementar serviços de atualização de fornecedores . Também há um exemplo dessa implementação de serviço.



Vejamos um exemplo de compilação de atualização A / B no AOSP. Para fazer isso, edite o Makefile da plataforma de destino:



#  A/B
AB_OTA_UPDATER := true
#    :
AB_OTA_PARTITIONS := boot system vendor
#  
PRODUCT_PACKAGES := update_engine update_engine_client update_verifier
#  recovery
TARGET_NO_RECOVERY := true
#,       cache:
#BOARD_CACHEIMAGE_PARTITION_SIZE := ...
#BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE := ...


Depois de chamar make otapackage, obtemos um arquivo zip com a atualização. Neste formulário, ele já é adequado para o modo de sideload . Podemos reiniciar na recuperação e chamar adb sideload ota.zip . Este método é conveniente para depuração.



A aplicação de uma atualização de dentro de um sistema de produção geralmente é específica do fornecedor. A maneira mais fácil é carregar payload.bin em um servidor http e chamar update_engine_client diretamente .



Exemplo de chamada:



update_engine_client \
--payload=http://path/to/payload.bin \
--update \
--headers=" \
FILE_HASH=ozGgyQEddkI5Zax+Wbjo6I/PCR8PEZka9gGd0nWa+oY= \
FILE_SIZE=282344983 \
METADATA_HASH=GLIKfE6KRwylWMHsNadG/Q8iy5f786WTatvMdBlpOPg= \
METADATA_SIZE=26723"


O conteúdo do arquivo payload_properties.txt é passado para o parâmetro headers . No logcat, você pode ver o andamento da atualização. Se você passar na opção --follow , o progresso será duplicado em stdout .



Conclusão



As vantagens do novo mecanismo de atualização são óbvias:



  • a atualização do sistema ocorre em segundo plano, sem interromper o trabalho do usuário. Sim, você ainda precisará reiniciar (para um novo slot), mas será  muito  mais rápido do que reiniciar na recuperação para aplicar a atualização;
  • a probabilidade de um loop de inicialização é minimizada (ninguém está imune a erros na implementação). O processo de atualização pode ser interrompido, não afetará o slot ativo de forma alguma;
  • torna-se possível reverter para a versão anterior do firmware. Mesmo se, por algum motivo, a atualização não foi bem-sucedida, o sistema irá simplesmente reverter para a versão anterior;
  • graças ao streaming, o dispositivo será atualizado mais rápido;
  • dependendo da implementação, você pode excluir completamente o usuário do processo de atualização.


Das desvantagens, eu destacaria dois pontos:



  • A / B OTA torna-se dependente do layout do disco atual, porque a atualização ocorre enquanto o sistema está em execução. Ou seja, torna-se impossível rolar a atualização com as partições alteradas;
  • a relativa complexidade da implementação.


E, no entanto, na minha opinião, os prós superam. A propósito, em nosso dispositivo recentemente anunciado , estamos usando atualizações A / B OTA.



All Articles