Esta página descreve as alterações adicionadas ao AOSP para reduzir alterações desnecessárias de arquivos entre compilações. Os implementadores de dispositivos que mantêm seus próprios sistemas de compilação podem usar essas informações como um guia para reduzir o tamanho de suas atualizações over-the-air (OTA).
As atualizações do Android OTA ocasionalmente contêm arquivos alterados que não correspondem às alterações de código. Na verdade, eles são artefatos de sistema de construção. Isso pode ocorrer quando o mesmo código, criado em momentos diferentes, a partir de diretórios diferentes ou em máquinas diferentes, produz um grande número de arquivos alterados. Esses arquivos em excesso aumentam o tamanho de um patch OTA e tornam difícil determinar qual código foi alterado.
Para tornar o conteúdo de um OTA mais transparente, o AOSP inclui alterações no sistema de construção projetadas para reduzir o tamanho dos patches OTA. As alterações desnecessárias de arquivos entre as compilações foram eliminadas e apenas os arquivos relacionados ao patch estão contidos nas atualizações OTA. O AOSP também inclui uma ferramenta de comparação de compilação , que filtra as alterações comuns de arquivos relacionados à compilação para fornecer uma comparação de arquivo de compilação mais limpa, e uma ferramenta de mapeamento de bloco , que ajuda a manter a alocação de blocos consistente.
Um sistema de construção pode criar patches desnecessariamente grandes de várias maneiras. Para atenuar isso, no Android 8.0 e superior, novos recursos foram implementados para reduzir o tamanho do patch para cada diff de arquivo. As melhorias que reduziram os tamanhos dos pacotes de atualização OTA incluem o seguinte:
- Uso de Brotli , um algoritmo de compressão sem perdas de propósito genérico para imagens completas em atualizações de dispositivos não A / B. O Brotli pode ser personalizado para otimizar a compressão. Em atualizações maiores compostas por dois ou mais blocos no sistema de arquivos (por exemplo,
system.img
), os fabricantes de dispositivos ou parceiros podem adicionar seus próprios algoritmos de compactação e podem usar diferentes algoritmos de compactação em diferentes blocos da mesma atualização. - Uso de recompressão Puffin , uma ferramenta de patch determinística para deflate streams, que lida com as funções de compressão e diff para geração de atualização A / B OTA.
- Mudanças no uso da ferramenta de geração delta, por exemplo, como a biblioteca
bsdiff
é usada para compactar patches. No Android 9 e superior, a ferramentabsdiff
seleciona o algoritmo de compressão que daria os melhores resultados de compressão para um patch. - As melhorias no
update_engine
resultaram em menos memória consumida quando os patches são aplicados para atualizações de dispositivos A / B. - Melhorias na divisão de grandes arquivos zip para atualizações OTA baseadas em bloco. Um modo em
imgdiff
divide arquivos APK grandes, com base nos nomes de entrada. Isso produz um patch menor em comparação com a divisão de arquivos linearmente e o uso da ferramentabsdiff
para compactá-los.
As seções a seguir discutem vários problemas que afetam os tamanhos de atualização OTA, suas soluções e exemplos de implementação no AOSP.
Ordem de arquivo
Problema : os sistemas de arquivos não garantem a ordem dos arquivos quando solicitados por uma lista de arquivos em um diretório, embora geralmente seja o mesmo para o mesmo check-out. Ferramentas como ls
classificam os resultados por padrão, mas a função curinga usada por comandos como find
e make
não classifica. Antes de usar essas ferramentas, você deve classificar as saídas.
Solução : ao usar ferramentas como find
e make
com a função curinga, classifique a saída desses comandos antes de usá-los. Ao usar $(wildcard)
ou $(shell find)
em arquivos Android.mk
, classifique-os também. Algumas ferramentas, como Java, classificam as entradas, portanto, antes de classificar os arquivos, verifique se a ferramenta que você está usando ainda não o fez.
Exemplos: Muitas instâncias foram corrigidas no sistema de construção central usando a macro embutida all-*-files-under
, que inclui all-cpp-files-under
(já que várias definições foram espalhadas em outros makefiles). Para obter detalhes, consulte o seguinte:
- https://android.googlesource.com/platform/build/+/4d66adfd0e6d599d8502007e4ea9aaf82e95569f
- https://android.googlesource.com/platform/build/+/379f9f9cec4fe1c66b6d60a6c19fecb81b9eb410
- https://android.googlesource.com/platform/build/+/7c3e3f8314eec2c053012dd97d2ae649ebeb5653
- https://android.googlesource.com/platform/build/+/5c64b4e81c1331cab56d8a8c201f26bb263b630c
Diretório de construção
Problema: Alterar o diretório no qual as coisas são construídas pode fazer com que os binários sejam diferentes. A maioria dos caminhos na construção do Android são caminhos relativos, portanto __FILE__
em C / C ++ não é um problema. No entanto, os símbolos de depuração codificam o nome do caminho completo por padrão, e o .note.gnu.build-id
é gerado a partir do hash do binário pré-removido, portanto, ele mudará se os símbolos de depuração mudarem.
Solução: o AOSP agora torna os caminhos de depuração relativos. Para obter detalhes, consulte CL: https://android.googlesource.com/platform/build/+/6a66a887baadc9eb3d0d60e26f748b8453e27a02 .
Timestamps
Problema: os registros de data e hora na saída do build resultam em alterações desnecessárias de arquivo. É provável que isso aconteça nos seguintes locais:
-
__DATE__/__TIME__/__TIMESTAMP__
macros__DATE__/__TIME__/__TIMESTAMP__
em código C ou C ++. - Timestamps incorporados em arquivos baseados em zip.
Soluções / exemplos: Para remover carimbos de data / hora da saída da compilação, use as instruções fornecidas abaixo em __DATE __ / __ TIME __ / __ TIMESTAMP__ em C / C ++. e carimbos de data / hora incorporados em arquivos .
__DATA __ / __ HORA __ / __ TIMESTAMP__ em C / C ++
Essas macros sempre produzem resultados diferentes para compilações diferentes, portanto, não as use. Aqui estão algumas opções para eliminar essas macros:
- Remova eles. Para obter um exemplo, consulte https://android.googlesource.com/platform/system/core/+/30622bbb209db187f6851e4cf0cdaa147c2fca9f .
- Para identificar exclusivamente o binário em execução, leia a id de construção do cabeçalho ELF.
- Para saber quando o sistema operacional foi construído, leia o
ro.build.date
(isso funciona para tudo, exceto compilações incrementais, que podem não atualizar esta data). Para obter um exemplo, consulte https://android.googlesource.com/platform/external/libchrome/+/8b7977eccc94f6b3a3896cd13b4aeacbfa1e0f84 .
Carimbos de data e hora incorporados em arquivos (zip, jar)
O Android 7.0 corrigiu o problema de carimbos de data / hora incorporados em arquivos zip adicionando -X
a todos os usos do comando zip
. Isso removeu o UID / GID do construtor e o carimbo de data / hora Unix estendido do arquivo zip.
Uma nova ferramenta, ziptime
(localizada em /platform/build/+/master/tools/ziptime/
) redefine os timestamps normais nos cabeçalhos zip. Para obter detalhes, consulte o arquivo README .
A ferramenta signapk
define carimbos de data / hora para os arquivos APK que podem variar dependendo do fuso horário do servidor. Para obter detalhes, consulte o CL https://android.googlesource.com/platform/build/+/6c41036bcf35fe39162b50d27533f0f3bfab3028 .
Strings de versão
Problema: as strings da versão do APK geralmente tinham BUILD_NUMBER
anexado às versões codificadas. Mesmo se nada mais fosse alterado em um APK, como resultado, o APK ainda seria diferente.
Solução: remova o número da compilação da string da versão do APK.
Solução: remova o número da compilação da string da versão do APK.
Exemplos:
- https://android.googlesource.com/platform/packages/apps/Camera2/+/5e0f4cf699a4c7c95e2c38ae3babe6f20c258d27
- https://android.googlesource.com/platform/build/+/d75d893da8f97a5c7781142aaa7a16cf1dbb669c
Habilitar computação verity no dispositivo
Se dm-verity estiver habilitado em seu dispositivo, então as ferramentas OTA automaticamente pegam sua configuração verity e habilitam a computação verity no dispositivo. Isso permite que os blocos verity sejam calculados em dispositivos Android, em vez de serem armazenados como bytes brutos em seu pacote OTA. Os blocos Verity podem usar aproximadamente 16 MB para uma partição de 2 GB.
No entanto, computar o verity no dispositivo pode levar muito tempo. Especificamente, o código de correção de erro de encaminhamento pode levar muito tempo. Em dispositivos de pixel, costuma demorar até 10 minutos. Em dispositivos simples, pode demorar mais. Se você deseja desabilitar a computação verity no dispositivo, mas ainda habilitar dm-verity, você pode fazer isso passando --disable_fec_computation
para a ferramenta ota_from_target_files
ao gerar uma atualização OTA. Este sinalizador desativa o cálculo de verificação no dispositivo durante as atualizações OTA. Isso diminui o tempo de instalação OTA, mas aumenta o tamanho do pacote OTA. Se o seu dispositivo não tiver o dm-verity habilitado, passar este sinalizador não terá efeito.
Ferramentas de construção consistentes
Problema: as ferramentas que geram arquivos instalados devem ser consistentes (uma determinada entrada deve sempre produzir a mesma saída).
Soluções / exemplos: mudanças foram necessárias nas seguintes ferramentas de construção:
- AVISO criador do arquivo . O criador do arquivo NOTICE foi alterado para criar coleções de NOTICE reproduzíveis. Consulte CL: https://android.googlesource.com/platform/build/+/8ae4984c2c8009e7a08e2a76b1762c2837ad4f64 .
- Java Android Compiler Kit (Jack) . O conjunto de ferramentas Jack exigia uma atualização para lidar com mudanças ocasionais na ordenação gerada do construtor. Acessadores determinísticos para construtores foram adicionados ao conjunto de ferramentas: https://android.googlesource.com/toolchain/jack/+/056a5425b3ef57935206c19ecb198a89221ca64b .
- Compilador ART AOT (dex2oat) . O binário do compilador ART recebeu uma atualização que adicionou uma opção para criar uma imagem determinística: https://android.googlesource.com/platform/art/+/ace0dc1dd5480ad458e622085e51583653853fb9 .
- O arquivo libpac.so (V8) . Cada construção cria um arquivo
/system/lib/libpac.so
diferente porque o instantâneo V8 muda para cada construção. A solução foi remover o instantâneo: https://android.googlesource.com/platform/external/v8/+/e537f38c36600fd0f3026adba6b3f4cbcee1fb29 . - Arquivos pré-definidos do aplicativo (.odex) . Os arquivos pré-dexoptados (.odex) continham preenchimento não inicializado em sistemas de 64 bits. Isso foi corrigido: https://android.googlesource.com/platform/art/+/34ed3afc41820c72a3c0ab9770be66b6668aa029 .
Usando a ferramenta build diff
Para os casos em que não é possível eliminar as alterações de arquivo relacionadas à construção, o AOSP inclui uma ferramenta de comparação de construção, target_files_diff.py
, para uso na comparação de dois pacotes de arquivo. Esta ferramenta realiza uma comparação recursiva entre duas compilações, excluindo alterações de arquivos comuns relacionadas à compilação, como
- Mudanças esperadas na saída do build (por exemplo, devido a uma mudança no número do build).
- Alterações devido a problemas conhecidos no sistema de compilação atual.
Para usar a ferramenta build diff, execute o seguinte comando:
target_files_diff.py dir1 dir2
dir1
e dir2
são diretórios base que contêm os arquivos de destino extraídos para cada construção.
Manter a alocação de blocos consistente
Para um determinado arquivo, embora seu conteúdo permaneça o mesmo entre duas compilações, os blocos reais que contêm os dados podem ter mudado. Como resultado, o atualizador deve executar E / S desnecessária para mover os blocos para uma atualização OTA.
Em uma atualização Virtual A / B OTA, a E / S desnecessária pode aumentar muito o espaço de armazenamento necessário para armazenar o instantâneo de cópia na gravação. Em uma atualização não A / B OTA, mover os blocos para uma atualização OTA contribui para o tempo de atualização, pois há mais E / S devido às movimentações dos blocos.
Para resolver esse problema, no Android 7.0, o Google estendeu a ferramenta make_ext4fs
para manter a alocação de blocos consistente entre as compilações. A ferramenta make_ext4fs
aceita um sinalizador -d base_fs
opcional que tenta alocar arquivos para os mesmos blocos ao gerar uma imagem ext4
. Você pode extrair os arquivos de mapeamento de bloco (como os arquivos de mapeamento base_fs
) do arquivo zip dos arquivos de destino de uma compilação anterior. Para cada partição ext4
, existe um arquivo .map
no diretório IMAGES
(por exemplo, IMAGES/system.map
corresponde à partição do system
). Esses arquivos base_fs
podem ser verificados e especificados via PRODUCT_<partition>_BASE_FS_PATH
, como neste exemplo:
PRODUCT_SYSTEM_BASE_FS_PATH := path/to/base_fs_files/base_system.map PRODUCT_SYSTEM_EXT_BASE_FS_PATH := path/to/base_fs_files/base_system_ext.map PRODUCT_VENDOR_BASE_FS_PATH := path/to/base_fs_files/base_vendor.map PRODUCT_PRODUCT_BASE_FS_PATH := path/to/base_fs_files/base_product.map PRODUCT_ODM_BASE_FS_PATH := path/to/base_fs_files/base_odm.map
Embora isso não ajude a reduzir o tamanho geral do pacote OTA, melhora o desempenho da atualização OTA reduzindo a quantidade de E / S. Para atualizações A / B virtuais, reduz drasticamente a quantidade de espaço de armazenamento necessária para aplicar o OTA.
Evite atualizar aplicativos
Além de minimizar as diferenças de compilação, você pode reduzir os tamanhos de atualização OTA excluindo atualizações para aplicativos que recebem atualizações por meio de lojas de aplicativos. Os APKs geralmente compreendem uma parte significativa de várias partições em um dispositivo. Incluir as versões mais recentes de aplicativos que são atualizados por lojas de aplicativos em uma atualização OTA pode ter um grande impacto de tamanho nos pacotes OTA e fornecer poucos benefícios para o usuário. No momento em que os usuários recebem um pacote OTA, eles já podem ter o aplicativo atualizado, ou uma versão ainda mais recente, recebida diretamente nas lojas de aplicativos.