Perguntas frequentes

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

Sim. O nome de marketing para atualizações A/B é atualizações simples. Os smartphones Pixel e Pixel XL de outubro de 2016 foram enviados com A/B, e todos os Chromebooks usam a mesma implementação update_engine de A/B. A implementação do código de plataforma necessário é 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 experiência melhor ao usuário ao fazer atualizações. As medições das atualizações de segurança mensais mostram que esse recurso já é 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. Além disso, os usuários do Pixel atualizam mais rápido do que os do Nexus. As falhas na atualização de 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 voltar para a imagem do sistema funcional anterior.

O que é system_other?

Os aplicativos são armazenados em arquivos .apk, que são arquivos ZIP. Cada arquivo .apk tem um ou mais arquivos .dex com bytecode Dalvik portátil. Um arquivo .odex (optimized .dex) fica separado do arquivo .apk e pode conter código de máquina específico para o 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 a cada vez que o aplicativo é 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 de tempo de execução se houver espaço disponível.

Exemplo: para o arquivo installed-files.txt de um Nexus 6P com Android 7.1 e um tamanho total de imagem do sistema de 2628 MiB (2755792836 bytes), a divisão dos maiores contribuintes 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 da ICU 27468687 bytes 0,9%

Esses números são semelhantes para outros dispositivos também. Portanto, em dispositivos Nexus/Pixel, os arquivos .odex ocupam aproximadamente metade da partição do sistema. Isso significava que podíamos 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. Isso porque, se tivéssemos usado o SquashFS, teríamos enviado os arquivos .odex pré-otimizados em system_a em vez de system_b.

Copiar arquivos .odex para /data significa que o espaço salvo em /system é perdido em /data?

Não exatamente. No Pixel, a maior parte do espaço ocupado por arquivos .odex é para apps, que geralmente estão 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 totalmente excluídos e substituídos por arquivos .odex pequenos e orientados por perfil quando o usuário usa cada app (não exigindo espaço para apps que não são usados). Para mais detalhes, consulte a palestra do Google I/O 2016 The Evolution of Art (em inglês).

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

  • Os apps atualizados pelo Google Play sempre tiveram os 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 para o 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 acontece enquanto o sistema antigo ainda está em execução. Assim, os arquivos .odex antigos e novos ficam em /data ao mesmo tempo.

O código em OtaDexoptService (frameworks/base/+/android16-qpr1-release/services/core/java/com/android/server/pm/OtaDexoptService.java) chama getAvailableSpace antes de otimizar cada pacote para evitar o preenchimento excessivo de /data. A quantidade disponível aqui ainda é conservadora: é o espaço restante antes de atingir o limite mínimo usual do sistema (medido como uma porcentagem e uma contagem de bytes). Assim, 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 (como 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, em que /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/+/android16-qpr1-release/services/core/java/com/android/server/pm/PackageManagerService.java#7215). Depois que o novo sistema é inicializado com êxito, installd (frameworks/native/+/android16-qpr1-release/cmds/installd/dexopt.cpp#2422) pode 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ó acontece se você já tinha muito espaço livre em /data. Exceto durante uma atualização, há apenas uma cópia. Como parte dos recursos gerais de robustez do ART, ele nunca preencherá /data com arquivos .odex, já que isso também seria um problema em um sistema não A/B.

Toda essa gravação/cópia não aumenta o desgaste da memória flash?

Apenas uma pequena parte da memória flash é reescrita: 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 dispositivos 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 devem ser semelhantes.

Fazer o flash de duas partições do sistema aumenta o tempo de flash de fábrica?

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

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

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

A redefinição de dados de fábrica deve ser uma operação rara em comparação com a inicialização regular, 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/+/android16-qpr1-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 um OTA lenta?

Não. Como explicado acima, o novo dex2oat é executado enquanto a imagem do sistema antigo ainda está em execução para gerar os arquivos 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 funcionam bem, como foi comprovado no Pixel, e 320 MiB de 16 GiB significam uma redução de 2%. Da mesma forma, 320 MiB de 8 GiB é uma redução de 4%. Obviamente, o 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 AVB2.0?

Não.

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

Não. Há uma confusão aqui porque, se um sistema A/B não conseguir inicializar a nova imagem do sistema, ele vai (após algumas tentativas determinadas pelo carregador de inicialização) reverter automaticamente para a imagem do sistema "anterior". O ponto principal aqui é que "anterior" no sentido de A/B ainda é a imagem do sistema "atual". Assim que o dispositivo inicializar uma nova imagem, a proteção contra rollback será ativada e vai garantir que você não possa voltar. Mas até que você inicialize a nova imagem, a proteção contra rollback 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 vai ser 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 contrário é verdadeiro. Como o usuário ainda está usando o dispositivo, o objetivo é causar o menor impacto possível, por isso, a atualização é propositalmente lenta. Usando a 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 permite 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 retomar quando ele estiver ocioso 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, e a etapa 2 é a pré-compilação dos arquivos .dex. Essas duas fases são bem diferentes em termos de impacto na performance. A primeira fase é a 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. Obviamente, há muito mais trabalho envolvido na compilação de um app grande e complexo do que em 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 de app em segundo plano antes de mostrar a notificação 5 apps atualizados, como já é 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 iniciadas pelo usuário, mas isso pode mudar no futuro. Se o usuário tiver pedido explicitamente a instalação da atualização ou estiver assistindo à tela de progresso, vamos priorizar o trabalho de atualização presumindo que ele está esperando ativamente que ela termine.

O que acontece se uma atualização não for aplicada?

Com atualizações não A/B, se uma atualização não fosse aplicada, o usuário geralmente ficava com um dispositivo inutilizável. A única exceção era se a falha ocorresse antes mesmo de um aplicativo ser 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.