Reduzir o tamanho do OTA

Esta página descreve as mudanças adicionadas ao AOSP para reduzir alterações desnecessárias de arquivos entre builds. Os implementadores de dispositivos que mantêm os próprios sistemas de build podem usar essas informações como um guia para reduzir o tamanho das atualizações OTA (pelo ar).

As atualizações OTA do Android às vezes contêm arquivos alterados que não correspondem a mudanças no código. Na verdade, eles são artefatos do sistema de build. Isso pode acontecer quando o mesmo código, criado em momentos diferentes, 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 dificultam a determinação de qual código foi alterado.

Para tornar o conteúdo de uma OTA mais transparente, o AOSP inclui mudanças no sistema de build projetadas para reduzir o tamanho dos patches de OTA. Mudanças desnecessárias entre builds foram eliminadas, e apenas arquivos relacionados a patches estão contidos nas atualizações OTA. O AOSP também inclui uma ferramenta de diff de build, que filtra mudanças comuns em arquivos relacionadas ao build para fornecer um diff de arquivo de build mais limpo, e uma ferramenta de mapeamento de blocos, que ajuda a manter a alocação de blocos consistente.

Um sistema de build pode criar patches desnecessariamente grandes de várias maneiras. Para reduzir esse problema, no Android 8.0 e versões mais recentes, novos recursos foram implementados para reduzir o tamanho do patch de cada diferença de arquivo. As melhorias que reduziram os tamanhos dos pacotes de atualização OTA incluem o seguinte:

  • Uso do ZSTD, um algoritmo de compressão sem perda de dados de uso geral para imagens completas em atualizações de dispositivos não A/B. O ZSTD pode ser personalizado para taxas de compactação mais altas aumentando o nível de compactação. O nível de compactação é definido durante a geração de OTA e pode ser definido transmitindo a flag --vabc_compression_param=zstd,$COMPRESSION_LEVEL
  • Aumentar o tamanho da janela de compactação usada durante a OTA. O tamanho máximo da janela de compactação pode ser definido personalizando o parâmetro de build no arquivo .mk de um dispositivo. Essa variável é definida como PRODUCT_VIRTUAL_AB_COMPRESSION_FACTOR := 262144
  • Uso da recompressão Puffin, uma ferramenta de patch determinística para fluxos deflate, que processa as funções de compressão e diff para geração de atualização OTA A/B.
  • Mudanças no uso da ferramenta de geração de delta, como a forma como a biblioteca bsdiff é usada para compactar patches. No Android 9 e versões mais recentes, a ferramenta bsdiff seleciona o algoritmo de compactação que oferece os melhores resultados 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.

As seções a seguir discutem vários problemas que afetam os tamanhos das atualizações OTA, as soluções e exemplos de implementação no AOSP.

Ordem dos arquivos

Problema: os sistemas de arquivos não garantem uma ordem de arquivos quando solicitados a fornecer uma lista de arquivos em um diretório, embora geralmente seja a mesma para o mesmo checkout. Ferramentas como ls classificam os resultados por padrão, mas a função de caractere curinga usada por comandos como find e make não faz isso. Antes de usar essas ferramentas, classifique as saídas.

Solução: quando você usa ferramentas como find e make com a função de caractere curinga, classifique a saída desses comandos antes de usá-los. Ao usar $(wildcard) ou $(shell find) em arquivos Android.mk, ordene-os também. Algumas ferramentas, como o Java, classificam as entradas. Portanto, antes de classificar os arquivos, verifique se a ferramenta que você está usando já fez isso.

Exemplos:muitas instâncias foram corrigidas no sistema de build principal usando a macro all-*-files-under integrada, que inclui all-cpp-files-under (já que várias definições foram distribuídas em outros makefiles). Para mais detalhes, consulte:

Diretório de build

Problema:mudar o diretório em que as coisas são criadas pode fazer com que os binários sejam diferentes. A maioria dos caminhos no build do Android são relativos, então __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 com base no hash do binário pré-removido. Portanto, ele muda se os símbolos de depuração mudarem.

Solução:o AOSP agora torna os caminhos de depuração relativos. Para mais detalhes, consulte CL: https://android.googlesource.com/platform/build/+/6a66a887baadc9eb3d0d60e26f748b8453e27a02.

Carimbos de data/hora

Problema:os carimbos de data/hora na saída da build resultam em mudanças desnecessárias nos arquivos. Isso provavelmente vai acontecer nos seguintes locais:

  • Macros __DATE__/__TIME__/__TIMESTAMP__ em código C ou C++.
  • Carimbos de data/hora incorporados em arquivos baseados em zip.

Soluções/exemplos:para remover os carimbos de data/hora da saída de build, use as instruções abaixo em __DATE__/__TIME__/__TIMESTAMP__ em C/C++ e Carimbos de data/hora incorporados em arquivos.

__DATE__/__TIME__/__TIMESTAMP__ em C/C++

Essas macros sempre produzem saídas diferentes para builds diferentes. Portanto, não as use. Confira algumas opções para eliminar essas macros:

Carimbos de data/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 criador e o carimbo de data/hora Unix estendido do arquivo ZIP.

Uma nova ferramenta, ziptime (localizada em /platform/build/+/android16-release/tools/ziptime/), redefine os carimbos de data/hora normais nos cabeçalhos ZIP. Para mais detalhes, consulte o arquivo README.

A ferramenta signapk define carimbos de data/hora para os arquivos APK, que podem variar de acordo com o fuso horário do servidor. Para mais detalhes, consulte o CL https://android.googlesource.com/platform/build/+/6c41036bcf35fe39162b50d27533f0f3bfab3028.

A ferramenta signapk define carimbos de data/hora para os arquivos APK, que podem variar de acordo com o fuso horário do servidor. Para mais detalhes, consulte o CL https://android.googlesource.com/platform/build/+/6c41036bcf35fe39162b50d27533f0f3bfab3028.

Strings de versão

Problema:as strings de versão do APK geralmente tinham o BUILD_NUMBER anexado às versões codificadas. Mesmo que nada mais mudasse em um APK, ele ainda seria diferente.

Solução:remova o número do build da string de versão do APK.

Exemplos:

Ativar o cálculo de veracidade no dispositivo

Se o dm-verity estiver ativado no seu dispositivo, as ferramentas de OTA vão capturar automaticamente a configuração de verificação e ativar a computação de verificação no dispositivo. Isso permite que os blocos de integridade sejam calculados em dispositivos Android, em vez de serem armazenados como bytes brutos no pacote OTA. Os blocos do Verity podem usar aproximadamente 16 MB para uma partição de 2 GB.

No entanto, calcular a veracidade no dispositivo pode levar muito tempo. Especificamente, o código de correção de erros de encaminhamento pode levar muito tempo. Em dispositivos Pixel, isso costuma levar até 10 minutos. Em dispositivos básicos, isso pode levar mais tempo. Se você quiser desativar o cálculo de integridade no dispositivo, mas ainda ativar o dm-verity, transmita --disable_fec_computation para a ferramenta ota_from_target_files ao gerar uma atualização OTA. Essa flag desativa o cálculo de integridade no dispositivo durante as atualizações OTA. Isso diminui o tempo de instalação da OTA, mas aumenta o tamanho do pacote. Se o dispositivo não tiver o dm-verity ativado, transmitir essa flag não terá efeito.

Ferramentas de build consistentes

Problema:as ferramentas que geram arquivos instalados precisam ser consistentes (uma determinada entrada sempre precisa produzir a mesma saída).

Soluções/exemplos:foram necessárias mudanças nas seguintes ferramentas de build:

Usar a ferramenta de diff de build

Para casos em que não é possível eliminar mudanças em arquivos relacionadas à build, o AOSP inclui uma ferramenta de diff de build, target_files_diff.py para uso na comparação de dois pacotes de arquivos. Essa ferramenta realiza uma diff recursiva entre dois builds, excluindo mudanças comuns relacionadas a arquivos de build, como

  • Mudanças esperadas na saída do build (por exemplo, devido a uma mudança no número do build).
  • Mudanças devido a problemas conhecidos no sistema de build atual.

Para usar a ferramenta de diff de build, execute o seguinte comando:

target_files_diff.py dir1 dir2

dir1 e dir2 são diretórios de base que contêm os arquivos de destino extraídos para cada build.

Mantenha a alocação de blocos consistente

Para um determinado arquivo, embora o conteúdo permaneça o mesmo entre duas builds, os blocos reais que contêm os dados podem ter mudado. Como resultado, o atualizador precisa realizar E/S desnecessária para mover os blocos em uma atualização OTA.

Em uma atualização OTA A/B virtual, E/S desnecessárias podem aumentar muito o espaço de armazenamento necessário para armazenar o snapshot de cópia na gravação. Em uma atualização OTA não A/B, mover os blocos para uma atualização OTA contribui para o tempo de atualização, já que há mais E/S devido às movimentações de blocos.

Para resolver esse problema, no Android 7.0, o Google estendeu a ferramenta make_ext4fs para manter a alocação de blocos consistente em todas as builds. A ferramenta make_ext4fs aceita uma flag -d base_fs opcional que tenta alocar arquivos aos mesmos blocos ao gerar uma imagem ext4. É possível extrair os arquivos de mapeamento de blocos (como os arquivos de mapa base_fs) do arquivo zip dos arquivos de destino de um build anterior. Para cada partição ext4, há um arquivo .map no diretório IMAGES. Por exemplo, IMAGES/system.map corresponde à partição 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 a performance da atualização OTA reduzindo a quantidade de E/S. Para atualizações A/B virtuais, ele reduz drasticamente a quantidade de espaço de armazenamento necessário para aplicar a OTA.

Evitar atualizar apps

Além de minimizar as diferenças de build, é possível reduzir os tamanhos das atualizações OTA excluindo as atualizações de apps que recebem atualizações pelas lojas de apps. Os APKs geralmente compreendem uma parte significativa de várias partições em um dispositivo. Incluir as versões mais recentes de apps atualizados por app stores em uma atualização OTA pode ter um grande impacto no tamanho dos pacotes OTA e oferecer pouco benefício ao usuário. Quando os usuários recebem um pacote OTA, eles já podem ter o app atualizado ou uma versão ainda mais recente, recebida diretamente das app stores.