O Google está comprometido em promover a equidade racial para as comunidades negras. Veja como.
Esta página foi traduzida pela API Cloud Translation.
Switch to English

Atualizações do sistema A / B (sem costura)

As atualizações do sistema A / B, também conhecidas como atualizações contínuas, garantem que um sistema de inicialização viável permaneça no disco durante uma atualização over-the-air (OTA) . Essa abordagem reduz a probabilidade de um dispositivo inativo após uma atualização, o que significa menos substituições e atualizações de dispositivos nos centros de reparo e garantia. Outros sistemas operacionais de nível comercial, como o ChromeOS, também usam as atualizações A / B com êxito.

Para obter mais informações sobre atualizações do sistema A / B e como elas funcionam, consulte Seleção de partição (slots) .

As atualizações do sistema A / B oferecem os seguintes benefícios:

  • As atualizações do OTA podem ocorrer enquanto o sistema está em execução, sem interromper o usuário. Os usuários podem continuar usando seus dispositivos durante uma OTA - o único tempo de inatividade durante uma atualização é quando o dispositivo é reinicializado na partição de disco atualizada.
  • Após uma atualização, a reinicialização não leva mais que uma reinicialização regular.
  • Se um OTA falhar na aplicação (por exemplo, devido a um flash incorreto), o usuário não será afetado. O usuário continuará executando o sistema operacional antigo e o cliente estará livre para tentar novamente a atualização.
  • Se uma atualização OTA for aplicada, mas falhar na inicialização, o dispositivo será reiniciado novamente na partição antiga e permanecerá utilizável. O cliente é livre para tentar novamente a atualização.
  • Quaisquer erros (como erros de E / S) afetam apenas o conjunto de partições não utilizadas e podem ser tentados novamente. Esses erros também se tornam menos prováveis ​​porque a carga de E / S é deliberadamente baixa para evitar degradar a experiência do usuário.
  • As atualizações podem ser transmitidas para dispositivos A / B, eliminando a necessidade de baixar o pacote antes de instalá-lo. Streaming significa que não é necessário que o usuário tenha espaço livre suficiente para armazenar o pacote de atualização em /data ou /cache .
  • A partição de cache não é mais usada para armazenar pacotes de atualização OTA, portanto, não há necessidade de garantir que a partição de cache seja grande o suficiente para futuras atualizações.
  • O dm-verity garante que um dispositivo inicialize uma imagem não corrompida. Se um dispositivo não inicializar devido a um problema incorreto de OTA ou dm-verity, o dispositivo pode reiniciar em uma imagem antiga. (A Inicialização verificada do Android não requer atualizações A / B.)

Sobre atualizações do sistema A / B

As atualizações A / B exigem alterações no cliente e no sistema. O servidor de pacotes OTA, no entanto, não deve exigir alterações: os pacotes de atualização ainda são servidos por HTTPS. Para dispositivos que usam a infraestrutura OTA do Google, as alterações do sistema estão todas no AOSP e o código do cliente é fornecido pelos serviços do Google Play. Os OEMs que não usam a infraestrutura OTA do Google poderão reutilizar o código do sistema AOSP, mas precisarão fornecer seu próprio cliente.

Para OEMs que fornecem seu próprio cliente, o cliente precisa:

  • Decida quando fazer uma atualização. Como as atualizações A / B acontecem em segundo plano, elas não são mais iniciadas pelo usuário. Para evitar perturbar os usuários, é recomendável que as atualizações sejam agendadas quando o dispositivo estiver no modo de manutenção ociosa, como durante a noite e no Wi-Fi. No entanto, seu cliente pode usar qualquer heurística que desejar.
  • Verifique com seus servidores de pacotes OTA e determine se uma atualização está disponível. Deve ser basicamente o mesmo que o código do cliente existente, exceto que você deseja sinalizar que o dispositivo suporta A / B. (O cliente do Google também inclui um botão Verificar agora para os usuários verificarem a atualização mais recente.)
  • Ligue para update_engine com a URL HTTPS do seu pacote de atualização, supondo que um esteja disponível. update_engine atualizará os blocos brutos na partição não usada no momento, enquanto transmite o pacote de atualização.
  • Relate sucessos ou falhas de instalação em seus servidores, com base no código de resultado update_engine . Se a atualização for aplicada com êxito, o update_engine instruirá o carregador de inicialização a inicializar no novo sistema operacional na próxima reinicialização. O carregador de inicialização retornará ao sistema operacional antigo se o novo sistema operacional falhar na inicialização, portanto, nenhum trabalho será necessário do cliente. Se a atualização falhar, o cliente precisará decidir quando (e se) tentar novamente, com base no código de erro detalhado. Por exemplo, um bom cliente pode reconhecer que um pacote OTA parcial ("diff") falha e tentar um pacote OTA completo.

Opcionalmente, o cliente pode:

  • Mostrar uma notificação solicitando ao usuário que reinicie. Se você deseja implementar uma política em que o usuário é incentivado a atualizar rotineiramente, essa notificação pode ser adicionada ao seu cliente. Se o cliente não solicitar aos usuários, os usuários receberão a atualização na próxima vez em que forem reiniciados. (O cliente do Google tem um atraso configurável por atualização.)
  • Mostre uma notificação informando aos usuários se eles inicializaram em uma nova versão do sistema operacional ou se era esperado que eles o fizessem, mas voltaram à versão antiga do sistema operacional. (O cliente do Google normalmente não faz isso).

No lado do sistema, as atualizações do sistema A / B afetam o seguinte:

  • Seleção de partição (slots), o daemon update_engine e interações do carregador de inicialização (descritas abaixo)
  • Processo de criação e geração de pacotes de atualização OTA (descritos em Implementando atualizações A / B )

Seleção de partição (slots)

As atualizações do sistema A / B usam dois conjuntos de partições denominadas slots (normalmente slot A e slot B). O sistema é executado a partir do slot atual , enquanto as partições no slot não utilizado não são acessadas pelo sistema em execução durante a operação normal. Essa abordagem torna as atualizações resistentes a falhas, mantendo o slot não utilizado como substituto: se ocorrer um erro durante ou imediatamente após uma atualização, o sistema poderá reverter para o slot antigo e continuar com um sistema em funcionamento. Para atingir esse objetivo, nenhuma partição usada pelo slot atual deve ser atualizada como parte da atualização do OTA (incluindo partições para as quais há apenas uma cópia).

Cada slot possui um atributo inicializável que indica se o slot contém um sistema correto a partir do qual o dispositivo pode inicializar. O slot atual é inicializável quando o sistema está em execução, mas o outro slot pode ter uma versão antiga (ainda correta) do sistema, uma versão mais recente ou dados inválidos. Independentemente de qual seja o slot atual , existe um slot que é o slot ativo (aquele que o gerenciador de inicialização inicializará na próxima inicialização) ou o slot preferido .

Cada slot também possui um atributo bem - sucedido definido pelo espaço do usuário, que é relevante apenas se o slot também for inicializável. Um slot bem-sucedido deve poder inicializar, executar e atualizar-se. Um slot inicializável que não foi marcado como bem-sucedido (após várias tentativas de inicialização) deve ser marcado como não inicializável pelo carregador de inicialização, incluindo a alteração do slot ativo para outro slot inicializável (normalmente para o slot em execução imediatamente antes da tentativa de inicialização) para o novo e ativo). Os detalhes específicos da interface são definidos em boot_control.h .

Atualizar mecanismo daemon

As atualizações do sistema A / B usam um daemon em segundo plano chamado update_engine para preparar o sistema para inicializar em uma nova versão atualizada. Este daemon pode executar as seguintes ações:

  • Leia as partições A / B do slot atual e grave quaisquer dados nas partições A / B do slot não utilizadas, conforme as instruções do pacote OTA.
  • Chame a interface boot_control em um fluxo de trabalho predefinido.
  • Execute um programa pós-instalação a partir da nova partição depois de gravar todas as partições de slot não utilizadas, conforme as instruções do pacote OTA. (Para detalhes, consulte Pós-instalação ).

Como o daemon update_engine não está envolvido no processo de inicialização, ele é limitado no que pode fazer durante uma atualização pelas políticas e recursos do SELinux no slot atual (essas políticas e recursos não podem ser atualizados até que o sistema inicialize em um nova versão). Para manter um sistema robusto, o processo de atualização não deve modificar a tabela de partições, o conteúdo das partições no slot atual ou o conteúdo das partições não A / B que não podem ser limpas com uma redefinição de fábrica.

Atualizar fonte do mecanismo

A fonte update_engine está localizada em system/update_engine . Os arquivos dexopt do A / B OTA são divididos entre installd e um gerenciador de pacotes:

Para um exemplo de trabalho, consulte /device/google/marlin/device-common.mk .

Atualizar logs do mecanismo

Para versões do Android 8.x e versões anteriores, os logs update_engine podem ser encontrados no logcat e no relatório de erros. Para disponibilizar os logs update_engine no sistema de arquivos, corrija as seguintes alterações na sua compilação:

Essas alterações salvam uma cópia do log update_engine mais recente em /data/misc/update_engine_log/update_engine. YEAR - TIME . Além do log atual, os cinco logs mais recentes são salvos em /data/misc/update_engine_log/ . Usuários com o ID do grupo de logs poderão acessar os logs do sistema de arquivos.

Interações do carregador de inicialização

O HAL boot_control é usado pelo update_engine (e possivelmente por outros daemons) para instruir o gerenciador de inicialização do que inicializar. Cenários de exemplo comuns e seus estados associados incluem o seguinte:

  • Caso normal : O sistema está sendo executado a partir do slot atual, slot A ou B. Nenhuma atualização foi aplicada até o momento. O slot atual do sistema é inicializável, bem-sucedido e o slot ativo.
  • Atualização em andamento : o sistema está sendo executado no slot B, portanto, o slot B é o slot inicializável, bem-sucedido e ativo. O slot A foi marcado como não inicializável, pois o conteúdo do slot A está sendo atualizado, mas ainda não foi concluído. Uma reinicialização nesse estado deve continuar inicializando no slot B.
  • Atualização aplicada, reinicialização pendente : O sistema está sendo executado no slot B, o slot B é inicializável e bem-sucedido, mas o slot A foi marcado como ativo (e, portanto, marcado como inicializável). O slot A ainda não está marcado como bem-sucedido e algumas tentativas de inicialização do slot A devem ser feitas pelo gerenciador de inicialização.
  • Sistema reinicializado em nova atualização : O sistema está sendo executado no slot A pela primeira vez, o slot B ainda é inicializável e bem-sucedido, enquanto o slot A é inicializável apenas e continua ativo, mas não é bem-sucedido. Um daemon de espaço do usuário, update_verifier , deve marcar o slot A como bem-sucedido após algumas verificações.

Suporte para atualização de streaming

Os dispositivos do usuário nem sempre têm espaço /data suficientes para baixar o pacote de atualização. Como nem os OEMs nem os usuários desejam desperdiçar espaço em uma partição /cache , alguns usuários ficam sem atualizações porque o dispositivo não tem onde armazenar o pacote de atualização. Para resolver esse problema, o Android 8.0 adicionou suporte para atualizações A / B de streaming que gravam blocos diretamente na partição B durante o download, sem a necessidade de armazenar os blocos em /data . As atualizações A / B de streaming quase não precisam de armazenamento temporário e exigem armazenamento suficiente para aproximadamente 100 KiB de metadados.

Para habilitar atualizações de streaming no Android 7.1, escolha os seguintes patches:

Esses patches são necessários para oferecer suporte ao streaming de atualizações A / B no Android 7.1 e posterior, usando o Google Mobile Services (GMS) ou qualquer outro cliente de atualização.

Vida útil de uma atualização A / B

O processo de atualização é iniciado quando um pacote OTA (referido no código como carga útil ) está disponível para download. As políticas no dispositivo podem adiar o download e o aplicativo da carga útil com base no nível da bateria, na atividade do usuário, no status de carregamento ou em outras políticas. Além disso, como a atualização é executada em segundo plano, os usuários podem não saber que uma atualização está em andamento. Tudo isso significa que o processo de atualização pode ser interrompido a qualquer momento devido a políticas, reinicializações inesperadas ou ações do usuário.

Opcionalmente, os metadados no próprio pacote OTA indicam que a atualização pode ser transmitida; o mesmo pacote também pode ser usado para instalação sem streaming. O servidor pode usar os metadados para informar ao cliente que está sendo transmitido para que o cliente entregue o OTA para update_engine corretamente. Os fabricantes de dispositivos com seu próprio servidor e cliente podem habilitar atualizações de streaming, garantindo que o servidor identifique que a atualização está sendo transmitida (ou assume que todas as atualizações estão sendo transmitidas) e que o cliente faça a chamada correta para update_engine para transmissão. Os fabricantes podem usar o fato de o pacote ser da variante de streaming para enviar um sinalizador ao cliente para acionar a transferência para o lado da estrutura como streaming.

Depois que uma carga útil está disponível, o processo de atualização é o seguinte:

Degrau Atividades
1 O slot atual (ou "slot de origem") é marcado como bem-sucedido (se ainda não estiver marcado) com markBootSuccessful() .
2 O slot não utilizado (ou "slot de destino") é marcado como não inicializável, chamando a função setSlotAsUnbootable() . O slot atual sempre é marcado como bem-sucedido no início da atualização para impedir que o carregador de inicialização volte ao slot não utilizado, que em breve terá dados inválidos. Se o sistema atingiu o ponto em que pode começar a aplicar uma atualização, o slot atual é marcado como bem-sucedido, mesmo que outros componentes principais sejam quebrados (como a interface do usuário em um loop de falha), pois é possível enviar um novo software para corrigi-los. problemas

A carga útil da atualização é um blob opaco com as instruções para atualizar para a nova versão. A carga útil da atualização consiste no seguinte:
  • Metadados . Uma parte relativamente pequena da carga útil da atualização, os metadados contém uma lista de operações para produzir e verificar a nova versão no slot de destino. Por exemplo, uma operação pode descomprimir um determinado blob e gravá-lo em blocos específicos em uma partição de destino ou ler a partir de uma partição de origem, aplicar um patch binário e gravar em certos blocos em uma partição de destino.
  • Dados extras . Como a maior parte da carga útil da atualização, os dados extras associados às operações consistem no blob compactado ou no patch binário desses exemplos.
3 Os metadados da carga útil são baixados.
4 Para cada operação definida nos metadados, na ordem, os dados associados (se houver) são baixados na memória, a operação é aplicada e a memória associada é descartada.
5 As partições inteiras são relidas e verificadas com relação ao hash esperado.
6 A etapa pós-instalação (se houver) é executada. No caso de um erro durante a execução de qualquer etapa, a atualização falha e é tentada novamente com uma carga útil possivelmente diferente. Se todas as etapas até agora foram bem-sucedidas, a atualização será bem-sucedida e a última etapa será executada.
7 O slot não utilizado é marcado como ativo, chamando setActiveBootSlot() . Marcar o slot não usado como ativo não significa que ele será inicializado. O carregador de inicialização (ou o próprio sistema) pode retornar o slot ativo se não ler um estado bem-sucedido.
8 A pós-instalação (descrita abaixo) envolve a execução de um programa a partir da versão "nova atualização" enquanto ainda está sendo executado na versão antiga. Se definido no pacote OTA, esta etapa é obrigatória e o programa deve retornar com o código de saída 0 ; caso contrário, a atualização falhará.
9 Após o sistema inicializar com êxito o suficiente no novo slot e concluir as verificações pós-reinicialização, o slot agora atual (anteriormente o "slot de destino") é marcado como bem-sucedido chamando markBootSuccessful() .

Pós-instalação

Para cada partição em que uma etapa de pós-instalação é definida, o update_engine monta a nova partição em um local específico e executa o programa especificado no OTA em relação à partição montada. Por exemplo, se o programa pós-instalação for definido como usr/bin/postinstall na partição do sistema, essa partição do slot não utilizado será montada em um local fixo (como /postinstall_mount ) e /postinstall_mount/usr/bin/postinstall comando /postinstall_mount/usr/bin/postinstall é executado.

Para que a pós-instalação seja bem-sucedida, o kernel antigo deve ser capaz de:

  • Monte o novo formato do sistema de arquivos . O tipo de sistema de arquivos não pode mudar, a menos que haja suporte para ele no kernel antigo, incluindo detalhes como o algoritmo de compactação usado ao usar um sistema de arquivos compactado (por exemplo, SquashFS).
  • Entenda o formato do programa pós-instalação da nova partição . Se estiver usando um binário Executable and Linkable Format (ELF), ele deve ser compatível com o kernel antigo (por exemplo, um novo programa de 64 bits em execução em um kernel antigo de 32 bits, se a arquitetura alternar entre compilações de 32 e 64 bits). A menos que o carregador ( ld ) seja instruído a usar outros caminhos ou criar um binário estático, as bibliotecas serão carregadas a partir da imagem antiga do sistema e não da nova.

Por exemplo, você pode usar um script de shell como um programa de pós-instalação interpretado pelo binário do shell do sistema antigo com um #! marcador na parte superior) e configure os caminhos da biblioteca a partir do novo ambiente para executar um programa de pós-instalação binário mais complexo. Como alternativa, você pode executar a etapa pós-instalação a partir de uma partição menor dedicada para permitir que o formato do sistema de arquivos na partição principal do sistema seja atualizado sem incorrer em problemas de compatibilidade com versões anteriores ou em atualizações de etapa; isso permitiria que os usuários atualizassem diretamente para a versão mais recente a partir de uma imagem de fábrica.

O novo programa pós-instalação é limitado pelas políticas do SELinux definidas no sistema antigo. Como tal, a etapa pós-instalação é adequada para executar tarefas exigidas pelo projeto em um determinado dispositivo ou outras tarefas de melhor esforço (ou seja, atualizar o firmware ou o carregador de inicialização compatível com A / B, preparar cópias de bancos de dados para a nova versão etc.) ) A etapa pós-instalação não é adequada para correções pontuais de erros antes da reinicialização, que requerem permissões imprevistas.

Os selecionados dirige o programa Postinstall no postinstall contexto SELinux. Todos os arquivos na nova partição montada serão marcados com postinstall_file , independentemente de quais são seus atributos após a reinicialização no novo sistema. Alterações nos atributos do SELinux no novo sistema não afetarão a etapa pós-instalação. Se o programa pós-instalação precisar de permissões extras, elas deverão ser adicionadas ao contexto pós-instalação.

Após a reinicialização

Após a reinicialização, o update_verifier aciona a verificação de integridade usando o dm-verity. Essa verificação é iniciada antes do zygote para evitar que os serviços Java façam alterações irreversíveis que impediriam uma reversão segura. Durante esse processo, o carregador de inicialização e o kernel também podem acionar uma reinicialização se a inicialização verificada ou o dm-verity detectar qualquer corrupção. Depois que a verificação é concluída, update_verifier marca a inicialização com êxito.

update_verifier lerá apenas os blocos listados em /data/ota_package/care_map.txt , incluído em um pacote A / B OTA ao usar o código AOSP. O cliente de atualização do sistema Java, como GmsCore, extrai care_map.txt , configura a permissão de acesso antes de reiniciar o dispositivo e exclui o arquivo extraído depois que o sistema é inicializado com êxito na nova versão.