Riduci le dimensioni dell'OTA

Questa pagina descrive le modifiche aggiunte ad AOSP per ridurre le modifiche non necessarie dei file tra le build. Gli implementatori di dispositivi che gestiscono i propri sistemi di compilazione possono utilizzare queste informazioni come guida per ridurre le dimensioni degli aggiornamenti over-the-air (OTA).

A volte gli aggiornamenti OTA di Android contengono file modificati che non corrispondono a modifiche al codice. In realtà, sono artefatti di sistema di compilazione. Questo può accadere quando lo stesso codice, compilato in momenti diversi, da directory diverse o su macchine diverse, produce un numero elevato di file modificati. Questi file in eccesso aumentano le dimensioni di una patch OTA e rendono difficile determinare quale codice è stato modificato.

Per rendere più trasparenti i contenuti di un aggiornamento OTA, AOSP include modifiche al sistema di compilazione progettate per ridurre le dimensioni dei patch OTA. Le modifiche non necessarie dei file tra le build sono state eliminate e solo i file relativi alle patch sono contenuti negli aggiornamenti OTA. AOSP include anche un strumento di confronto delle build, che filtra le modifiche ai file comuni relative alla compilazione per fornire un confronto più pulito dei file di compilazione, e un strumento di mappatura dei blocchi, che ti aiuta a mantenere costante l'allocazione dei blocchi.

Un sistema di compilazione può creare patch inutilmente grandi in diversi modi. Per ovviare a questo problema, in Android 8.0 e versioni successive sono state implementate nuove funzionalità per ridurre le dimensioni della patch per ogni differenza di file. I miglioramenti che hanno ridotto le dimensioni dei pacchetti di aggiornamento OTA includono:

  • Utilizzo di ZSTD, un algoritmo di compressione senza perdita di dati generico per le immagini complete negli aggiornamenti dei dispositivi non A/B. ZSTD può essere personalizzato per ottenere rapporti di compressione più elevati aumentando il livello di compressione. Il livello di compressione viene impostato durante la generazione OTA e può essere impostato passando il flag --vabc_compression_param=zstd,$COMPRESSION_LEVEL
  • Aumento della dimensione della finestra di compressione utilizzata durante l'OTA. La dimensione massima della finestra di compressione può essere impostata personalizzando il parametro di compilazione nel file .mk di un dispositivo. Questa variabile è impostata su PRODUCT_VIRTUAL_AB_COMPRESSION_FACTOR := 262144
  • Utilizzo della ricompressione Puffin, uno strumento di patching deterministico per gli stream deflate, che gestisce le funzioni di compressione e di differenza per la generazione di aggiornamenti OTA A/B.
  • Modifiche all'utilizzo dello strumento di generazione del delta, ad esempio il modo in cui la biblioteca bsdiff viene utilizzata per comprimere i patch. In Android 9 e versioni successive, lo strumento bsdiff seleziona l'algoritmo di compressione che offre i risultati di compressione migliori per una patch.
  • I miglioramenti apportati a update_engine hanno comportato una minore quantità di memoria utilizzata quando vengono applicate le patch per gli aggiornamenti dei dispositivi A/B.

Le sezioni seguenti illustrano vari problemi che influiscono sulle dimensioni degli aggiornamenti OTA, le relative soluzioni e esempi di implementazione in AOSP.

Ordine dei file

Problema: i filesystem non garantiscono un ordine dei file quando viene richiesto un elenco di file in una directory, anche se in genere è lo stesso per lo stesso pagamento. Strumenti come ls ordinano i risultati per impostazione predefinita, ma la funzione di caratteri jolly utilizzata da comandi come find e make non li ordina. Prima di utilizzare questi strumenti, devi ordinare gli output.

Soluzione: quando utilizzi strumenti come find e make con la funzione carattere jolly, ordina l'output di questi comandi prima di utilizzarli. Quando utilizzi $(wildcard) o $(shell find) nei Android.mk file, ordinali anche. Alcuni strumenti, come Java, ordinano gli input, quindi prima di ordinare i file, verifica che lo strumento che stai utilizzando non l'abbia già fatto.

Esempi: molte istanze sono state corrette nel sistema di compilazione di base utilizzando la macro all-*-files-under interna, che include all-cpp-files-under (poiché diverse definizioni erano distribuite in altri file make). Per maggiori dettagli, consulta quanto segue:

Directory di build

Problema: la modifica della directory in cui vengono compilati gli elementi può causare la differenza dei programmi binari. La maggior parte dei percorsi nella compilazione di Android sono percorsi relativi, quindi __FILE__ in C/C++ non è un problema. Tuttavia, i simboli di debug codificano il percorso completo per impostazione predefinita e .note.gnu.build-id viene generato dall'hashing del file binario pre-stripped, pertanto cambierà se cambiano i simboli di debug.

Soluzione: ora AOSP rende i percorsi di debug relativi. Per maggiori dettagli, consulta il CL: https://android.googlesource.com/platform/build/+/6a66a887baadc9eb3d0d60e26f748b8453e27a02.

Timestamp

Problema: i timestamp nell'output della compilazione comportano modifiche non necessarie ai file. È probabile che ciò si verifichi nelle seguenti località:

  • Macro __DATE__/__TIME__/__TIMESTAMP__ nel codice C o C++.
  • Timestamp incorporati in archivi basati su zip.

Soluzioni/esempi: per rimuovere i timestamp dall'output della compilazione, utilizza le istruzioni riportate di seguito in __DATE__/__TIME__/__TIMESTAMP__ in C/C++ e Timestamp incorporati negli archivi.

__DATE__/__TIME__/__TIMESTAMP__ in C/C++

Queste macro producono sempre output diversi per build diverse, quindi non utilizzarle. Di seguito sono riportate alcune opzioni per eliminare queste macro:

Timestamp incorporati negli archivi (zip, jar)

In Android 7.0 è stato risolto il problema dei timestamp incorporati negli archivi ZIP aggiungendo -X a tutti gli utilizzi del comando zip. In questo modo, dal file ZIP sono stati rimossi l'UID/GID del compilatore e il timestamp Unix esteso.

Un nuovo strumento, ziptime (disponibile in /platform/build/+/android16-release/tools/ziptime/), reimposta i timestamp normali nelle intestazioni ZIP. Per maggiori dettagli, consulta il file README.

Lo strumento signapk imposta i timestamp per i file APK che possono variare a seconda del fuso orario del server. Per maggiori dettagli, consulta il CL https://android.googlesource.com/platform/build/+/6c41036bcf35fe39162b50d27533f0f3bfab3028.

Lo strumento signapk imposta i timestamp per i file APK che possono variare a seconda del fuso orario del server. Per maggiori dettagli, consulta il CL https://android.googlesource.com/platform/build/+/6c41036bcf35fe39162b50d27533f0f3bfab3028.

Stringhe di versione

Problema: alle stringhe di versione APK veniva spesso aggiunto BUILD_NUMBER alle versioni hardcoded. Anche se non è cambiato nulla in un APK, l'APK sarà comunque diverso.

Soluzione:rimuovi il numero di build dalla stringa della versione dell'APK.

Esempi:

Attivare il calcolo di Verity sul dispositivo

Se dm-verity è abilitato sul tuo dispositivo, gli strumenti OTA rilevano automaticamente la configurazione di Verity e attivano il calcolo di Verity sul dispositivo. In questo modo, i blocchi di verifica possono essere calcolati sui dispositivi Android, anziché essere memorizzati come byte non elaborati nel pacchetto OTA. I blocchi Verity possono utilizzare circa 16 MB per una partizione di 2 GB.

Tuttavia, il calcolo della verifica sul dispositivo può richiedere molto tempo. In particolare, il codice di correzione degli errori in avanti può richiedere molto tempo. Sui Pixel, in genere sono necessari fino a 10 minuti. Sui dispositivi di fascia bassa potrebbe essere necessario più tempo. Se vuoi disattivare il calcolo di Verity sul dispositivo, ma attivare comunque dm-verity, puoi farlo passando --disable_fec_computation allo strumento ota_from_target_files quando generi un aggiornamento OTA. Questo flag disattiva il calcolo della verifica sul dispositivo durante gli aggiornamenti OTA. Riduce il tempo di installazione OTA, ma aumenta le dimensioni del pacchetto OTA. Se sul tuo dispositivo non è attivata la funzionalità DM-Verity, il passaggio di questo flag non ha alcun effetto.

Strumenti di compilazione coerenti

Problema: gli strumenti che generano i file installati devono essere coerenti (un determinato input deve produrre sempre lo stesso output).

Soluzioni/esempi:sono state necessarie modifiche nei seguenti strumenti di compilazione:

Utilizzare lo strumento di confronto delle build

Nei casi in cui non sia possibile eliminare le modifiche ai file correlate alla compilazione, AOSP include uno strumento per il confronto delle build, target_files_diff.py da utilizzare per confrontare due pacchetti di file. Questo strumento esegue un confronto ricorsivo tra due build, escludendo le modifiche ai file comuni correlate alla build, ad esempio

  • Modifiche previste nell'output della compilazione (ad esempio a causa di una modifica del numero di build).
  • Modifiche dovute a problemi noti nell'attuale sistema di compilazione.

Per utilizzare lo strumento di confronto della build, esegui il seguente comando:

target_files_diff.py dir1 dir2

dir1 e dir2 sono le directory di base che contengono i file di destinazione estratti per ogni compilazione.

Mantieni coerente l'allocazione dei blocchi

Per un determinato file, anche se i contenuti rimangono invariati tra due build, i blocchi effettivi che contengono i dati potrebbero essere cambiati. Di conseguenza, l'aggiornamento deve eseguire I/O non necessarie per spostare i blocchi per un aggiornamento OTA.

In un aggiornamento OTA A/B virtuale, l'I/O non necessaria può aumentare notevolmente lo spazio di archiviazione necessario per memorizzare lo snapshot con copia su scrittura. In un aggiornamento OTA non A/B, lo spostamento dei blocchi per un aggiornamento OTA contribuisce al tempo di aggiornamento perché le operazioni I/O sono maggiori a causa dei movimenti dei blocchi.

Per risolvere il problema, in Android 7.0 Google ha esteso lo strumento make_ext4fs per mantenere costante l'allocazione dei blocchi tra le build. Lo strumento make_ext4fs accetta un flag -d base_fs facoltativo che tenta di allocare i file agli stessi blocchi durante la generazione di un'immagine ext4. Puoi estrarre i file di mappatura dei blocchi (ad esempio i file mappa base_fs) dal file ZIP dei file di destinazione di una build precedente. Per ogni partizione ext4, esiste un file .map nella directory IMAGES (ad esempio, IMAGES/system.map corrisponde alla partizione system). Questi file base_fs possono quindi essere sottoposti a check-in e specificati tramite PRODUCT_<partition>_BASE_FS_PATH, come in questo esempio:

  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

Sebbene questo non aiuti a ridurre le dimensioni complessive del pacchetto OTA, migliora le prestazioni dell'aggiornamento OTA riducendo la quantità di I/O. Per gli aggiornamenti A/B virtuali, riduce drasticamente la quantità di spazio di archiviazione necessaria per applicare l'OTA.

Evita di aggiornare le app

Oltre a ridurre al minimo le differenze di build, puoi ridurre le dimensioni degli aggiornamenti OTA escludendo gli aggiornamenti per le app che ricevono aggiornamenti tramite gli store. Gli APK spesso costituiscono una parte significativa di varie partizioni su un dispositivo. L'inclusione delle versioni più recenti delle app aggiornate dagli store di app in un aggiornamento OTA potrebbe avere un impatto significativo sulle dimensioni dei pacchetti OTA e offrire scarsi vantaggi agli utenti. Quando gli utenti ricevono un pacchetto OTA, potrebbero già avere l'app aggiornata o una versione ancora più recente, ricevuta direttamente dagli store.