O Google já usou OTAs A/B em algum dispositivo?
Sim. O nome de marketing das atualizações A/B é atualizações contínuas. Os smartphones Pixel e Pixel XL
de outubro de 2016 foram enviados com A/B, e todos os Chromebooks usam a mesma
update_engine
implementação de A/B. A implementação necessária do código da plataforma é 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 receber atualizações. As medições das atualizações de segurança mensais mostram que esse recurso já provou ser um sucesso: em maio de 2017, 95% dos proprietários do Pixel estavam usando 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 fazem a atualização mais cedo do que os usuários 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, o Android mantém a capacidade de voltar para a imagem do sistema em funcionamento 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 que contêm bytecode Dalvik portátil. Um arquivo .odex (.dex otimizado) 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 de compilação antecipadas sem precisar esperar que o código seja compilado toda vez 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 com o Android 7.1 e um tamanho total de imagem do sistema de 2628 MiB (2755792836 bytes), o detalhamento dos maiores contribuintes para o tamanho total da imagem do sistema por tipo de arquivo é o 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% |
icu locale data | 27468687 bytes | 0,9% |
Esses números 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 poderí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 ext4 A/B é idêntico ao SquashFS A/B, porque se
usássemos o SquashFS, teríamos enviado os arquivos .odex pré-selecionados no system_a em vez de
system_b.
Copiar arquivos .odex para /data não 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
existem em /data
. Esses apps recebem atualizações do Google Play. Portanto, 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
totalmente e substituídos por arquivos .odex pequenos e orientados por perfil quando o usuário realmente usa cada
app, sem precisar de 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.
A comparação é difícil por alguns motivos importantes:
-
Os apps atualizados pelo Google Play sempre tiveram os arquivos .odex em
/data
assim que receberam 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 saber mais 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. Portanto, os arquivos .odex antigos e novos estão em
/data
ao mesmo tempo.
O código no OtaDexoptService (frameworks/base/+/main/services/core/java/com/android/server/pm/OtaDexoptService.java
) chama getAvailableSpace
antes de otimizar cada pacote para evitar o excesso de
/data
. O termo disponível ainda é conservador: é a quantidade
de espaço restante antes de atingir o limite normal de espaço baixo 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 estiver
próximo de preencher o espaço disponível (conforme descrito), 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 cenário, em que /data
está completamente cheio, a atualização espera 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/+/main/services/core/java/com/android/server/pm/PackageManagerService.java#7215
). Depois que o novo sistema é inicializado, installd
(frameworks/native/+/main/cmds/installd/dexopt.cpp#2422
) pode remover os arquivos .odex que foram 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
. 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
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 é 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 é verdade para dispositivos não A/B. Tradicionalmente, as OTAs completas baseadas em blocos gravavam uma quantidade semelhante de dados, portanto, as taxas de desgaste do flash são semelhantes.
Atualizar duas partições do sistema aumenta o tempo de atualização de fábrica?
Não. O pixel não aumentou no tamanho da imagem do sistema, apenas dividiu o espaço em duas partições.
Não é possível manter os arquivos .odex no B para que a reinicialização após a redefinição para a configuração original seja lenta?
Sim. Se você realmente usou um dispositivo, fez uma OTA e realizou uma redefinição de dados de fábrica, a
primeira reinicialização será mais lenta do que seria (1 minuto e 40 segundos em comparação com 40 segundos em um Pixel XL), porque
os arquivos .odex foram perdidos do B após a primeira OTA e, portanto, não podem ser copiados para
/data
. Essa é a concessão.
A redefinição de dados de fábrica precisa ser uma operação rara em comparação com a inicialização normal, para que o tempo necessário
seja 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ê pensa. Também
é possível marcar apps como aqueles que exigem compilação antecipada usando
coreApp="true"
no manifesto: (frameworks/base/+/main/packages/SystemUI/AndroidManifest.xml#23
). Atualmente, isso é usado por system_server
porque não é permitido usar JIT por
motivos de segurança.
Manter arquivos .odex em /data em vez de /system não torna a reinicialização lenta após um OTA?
Não. Como explicado acima, o novo dex2oat é executado enquanto a imagem antiga do sistema ainda está em execução para gerar os arquivos que serão necessários para o novo sistema. A atualização não será considerada disponível até que esse trabalho seja concluído.
Podemos (devemos) enviar um dispositivo A/B de 32 GB? 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 escolha recomendada em dispositivos com 4GiB, já que a sobrecarga de 320MiB é quase 10% do total de espaço disponível.
O AVB2.0 exige atualizações OTA A/B?
Não. O 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 quebram a proteção de reversão do AVB2.0?
Não. Há alguma confusão aqui porque, se um sistema A/B não inicializar a nova imagem do sistema, ele será revertido automaticamente para a imagem "anterior" (após algumas tentativas determinadas pelo carregador de inicialização). 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 de reversão será ativada e impedirá que você volte. No entanto, até que você inicialize a nova imagem, a proteção de reversão não considera que ela seja a imagem atual do sistema.
Se você estiver instalando uma atualização enquanto o sistema está em execução, isso não é lento?
Com as 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 objetivo é ter o menor impacto possível. Portanto, a atualização é deliberadamente lenta. Através 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 momento em que os usuários não estão usando os dispositivos. A plataforma oferece suporte à pausa/retomada da 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, 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 é a entrada/saída simples. Isso exige poucos recursos (RAM, CPU, E/S), porque apenas copia blocos lentamente.
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, compilar um app grande e complexo exige muito mais trabalho do que um app pequeno e simples. Na fase 1, não há blocos de disco maiores ou mais complexos do que outros.
O processo é semelhante ao Google Play instalando uma atualização de 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 pela atualização?
A implementação atual no GmsCore não distingue as atualizações em segundo plano e as iniciadas pelo usuário, mas isso pode mudar no futuro. No caso em que o usuário pediu explicitamente para que a atualização fosse instalada ou está observando a tela de progresso da atualização, vamos priorizar o trabalho de atualização com a suposição de que ele está aguardando ativamente a conclusão.
O que acontece se houver falha na aplicação de uma atualização?
Com as 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 foi se a falha ocorresse antes mesmo de um aplicativo ser iniciado, por exemplo, porque o pacote não foi verificado. Com as atualizações A/B, a falha na aplicação de uma atualização não afeta o sistema em execução. A atualização pode ser tentada novamente mais tarde.