Perguntas frequentes

O Google já usou OTAs A/B em algum dispositivo?

Sim. O nome de mercado para atualizações A/B é atualizações contínuas. Os smartphones Pixel e Pixel XL telefones de outubro de 2016 foram lançados com A/B, e todos os Chromebooks usam a mesma update_engine implementação de A/B. A implementação de código de plataforma necessária é pública no Android 7.1 e versões mais recentes.

Por que as OTAs A/B são melhores?

As OTAs A/B oferecem uma melhor experiência do usuário ao fazer atualizações. As medições de atualizações de segurança mensais mostram que esse recurso já se mostrou um sucesso: em maio de 2017, 95% dos proprietários de Pixel estavam executando a atualização de segurança mais recente após um mês, em comparação com 87% dos usuários do Nexus, e os usuários do Pixel atualizam mais cedo do que os usuários do Nexus. As falhas ao atualizar blocos durante uma OTA não resultam mais em um dispositivo que não inicializa. Até que a nova imagem do sistema seja inicializada com sucesso, o Android mantém a capacidade de reverter para a imagem do sistema de trabalho anterior.

O que é system_other?

Os aplicativos são armazenados em arquivos .apk, que são arquivos ZIP. Cada arquivo .apk tem dentro dele um ou mais arquivos .dex que contêm bytecode Dalvik portátil. Um arquivo .odex (otimizado .dex) fica separado do arquivo .apk e pode conter código de máquina específico do dispositivo. Se um arquivo .odex estiver disponível, o Android poderá executar aplicativos em velocidades compiladas antecipadamente sem precisar esperar que o código seja compilado sempre que o aplicativo for iniciado. Um arquivo .odex não é estritamente necessário: o Android pode executar o código .dex diretamente por interpretação ou compilação Just-In-Time (JIT), mas um arquivo .odex oferece a melhor combinação de velocidade de inicialização e velocidade de execução se houver espaço disponível.

Exemplo: para o arquivo installed-files.txt de um Nexus 6P executando o Android 7.1 com um tamanho total de imagem do sistema de 2628 MiB (2755792836 bytes), a divisão dos maiores colaboradores para o tamanho geral da imagem do sistema por tipo de arquivo é a seguinte:

.odex 1391770312 bytes 50,5%
.apk 846878259 bytes 30,7%
.so (código C/C++ nativo) 202162479 bytes 7,3%
Arquivos .oat/imagens .art 163892188 bytes 5,9%
Fontes 38952361 bytes 1,4%
Dados de localidade icu 27468687 bytes 0,9%

Esses números também são semelhantes para outros dispositivos. Portanto, em dispositivos Nexus/Pixel, os arquivos .odex ocupam aproximadamente metade da partição do sistema. Isso significa que podemos continuar usando o ext4, mas gravar os arquivos .odex na partição B na fábrica e copiá-los para /data na primeira inicialização. O armazenamento real usado com o ext4 A/B é idêntico ao SquashFS A/B, porque, se nós tivéssemos usado o SquashFS, teríamos enviado os arquivos .odex pré-otimizados no system_a em vez de system_b.

A cópia de arquivos .odex para /data não significa que o espaço salvo em /system seja perdido em /data?

Não exatamente. No Pixel, a maior parte do espaço ocupado por arquivos .odex é para apps, que normalmente existem em /data. Esses apps recebem atualizações do Google Play, então os arquivos .apk e .odex na imagem do sistema não são usados durante a maior parte da vida útil do dispositivo. Esses arquivos podem ser excluídos completamente e substituídos por arquivos .odex pequenos e orientados por perfil quando o usuário usa cada app (portanto, não é necessário espaço para apps que o usuário não usa). Para mais detalhes, consulte a palestra do Google I/O 2016 The Evolution of Art (link em inglês).

A comparação é difícil por alguns motivos principais:

  • Os apps atualizados pelo Google Play sempre tiveram arquivos .odex em /data assim que recebem a primeira atualização.
  • Os apps que o usuário não executa não precisam de um arquivo .odex.
  • A compilação orientada por perfil gera arquivos .odex menores do que a compilação antecipada (porque a primeira otimiza apenas o código crítico de desempenho).

Para detalhes sobre as opções de ajuste disponíveis para OEMs, consulte Como configurar o ART.

Não há duas cópias dos arquivos .odex em /data?

É um pouco mais complicado. Depois que a nova imagem do sistema é gravada, a nova versão do dex2oat é executada nos novos arquivos .dex para gerar os novos arquivos .odex. Isso ocorre enquanto o sistema antigo ainda está em execução, então os arquivos .odex antigos e novos estão em /data ao mesmo tempo.

O código em OtaDexoptService (frameworks/base/+/android17-release/services/core/java/com/android/server/pm/OtaDexoptService.java) chama getAvailableSpace antes de otimizar cada pacote para evitar o preenchimento excessivo de /data. Observe que disponível aqui ainda é conservador: é a quantidade de espaço restante antes de atingir o limite normal de espaço livre do sistema (medido como uma porcentagem e uma contagem de bytes). Portanto, se /data estiver cheio, não haverá duas cópias de cada arquivo .odex. O mesmo código também tem um BULK_DELETE_THRESHOLD: se o dispositivo chegar perto de preencher o espaço disponível (conforme descrito acima), os arquivos .odex pertencentes a apps que não são usados serão removidos. Esse é outro caso sem duas cópias de cada arquivo .odex.

No pior caso, quando /data está completamente cheio, a atualização aguarda até que o dispositivo seja reinicializado no novo sistema e não precise mais dos arquivos .odex do sistema antigo. O PackageManager processa isso: (frameworks/base/+/android17-release/services/core/java/com/android/server/pm/PackageManagerService.java#7215). Depois que o novo sistema for inicializado com sucesso, installd (frameworks/native/+/android17-release/cmds/installd/dexopt.cpp#2422) poderá remover os arquivos .odex usados pelo sistema antigo, retornando o dispositivo ao estado estável em que há apenas uma cópia.

Portanto, embora seja possível que /data contenha duas cópias de todos os arquivos .odex, (a) isso é temporário e (b) só ocorre se você tiver muito espaço livre em /data de qualquer forma. Exceto durante uma atualização, há apenas uma cópia. E, como parte dos recursos gerais de robustez do ART, ele nunca vai preencher /data com arquivos .odex de qualquer forma (porque isso também seria um problema em um sistema não A/B).

Toda essa gravação/cópia não aumenta o desgaste do flash?

Apenas uma pequena parte do flash é regravada: uma atualização completa do sistema Pixel grava cerca de 2,3 GiB. Os apps também são recompilados, mas isso também acontece com não A/B. Tradicionalmente, as OTAs completas baseadas em blocos gravavam uma quantidade semelhante de dados, então as taxas de desgaste do flash deveriam ser semelhantes.

O flashing de duas partições do sistema aumenta o tempo de flashing de fábrica?

Não. O Pixel não aumentou o tamanho da imagem do sistema. Ele apenas dividiu o espaço em duas partições.

Manter arquivos .odex em B não torna a reinicialização após a redefinição de fábrica lenta?

Sim. Se você já usou um dispositivo, fez uma OTA e realizou uma redefinição de fábrica, a primeira reinicialização será mais lenta do que seria de outra forma (1m40s vs 40s em um Pixel XL) porque os arquivos .odex terão sido perdidos de B após a primeira OTA e, portanto, não poderão ser copiados para /data. Essa é a compensação.

A redefinição de fábrica deve ser uma operação rara em comparação com a inicialização normal, então o tempo gasto é menos importante. Isso não afeta usuários ou revisores que recebem o dispositivo da fábrica, porque, nesse caso, a partição B está disponível. O uso do compilador JIT significa que não precisamos recompilar tudo, então não é tão ruim quanto você imagina. Também é possível marcar apps como exigindo compilação antecipada usando coreApp="true" no manifesto: (frameworks/base/+/android17-release/packages/SystemUI/AndroidManifest.xml#23). Isso é usado atualmente pelo system_server porque não é permitido JIT por motivos de segurança.

Manter arquivos .odex em /data em vez de /system não torna a reinicialização após uma OTA lenta?

Não. Conforme explicado acima, o novo dex2oat é executado enquanto a imagem do sistema antigo ainda está em execução para gerar os arquivos que serão necessários para o novo sistema. A atualização não é considerada disponível até que esse trabalho seja concluído.

Podemos (devemos) enviar um dispositivo A/B de 32 GiB? 16GiB? 8GiB?

32 GiB funciona bem, como foi comprovado no Pixel, e 320 MiB de 16 GiB significa uma redução de 2%. Da mesma forma, 320 MiB de 8 GiB é uma redução de 4%. Obviamente, A/B não seria a opção recomendada em dispositivos com 4 GiB, já que a sobrecarga de 320 MiB é quase 10% do espaço total disponível.

O AVB2.0 exige OTAs A/B?

Não. A inicialização verificada do Android sempre exigiu atualizações baseadas em blocos, mas não necessariamente atualizações A/B.

As OTAs A/B exigem o AVB2.0?

Não.

As OTAs A/B interrompem a proteção contra reversão do AVB2.0?

Não. Há alguma confusão aqui porque, se um sistema A/B não conseguir inicializar na nova imagem do sistema ele será revertido automaticamente para a imagem do sistema "anterior" (após um número de novas tentativas determinadas pelo carregador de inicialização). O ponto principal aqui é que "anterior" no sentido A/B ainda é a imagem do sistema "atual". Assim que o dispositivo inicializa uma nova imagem, a proteção contra reversão é ativada e garante que você não possa voltar. Mas, até que você tenha inicializado a nova imagem com sucesso, a proteção contra reversão não a considera a imagem do sistema atual.

Se você estiver instalando uma atualização enquanto o sistema está em execução, isso não é lento?

Com atualizações não A/B, o objetivo é instalar a atualização o mais rápido possível, porque o usuário está esperando e não pode usar o dispositivo enquanto a atualização é aplicada. Com as atualizações A/B, o oposto é verdadeiro. Como o usuário ainda está usando o dispositivo, o menor impacto possível é o objetivo, então a atualização é propositalmente lenta. Por meio da lógica no cliente de atualização do sistema Java (que para o Google é o GmsCore, o pacote principal fornecido pelo GMS), o Android também tenta escolher um horário em que os usuários não estejam usando os dispositivos. A plataforma oferece suporte para pausar/retomar a atualização, e o cliente pode usar isso para pausar a atualização se o usuário começar a usar o dispositivo e retomá-la quando o dispositivo estiver inativo novamente.

Há duas fases ao fazer uma OTA, mostradas claramente na interface como Etapa 1 de 2 e Etapa 2 de 2 na barra de progresso. A etapa 1 corresponde à gravação dos blocos de dados, enquanto a etapa 2 é a pré-compilação dos arquivos .dex. Essas duas fases são bastante diferentes em termos de impacto no desempenho. A primeira fase é uma E/S simples. Isso exige poucos recursos (RAM, CPU, E/S) porque é apenas uma cópia lenta de blocos.

A segunda fase executa o dex2oat para pré-compilar a nova imagem do sistema. Obviamente, isso tem limites menos claros nos requisitos, porque compila apps reais. E, obviamente, há muito mais trabalho envolvido na compilação de um app grande e complexo do que um app pequeno e simples. Já na fase 1, não há blocos de disco maiores ou mais complexos do que outros.

O processo é semelhante a quando o Google Play instala uma atualização do app em segundo plano antes de mostrar a notificação 5 apps atualizados, como tem sido feito há anos.

E se um usuário estiver esperando a atualização?

A implementação atual no GmsCore não distingue entre atualizações em segundo plano e atualizações iniciadas pelo usuário, mas pode fazer isso no futuro. No caso em que o usuário pediu explicitamente que a atualização fosse instalada ou está assistindo à tela de progresso da atualização, vamos priorizar o trabalho de atualização, supondo que ele esteja esperando ativamente que ela termine.

O que acontece se houver uma falha ao aplicar uma atualização?

Com atualizações não A/B, se uma atualização falhasse, o usuário geralmente ficaria com um dispositivo inutilizável. A única exceção era se a falha ocorresse antes que um aplicativo fosse iniciado (porque o pacote não foi verificado, por exemplo). Com as atualizações A/B, uma falha ao aplicar uma atualização não afeta o sistema em execução. A atualização pode ser repetida mais tarde.